Merge branch 'add-filesize-to-files' into 'master'

Add filesize to file entity, expose it to GraphQL API

Closes #128

See merge request framasoft/mobilizon!148
This commit is contained in:
Thomas Citharel 2019-06-03 17:20:41 +02:00
commit 10dbe14a52
8 changed files with 66 additions and 18 deletions

View file

@ -9,6 +9,7 @@ defmodule Mobilizon.Media.File do
field(:name, :string) field(:name, :string)
field(:url, :string) field(:url, :string)
field(:content_type, :string) field(:content_type, :string)
field(:size, :integer)
timestamps() timestamps()
end end
@ -16,7 +17,7 @@ defmodule Mobilizon.Media.File do
@doc false @doc false
def changeset(picture, attrs) do def changeset(picture, attrs) do
picture picture
|> cast(attrs, [:name, :url, :content_type]) |> cast(attrs, [:name, :url, :content_type, :size])
|> validate_required([:name, :url]) |> validate_required([:name, :url])
end end
end end

View file

@ -36,7 +36,8 @@ defmodule MobilizonWeb.Resolvers.Picture do
@spec do_fetch_picture(String.t()) :: {:ok, Picture.t()} | {:error, :not_found} @spec do_fetch_picture(String.t()) :: {:ok, Picture.t()} | {:error, :not_found}
defp do_fetch_picture(picture_id) do defp do_fetch_picture(picture_id) do
with %Picture{id: id, file: file} = _pic <- Media.get_picture(picture_id) do with %Picture{id: id, file: file} = _pic <- Media.get_picture(picture_id) do
{:ok, %{name: file.name, url: file.url, id: id}} {:ok,
%{name: file.name, url: file.url, id: id, content_type: file.content_type, size: file.size}}
else else
_err -> _err ->
{:error, "Picture with ID #{picture_id} was not found"} {:error, "Picture with ID #{picture_id} was not found"}
@ -50,11 +51,23 @@ defmodule MobilizonWeb.Resolvers.Picture do
} }
}) do }) do
with {:is_owned, true, _actor} <- User.owns_actor(user, actor_id), with {:is_owned, true, _actor} <- User.owns_actor(user, actor_id),
{:ok, %{"url" => [%{"href" => url}]}} <- MobilizonWeb.Upload.store(file), {:ok, %{"url" => [%{"href" => url, "mediaType" => content_type}], "size" => size}} <-
args <- Map.put(args, :url, url), MobilizonWeb.Upload.store(file),
args <-
args
|> Map.put(:url, url)
|> Map.put(:size, size)
|> Map.put(:content_type, content_type),
{:ok, picture = %Picture{}} <- {:ok, picture = %Picture{}} <-
Media.create_picture(%{"file" => args, "actor_id" => actor_id}) do Media.create_picture(%{"file" => args, "actor_id" => actor_id}) do
{:ok, %{name: picture.file.name, url: picture.file.url, id: picture.id}} {:ok,
%{
name: picture.file.name,
url: picture.file.url,
id: picture.id,
content_type: picture.file.content_type,
size: picture.file.size
}}
else else
{:is_owned, false} -> {:is_owned, false} ->
{:error, "Actor id is not owned by authenticated user"} {:error, "Actor id is not owned by authenticated user"}

View file

@ -11,6 +11,8 @@ defmodule MobilizonWeb.Schema.PictureType do
field(:alt, :string, description: "The picture's alternative text") field(:alt, :string, description: "The picture's alternative text")
field(:name, :string, description: "The picture's name") field(:name, :string, description: "The picture's name")
field(:url, :string, description: "The picture's full URL") field(:url, :string, description: "The picture's full URL")
field(:content_type, :string, description: "The picture's detected content type")
field(:size, :integer, description: "The picture's size")
end end
@desc "An attached picture or a link to a picture" @desc "An attached picture or a link to a picture"

View file

@ -52,9 +52,10 @@ defmodule MobilizonWeb.Upload do
name: String.t(), name: String.t(),
tempfile: String.t(), tempfile: String.t(),
content_type: String.t(), content_type: String.t(),
path: String.t() path: String.t(),
size: integer()
} }
defstruct [:id, :name, :tempfile, :content_type, :path] defstruct [:id, :name, :tempfile, :content_type, :path, :size]
@spec store(source, options :: [option()]) :: {:ok, Map.t()} | {:error, any()} @spec store(source, options :: [option()]) :: {:ok, Map.t()} | {:error, any()}
def store(upload, opts \\ []) do def store(upload, opts \\ []) do
@ -66,7 +67,7 @@ defmodule MobilizonWeb.Upload do
{:ok, url_spec} <- MobilizonWeb.Uploaders.Uploader.put_file(opts.uploader, upload) do {:ok, url_spec} <- MobilizonWeb.Uploaders.Uploader.put_file(opts.uploader, upload) do
{:ok, {:ok,
%{ %{
"type" => opts.activity_type, "type" => opts.activity_type || get_type(upload.content_type),
"url" => [ "url" => [
%{ %{
"type" => "Link", "type" => "Link",
@ -74,6 +75,7 @@ defmodule MobilizonWeb.Upload do
"href" => url_from_spec(upload, opts.base_url, url_spec) "href" => url_from_spec(upload, opts.base_url, url_spec)
} }
], ],
"size" => upload.size,
"name" => Map.get(opts, :description) || upload.name "name" => Map.get(opts, :description) || upload.name
}} }}
else else
@ -100,7 +102,7 @@ defmodule MobilizonWeb.Upload do
{Mobilizon.CommonConfig.get!([:instance, :avatar_upload_limit]), "Image"} {Mobilizon.CommonConfig.get!([:instance, :avatar_upload_limit]), "Image"}
_ -> _ ->
{Mobilizon.CommonConfig.get!([:instance, :upload_limit]), "Document"} {Mobilizon.CommonConfig.get!([:instance, :upload_limit]), nil}
end end
%{ %{
@ -119,14 +121,15 @@ defmodule MobilizonWeb.Upload do
end end
defp prepare_upload(%Plug.Upload{} = file, opts) do defp prepare_upload(%Plug.Upload{} = file, opts) do
with :ok <- check_file_size(file.path, opts.size_limit), with {:ok, size} <- check_file_size(file.path, opts.size_limit),
{:ok, content_type, name} <- Mobilizon.MIME.file_mime_type(file.path, file.filename) do {:ok, content_type, name} <- Mobilizon.MIME.file_mime_type(file.path, file.filename) do
{:ok, {:ok,
%__MODULE__{ %__MODULE__{
id: UUID.generate(), id: UUID.generate(),
name: name, name: name,
tempfile: file.path, tempfile: file.path,
content_type: content_type content_type: content_type,
size: size
}} }}
end end
end end
@ -134,7 +137,7 @@ defmodule MobilizonWeb.Upload do
defp check_file_size(path, size_limit) when is_integer(size_limit) and size_limit > 0 do defp check_file_size(path, size_limit) when is_integer(size_limit) and size_limit > 0 do
with {:ok, %{size: size}} <- File.stat(path), with {:ok, %{size: size}} <- File.stat(path),
true <- size <= size_limit do true <- size <= size_limit do
:ok {:ok, size}
else else
false -> {:error, :file_too_large} false -> {:error, :file_too_large}
error -> error error -> error
@ -143,6 +146,16 @@ defmodule MobilizonWeb.Upload do
defp check_file_size(_, _), do: :ok defp check_file_size(_, _), do: :ok
@picture_content_types ["image/png", "image/jpg", "image/jpeg", "image/webp"]
# Return whether the upload is a picture or not
defp get_type(content_type) do
if content_type in @picture_content_types do
"Image"
else
"Document"
end
end
defp url_from_spec(%__MODULE__{name: name}, base_url, {:file, path}) do defp url_from_spec(%__MODULE__{name: name}, base_url, {:file, path}) do
path = path =
URI.encode(path, &char_unescaped?/1) <> URI.encode(path, &char_unescaped?/1) <>

View file

@ -209,12 +209,15 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
Save picture data from raw data and return AS Link data. Save picture data from raw data and return AS Link data.
""" """
def make_picture_data(%{picture: picture}) do def make_picture_data(%{picture: picture}) do
with {:ok, %{"url" => [%{"href" => url}]}} <- MobilizonWeb.Upload.store(picture.file), with {:ok, %{"url" => [%{"href" => url, "mediaType" => content_type}], "size" => size}} <-
MobilizonWeb.Upload.store(picture.file),
{:ok, %Picture{file: _file} = pic} <- {:ok, %Picture{file: _file} = pic} <-
Mobilizon.Media.create_picture(%{ Mobilizon.Media.create_picture(%{
"file" => %{ "file" => %{
"url" => url, "url" => url,
"name" => picture.name "name" => picture.name,
"content_type" => content_type,
"size" => size
}, },
"actor_id" => picture.actor_id "actor_id" => picture.actor_id
}) do }) do

View file

@ -21,7 +21,9 @@ defmodule MobilizonWeb.Resolvers.PictureResolverTest do
picture(id: "#{id}") { picture(id: "#{id}") {
name, name,
alt, alt,
url url,
content_type,
size
} }
} }
""" """
@ -32,6 +34,11 @@ defmodule MobilizonWeb.Resolvers.PictureResolverTest do
assert json_response(res, 200)["data"]["picture"]["name"] == picture.file.name assert json_response(res, 200)["data"]["picture"]["name"] == picture.file.name
assert json_response(res, 200)["data"]["picture"]["content_type"] ==
picture.file.content_type
assert json_response(res, 200)["data"]["picture"]["size"] == 13_120
assert json_response(res, 200)["data"]["picture"]["url"] =~ assert json_response(res, 200)["data"]["picture"]["url"] =~
MobilizonWeb.Endpoint.url() MobilizonWeb.Endpoint.url()
end end
@ -68,7 +75,9 @@ defmodule MobilizonWeb.Resolvers.PictureResolverTest do
actor_id: #{actor.id} actor_id: #{actor.id}
) { ) {
url, url,
name name,
content_type,
size
} }
} }
""" """
@ -91,6 +100,8 @@ defmodule MobilizonWeb.Resolvers.PictureResolverTest do
) )
assert json_response(res, 200)["data"]["uploadPicture"]["name"] == picture.name assert json_response(res, 200)["data"]["uploadPicture"]["name"] == picture.name
assert json_response(res, 200)["data"]["uploadPicture"]["content_type"] == "image/png"
assert json_response(res, 200)["data"]["uploadPicture"]["size"] == 10_097
assert json_response(res, 200)["data"]["uploadPicture"]["url"] assert json_response(res, 200)["data"]["uploadPicture"]["url"]
end end

View file

@ -21,7 +21,11 @@ defmodule Mobilizon.UploadTest do
{:ok, data} = Upload.store(file) {:ok, data} = Upload.store(file)
assert %{"url" => [%{"href" => url}]} = data assert %{
"url" => [%{"href" => url, "mediaType" => "image/jpeg"}],
"size" => 13_227,
"type" => "Image"
} = data
assert String.starts_with?(url, MobilizonWeb.Endpoint.url() <> "/media/") assert String.starts_with?(url, MobilizonWeb.Endpoint.url() <> "/media/")
end end

View file

@ -174,7 +174,8 @@ defmodule Mobilizon.Factory do
%Mobilizon.Media.File{ %Mobilizon.Media.File{
name: "My Picture", name: "My Picture",
url: MobilizonWeb.Endpoint.url() <> "/uploads/something", url: MobilizonWeb.Endpoint.url() <> "/uploads/something",
content_type: "image/png" content_type: "image/png",
size: 13_120
} }
end end