From 7794d7c694110543998ee1fb278c68babda37301 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Wed, 19 Aug 2020 06:50:20 +0300 Subject: added Pleroma.Web.PleromaAPI.EmojiFileController --- .../operations/pleroma_emoji_file_operation.ex | 133 +++++++++++++++++++++ .../operations/pleroma_emoji_pack_operation.ex | 105 ---------------- .../controllers/emoji_file_controller.ex | 122 +++++++++++++++++++ .../controllers/emoji_pack_controller.ex | 107 +---------------- lib/pleroma/web/router.ex | 6 +- 5 files changed, 259 insertions(+), 214 deletions(-) create mode 100644 lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex create mode 100644 lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex new file mode 100644 index 000000000..b6932157a --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex @@ -0,0 +1,133 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.PleromaEmojiFileOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.ApiError + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def create_operation do + %Operation{ + tags: ["Emoji Packs"], + summary: "Add new file to the pack", + operationId: "PleromaAPI.EmojiPackController.add_file", + security: [%{"oAuth" => ["write"]}], + requestBody: request_body("Parameters", create_request(), required: true), + parameters: [name_param()], + responses: %{ + 200 => Operation.response("Files Object", "application/json", files_object()), + 400 => Operation.response("Bad Request", "application/json", ApiError), + 409 => Operation.response("Conflict", "application/json", ApiError) + } + } + end + + defp create_request do + %Schema{ + type: :object, + required: [:file], + properties: %{ + file: %Schema{ + description: + "File needs to be uploaded with the multipart request or link to remote file", + anyOf: [ + %Schema{type: :string, format: :binary}, + %Schema{type: :string, format: :uri} + ] + }, + shortcode: %Schema{ + type: :string, + description: + "Shortcode for new emoji, must be unique for all emoji. If not sended, shortcode will be taken from original filename." + }, + filename: %Schema{ + type: :string, + description: + "New emoji file name. If not specified will be taken from original filename." + } + } + } + end + + def update_operation do + %Operation{ + tags: ["Emoji Packs"], + summary: "Add new file to the pack", + operationId: "PleromaAPI.EmojiPackController.update_file", + security: [%{"oAuth" => ["write"]}], + requestBody: request_body("Parameters", update_request(), required: true), + parameters: [name_param()], + responses: %{ + 200 => Operation.response("Files Object", "application/json", files_object()), + 400 => Operation.response("Bad Request", "application/json", ApiError), + 409 => Operation.response("Conflict", "application/json", ApiError) + } + } + end + + defp update_request do + %Schema{ + type: :object, + required: [:shortcode, :new_shortcode, :new_filename], + properties: %{ + shortcode: %Schema{ + type: :string, + description: "Emoji file shortcode" + }, + new_shortcode: %Schema{ + type: :string, + description: "New emoji file shortcode" + }, + new_filename: %Schema{ + type: :string, + description: "New filename for emoji file" + }, + force: %Schema{ + type: :boolean, + description: "With true value to overwrite existing emoji with new shortcode", + default: false + } + } + } + end + + def delete_operation do + %Operation{ + tags: ["Emoji Packs"], + summary: "Delete emoji file from pack", + operationId: "PleromaAPI.EmojiPackController.delete_file", + security: [%{"oAuth" => ["write"]}], + parameters: [ + name_param(), + Operation.parameter(:shortcode, :query, :string, "File shortcode", + example: "cofe", + required: true + ) + ], + responses: %{ + 200 => Operation.response("Files Object", "application/json", files_object()), + 400 => Operation.response("Bad Request", "application/json", ApiError) + } + } + end + + defp name_param do + Operation.parameter(:name, :path, :string, "Pack Name", example: "cofe", required: true) + end + + defp files_object do + %Schema{ + type: :object, + additionalProperties: %Schema{type: :string}, + description: "Object with emoji names as keys and filenames as values" + } + end +end diff --git a/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex index b2b4f8713..59548af13 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex @@ -175,111 +175,6 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do } end - def add_file_operation do - %Operation{ - tags: ["Emoji Packs"], - summary: "Add new file to the pack", - operationId: "PleromaAPI.EmojiPackController.add_file", - security: [%{"oAuth" => ["write"]}], - requestBody: request_body("Parameters", add_file_request(), required: true), - parameters: [name_param()], - responses: %{ - 200 => Operation.response("Files Object", "application/json", files_object()), - 400 => Operation.response("Bad Request", "application/json", ApiError), - 409 => Operation.response("Conflict", "application/json", ApiError) - } - } - end - - defp add_file_request do - %Schema{ - type: :object, - required: [:file], - properties: %{ - file: %Schema{ - description: - "File needs to be uploaded with the multipart request or link to remote file", - anyOf: [ - %Schema{type: :string, format: :binary}, - %Schema{type: :string, format: :uri} - ] - }, - shortcode: %Schema{ - type: :string, - description: - "Shortcode for new emoji, must be unique for all emoji. If not sended, shortcode will be taken from original filename." - }, - filename: %Schema{ - type: :string, - description: - "New emoji file name. If not specified will be taken from original filename." - } - } - } - end - - def update_file_operation do - %Operation{ - tags: ["Emoji Packs"], - summary: "Add new file to the pack", - operationId: "PleromaAPI.EmojiPackController.update_file", - security: [%{"oAuth" => ["write"]}], - requestBody: request_body("Parameters", update_file_request(), required: true), - parameters: [name_param()], - responses: %{ - 200 => Operation.response("Files Object", "application/json", files_object()), - 400 => Operation.response("Bad Request", "application/json", ApiError), - 409 => Operation.response("Conflict", "application/json", ApiError) - } - } - end - - defp update_file_request do - %Schema{ - type: :object, - required: [:shortcode, :new_shortcode, :new_filename], - properties: %{ - shortcode: %Schema{ - type: :string, - description: "Emoji file shortcode" - }, - new_shortcode: %Schema{ - type: :string, - description: "New emoji file shortcode" - }, - new_filename: %Schema{ - type: :string, - description: "New filename for emoji file" - }, - force: %Schema{ - type: :boolean, - description: "With true value to overwrite existing emoji with new shortcode", - default: false - } - } - } - end - - def delete_file_operation do - %Operation{ - tags: ["Emoji Packs"], - summary: "Delete emoji file from pack", - operationId: "PleromaAPI.EmojiPackController.delete_file", - security: [%{"oAuth" => ["write"]}], - parameters: [ - name_param(), - Operation.parameter(:shortcode, :query, :string, "File shortcode", - example: "cofe", - required: true - ) - ], - responses: %{ - 200 => Operation.response("Files Object", "application/json", files_object()), - 400 => Operation.response("Bad Request", "application/json", ApiError) - } - } - end - def import_from_filesystem_operation do %Operation{ tags: ["Emoji Packs"], diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex new file mode 100644 index 000000000..ba9f07795 --- /dev/null +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex @@ -0,0 +1,122 @@ +defmodule Pleroma.Web.PleromaAPI.EmojiFileController do + use Pleroma.Web, :controller + + alias Pleroma.Emoji.Pack + alias Pleroma.Web.ApiSpec + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + plug( + Pleroma.Plugs.OAuthScopesPlug, + %{scopes: ["write"], admin: true} + when action in [ + :create, + :update, + :delete + ] + ) + + defdelegate open_api_operation(action), to: ApiSpec.PleromaEmojiFileOperation + + def create(%{body_params: params} = conn, %{name: pack_name}) do + filename = params[:filename] || get_filename(params[:file]) + shortcode = params[:shortcode] || Path.basename(filename, Path.extname(filename)) + + with {:ok, pack} <- Pack.add_file(pack_name, shortcode, filename, params[:file]) do + json(conn, pack.files) + else + {:error, :already_exists} -> + conn + |> put_status(:conflict) + |> json(%{error: "An emoji with the \"#{shortcode}\" shortcode already exists"}) + + {:error, :not_found} -> + conn + |> put_status(:bad_request) + |> json(%{error: "pack \"#{pack_name}\" is not found"}) + + {:error, :empty_values} -> + conn + |> put_status(:bad_request) + |> json(%{error: "pack name, shortcode or filename cannot be empty"}) + + {:error, _} -> + render_error( + conn, + :internal_server_error, + "Unexpected error occurred while adding file to pack." + ) + end + end + + def update(%{body_params: %{shortcode: shortcode} = params} = conn, %{name: pack_name}) do + new_shortcode = params[:new_shortcode] + new_filename = params[:new_filename] + force = params[:force] + + with {:ok, pack} <- Pack.update_file(pack_name, shortcode, new_shortcode, new_filename, force) do + json(conn, pack.files) + else + {:error, :doesnt_exist} -> + conn + |> put_status(:bad_request) + |> json(%{error: "Emoji \"#{shortcode}\" does not exist"}) + + {:error, :already_exists} -> + conn + |> put_status(:conflict) + |> json(%{ + error: + "New shortcode \"#{new_shortcode}\" is already used. If you want to override emoji use 'force' option" + }) + + {:error, :not_found} -> + conn + |> put_status(:bad_request) + |> json(%{error: "pack \"#{pack_name}\" is not found"}) + + {:error, :empty_values} -> + conn + |> put_status(:bad_request) + |> json(%{error: "new_shortcode or new_filename cannot be empty"}) + + {:error, _} -> + render_error( + conn, + :internal_server_error, + "Unexpected error occurred while updating file in pack." + ) + end + end + + def delete(conn, %{name: pack_name, shortcode: shortcode}) do + with {:ok, pack} <- Pack.delete_file(pack_name, shortcode) do + json(conn, pack.files) + else + {:error, :doesnt_exist} -> + conn + |> put_status(:bad_request) + |> json(%{error: "Emoji \"#{shortcode}\" does not exist"}) + + {:error, :not_found} -> + conn + |> put_status(:bad_request) + |> json(%{error: "pack \"#{pack_name}\" is not found"}) + + {:error, :empty_values} -> + conn + |> put_status(:bad_request) + |> json(%{error: "pack name or shortcode cannot be empty"}) + + {:error, _} -> + render_error( + conn, + :internal_server_error, + "Unexpected error occurred while removing file from pack." + ) + end + end + + defp get_filename(%Plug.Upload{filename: filename}), do: filename + defp get_filename(url) when is_binary(url), do: Path.basename(url) +end diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex index 657f46324..e3969fee1 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex @@ -14,10 +14,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do :download, :create, :update, - :delete, - :add_file, - :update_file, - :delete_file + :delete ] ) @@ -184,105 +181,6 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do end end - def add_file(%{body_params: params} = conn, %{name: name}) do - filename = params[:filename] || get_filename(params[:file]) - shortcode = params[:shortcode] || Path.basename(filename, Path.extname(filename)) - - with {:ok, pack} <- Pack.add_file(name, shortcode, filename, params[:file]) do - json(conn, pack.files) - else - {:error, :already_exists} -> - conn - |> put_status(:conflict) - |> json(%{error: "An emoji with the \"#{shortcode}\" shortcode already exists"}) - - {:error, :not_found} -> - conn - |> put_status(:bad_request) - |> json(%{error: "pack \"#{name}\" is not found"}) - - {:error, :empty_values} -> - conn - |> put_status(:bad_request) - |> json(%{error: "pack name, shortcode or filename cannot be empty"}) - - {:error, _} -> - render_error( - conn, - :internal_server_error, - "Unexpected error occurred while adding file to pack." - ) - end - end - - def update_file(%{body_params: %{shortcode: shortcode} = params} = conn, %{name: name}) do - new_shortcode = params[:new_shortcode] - new_filename = params[:new_filename] - force = params[:force] - - with {:ok, pack} <- Pack.update_file(name, shortcode, new_shortcode, new_filename, force) do - json(conn, pack.files) - else - {:error, :doesnt_exist} -> - conn - |> put_status(:bad_request) - |> json(%{error: "Emoji \"#{shortcode}\" does not exist"}) - - {:error, :already_exists} -> - conn - |> put_status(:conflict) - |> json(%{ - error: - "New shortcode \"#{new_shortcode}\" is already used. If you want to override emoji use 'force' option" - }) - - {:error, :not_found} -> - conn - |> put_status(:bad_request) - |> json(%{error: "pack \"#{name}\" is not found"}) - - {:error, :empty_values} -> - conn - |> put_status(:bad_request) - |> json(%{error: "new_shortcode or new_filename cannot be empty"}) - - {:error, _} -> - render_error( - conn, - :internal_server_error, - "Unexpected error occurred while updating file in pack." - ) - end - end - - def delete_file(conn, %{name: name, shortcode: shortcode}) do - with {:ok, pack} <- Pack.delete_file(name, shortcode) do - json(conn, pack.files) - else - {:error, :doesnt_exist} -> - conn - |> put_status(:bad_request) - |> json(%{error: "Emoji \"#{shortcode}\" does not exist"}) - - {:error, :not_found} -> - conn - |> put_status(:bad_request) - |> json(%{error: "pack \"#{name}\" is not found"}) - - {:error, :empty_values} -> - conn - |> put_status(:bad_request) - |> json(%{error: "pack name or shortcode cannot be empty"}) - - {:error, _} -> - render_error( - conn, - :internal_server_error, - "Unexpected error occurred while removing file from pack." - ) - end - end - def import_from_filesystem(conn, _params) do with {:ok, names} <- Pack.import_from_filesystem() do json(conn, names) @@ -298,7 +196,4 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do |> json(%{error: "Error accessing emoji pack directory"}) end end - - defp get_filename(%Plug.Upload{filename: filename}), do: filename - defp get_filename(url) when is_binary(url), do: Path.basename(url) end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index c6433cc53..8a307d591 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -229,9 +229,9 @@ defmodule Pleroma.Web.Router do patch("/:name", EmojiPackController, :update) delete("/:name", EmojiPackController, :delete) - post("/:name/files", EmojiPackController, :add_file) - patch("/:name/files", EmojiPackController, :update_file) - delete("/:name/files", EmojiPackController, :delete_file) + post("/:name/files", EmojiFileController, :create) + patch("/:name/files", EmojiFileController, :update) + delete("/:name/files", EmojiFileController, :delete) end # Pack info / downloading -- cgit v1.2.3 From f5845ff03395816902a637a28412f85e671575e7 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Sat, 22 Aug 2020 10:42:02 +0300 Subject: upload emoji zip file --- lib/pleroma/emoji/pack.ex | 112 ++++++++++++++++----- lib/pleroma/utils.ex | 18 ++++ .../operations/pleroma_emoji_file_operation.ex | 6 +- .../controllers/emoji_file_controller.ex | 42 ++++++-- 4 files changed, 145 insertions(+), 33 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex index d076ae312..03aed33bb 100644 --- a/lib/pleroma/emoji/pack.ex +++ b/lib/pleroma/emoji/pack.ex @@ -17,6 +17,7 @@ defmodule Pleroma.Emoji.Pack do } alias Pleroma.Emoji + alias Pleroma.Emoji.Pack @spec create(String.t()) :: {:ok, t()} | {:error, File.posix()} | {:error, :empty_values} def create(name) do @@ -64,24 +65,93 @@ defmodule Pleroma.Emoji.Pack do end end - @spec add_file(String.t(), String.t(), Path.t(), Plug.Upload.t() | String.t()) :: - {:ok, t()} | {:error, File.posix() | atom()} - def add_file(name, shortcode, filename, file) do - with :ok <- validate_not_empty([name, shortcode, filename]), + @spec add_file(String.t(), String.t(), Path.t(), Plug.Upload.t()) :: + {:ok, t()} + | {:error, File.posix() | atom()} + def add_file(%Pack{} = pack, _, _, %Plug.Upload{content_type: "application/zip"} = file) do + with {:ok, zip_items} <- :zip.table(to_charlist(file.path)) do + emojies = + for {_, path, s, _, _, _} <- zip_items, elem(s, 2) == :regular do + filename = Path.basename(path) + shortcode = Path.basename(filename, Path.extname(filename)) + + %{ + path: path, + filename: path, + shortcode: shortcode, + exist: not is_nil(Pleroma.Emoji.get(shortcode)) + } + end + |> Enum.group_by(& &1[:exist]) + + case Map.get(emojies, false, []) do + [_ | _] = new_emojies -> + {:ok, tmp_dir} = Pleroma.Utils.tmp_dir("emoji") + + try do + {:ok, _emoji_files} = + :zip.unzip( + to_charlist(file.path), + [ + {:file_list, Enum.map(new_emojies, & &1[:path])}, + {:cwd, tmp_dir} + ] + ) + + {_, updated_pack} = + Enum.map_reduce(new_emojies, pack, fn item, emoji_pack -> + emoji_file = %Plug.Upload{ + filename: item[:filename], + path: Path.join(tmp_dir, item[:path]) + } + + {:ok, updated_pack} = + do_add_file( + emoji_pack, + item[:shortcode], + to_string(item[:filename]), + emoji_file + ) + + {item, updated_pack} + end) + + Emoji.reload() + + {:ok, updated_pack} + after + File.rm_rf(tmp_dir) + end + + _ -> + {:ok, pack} + end + end + end + + def add_file(%Pack{} = pack, shortcode, filename, file) do + with :ok <- validate_not_empty([shortcode, filename]), :ok <- validate_emoji_not_exists(shortcode), - {:ok, pack} <- load_pack(name), - :ok <- save_file(file, pack, filename), - {:ok, updated_pack} <- pack |> put_emoji(shortcode, filename) |> save_pack() do + {:ok, updated_pack} <- do_add_file(pack, shortcode, filename, file) do Emoji.reload() {:ok, updated_pack} end end - @spec delete_file(String.t(), String.t()) :: + defp do_add_file(pack, shortcode, filename, file) do + with :ok <- save_file(file, pack, filename), + {:ok, updated_pack} <- + pack + |> put_emoji(shortcode, filename) + |> save_pack() do + {:ok, updated_pack} + end + end + + @spec delete_file(t(), String.t()) :: {:ok, t()} | {:error, File.posix() | atom()} - def delete_file(name, shortcode) do - with :ok <- validate_not_empty([name, shortcode]), - {:ok, pack} <- load_pack(name), + def delete_file(%Pack{} = pack, shortcode) do + with :ok <- validate_not_empty([shortcode]), :ok <- remove_file(pack, shortcode), {:ok, updated_pack} <- pack |> delete_emoji(shortcode) |> save_pack() do Emoji.reload() @@ -89,11 +159,10 @@ defmodule Pleroma.Emoji.Pack do end end - @spec update_file(String.t(), String.t(), String.t(), String.t(), boolean()) :: + @spec update_file(t(), String.t(), String.t(), String.t(), boolean()) :: {:ok, t()} | {:error, File.posix() | atom()} - def update_file(name, shortcode, new_shortcode, new_filename, force) do - with :ok <- validate_not_empty([name, shortcode, new_shortcode, new_filename]), - {:ok, pack} <- load_pack(name), + def update_file(%Pack{} = pack, shortcode, new_shortcode, new_filename, force) do + with :ok <- validate_not_empty([shortcode, new_shortcode, new_filename]), {:ok, filename} <- get_filename(pack, shortcode), :ok <- validate_emoji_not_exists(new_shortcode, force), :ok <- rename_file(pack, filename, new_filename), @@ -386,19 +455,12 @@ defmodule Pleroma.Emoji.Pack do end end - defp save_file(file, pack, filename) do + defp save_file(%Plug.Upload{path: upload_path}, pack, filename) do file_path = Path.join(pack.path, filename) create_subdirs(file_path) - case file do - %Plug.Upload{path: upload_path} -> - # Copy the uploaded file from the temporary directory - with {:ok, _} <- File.copy(upload_path, file_path), do: :ok - - url when is_binary(url) -> - # Download and write the file - file_contents = Tesla.get!(url).body - File.write(file_path, file_contents) + with {:ok, _} <- File.copy(upload_path, file_path) do + :ok end end diff --git a/lib/pleroma/utils.ex b/lib/pleroma/utils.ex index 21d1159be..fcb8c64c7 100644 --- a/lib/pleroma/utils.ex +++ b/lib/pleroma/utils.ex @@ -24,4 +24,22 @@ defmodule Pleroma.Utils do def command_available?(command) do match?({_output, 0}, System.cmd("sh", ["-c", "command -v #{command}"])) end + + @doc "creates the uniq temporary directory" + @spec tmp_dir(String.t()) :: {:ok, String.t()} | {:error, :file.posix()} + def tmp_dir(prefix \\ "") do + sub_dir = [ + prefix, + Timex.to_unix(Timex.now()), + :os.getpid(), + String.downcase(Integer.to_string(:rand.uniform(0x100000000), 36)) + ] + + tmp_dir = Path.join(System.tmp_dir!(), Enum.join(sub_dir, "-")) + + case File.mkdir(tmp_dir) do + :ok -> {:ok, tmp_dir} + error -> error + end + end end diff --git a/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex index b6932157a..7dd4ce311 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex @@ -24,6 +24,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiFileOperation do parameters: [name_param()], responses: %{ 200 => Operation.response("Files Object", "application/json", files_object()), + 422 => Operation.response("Unprocessable Entity", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError), 400 => Operation.response("Bad Request", "application/json", ApiError), 409 => Operation.response("Conflict", "application/json", ApiError) } @@ -67,6 +69,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiFileOperation do parameters: [name_param()], responses: %{ 200 => Operation.response("Files Object", "application/json", files_object()), + 404 => Operation.response("Not Found", "application/json", ApiError), 400 => Operation.response("Bad Request", "application/json", ApiError), 409 => Operation.response("Conflict", "application/json", ApiError) } @@ -114,7 +117,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiFileOperation do ], responses: %{ 200 => Operation.response("Files Object", "application/json", files_object()), - 400 => Operation.response("Bad Request", "application/json", ApiError) + 400 => Operation.response("Bad Request", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) } } end diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex index ba9f07795..d10f46fde 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex @@ -22,7 +22,9 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do filename = params[:filename] || get_filename(params[:file]) shortcode = params[:shortcode] || Path.basename(filename, Path.extname(filename)) - with {:ok, pack} <- Pack.add_file(pack_name, shortcode, filename, params[:file]) do + with {:ok, pack} <- Pack.load_pack(pack_name), + {:ok, file} <- get_file(params[:file]), + {:ok, pack} <- Pack.add_file(pack, shortcode, filename, file) do json(conn, pack.files) else {:error, :already_exists} -> @@ -32,12 +34,12 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do {:error, :not_found} -> conn - |> put_status(:bad_request) + |> put_status(:not_found) |> json(%{error: "pack \"#{pack_name}\" is not found"}) {:error, :empty_values} -> conn - |> put_status(:bad_request) + |> put_status(:unprocessable_entity) |> json(%{error: "pack name, shortcode or filename cannot be empty"}) {:error, _} -> @@ -54,7 +56,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do new_filename = params[:new_filename] force = params[:force] - with {:ok, pack} <- Pack.update_file(pack_name, shortcode, new_shortcode, new_filename, force) do + with {:ok, pack} <- Pack.load_pack(pack_name), + {:ok, pack} <- Pack.update_file(pack, shortcode, new_shortcode, new_filename, force) do json(conn, pack.files) else {:error, :doesnt_exist} -> @@ -72,7 +75,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do {:error, :not_found} -> conn - |> put_status(:bad_request) + |> put_status(:not_found) |> json(%{error: "pack \"#{pack_name}\" is not found"}) {:error, :empty_values} -> @@ -90,7 +93,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do end def delete(conn, %{name: pack_name, shortcode: shortcode}) do - with {:ok, pack} <- Pack.delete_file(pack_name, shortcode) do + with {:ok, pack} <- Pack.load_pack(pack_name), + {:ok, pack} <- Pack.delete_file(pack, shortcode) do json(conn, pack.files) else {:error, :doesnt_exist} -> @@ -100,7 +104,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do {:error, :not_found} -> conn - |> put_status(:bad_request) + |> put_status(:not_found) |> json(%{error: "pack \"#{pack_name}\" is not found"}) {:error, :empty_values} -> @@ -119,4 +123,28 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do defp get_filename(%Plug.Upload{filename: filename}), do: filename defp get_filename(url) when is_binary(url), do: Path.basename(url) + + def get_file(%Plug.Upload{} = file), do: {:ok, file} + + def get_file(url) when is_binary(url) do + with {:ok, %Tesla.Env{body: body, status: code, headers: headers}} + when code in 200..299 <- Pleroma.HTTP.get(url) do + path = Plug.Upload.random_file!("emoji") + + content_type = + case List.keyfind(headers, "content-type", 0) do + {"content-type", value} -> value + nil -> nil + end + + File.write(path, body) + + {:ok, + %Plug.Upload{ + filename: Path.basename(url), + path: path, + content_type: content_type + }} + end + end end -- cgit v1.2.3 From 0922791e4d2233d527dda23e66a952e3f359a3fe Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Sat, 22 Aug 2020 10:56:26 +0300 Subject: updated errors on add emoji --- .../operations/pleroma_emoji_file_operation.ex | 6 +- .../controllers/emoji_file_controller.ex | 73 +++++++++------------- 2 files changed, 32 insertions(+), 47 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex index 7dd4ce311..efbfce75f 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex @@ -71,7 +71,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiFileOperation do 200 => Operation.response("Files Object", "application/json", files_object()), 404 => Operation.response("Not Found", "application/json", ApiError), 400 => Operation.response("Bad Request", "application/json", ApiError), - 409 => Operation.response("Conflict", "application/json", ApiError) + 409 => Operation.response("Conflict", "application/json", ApiError), + 422 => Operation.response("Unprocessable Entity", "application/json", ApiError) } } end @@ -118,7 +119,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiFileOperation do responses: %{ 200 => Operation.response("Files Object", "application/json", files_object()), 400 => Operation.response("Bad Request", "application/json", ApiError), - 404 => Operation.response("Not Found", "application/json", ApiError) + 404 => Operation.response("Not Found", "application/json", ApiError), + 422 => Operation.response("Unprocessable Entity", "application/json", ApiError) } } end diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex index d10f46fde..71c53df1d 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex @@ -32,22 +32,13 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do |> put_status(:conflict) |> json(%{error: "An emoji with the \"#{shortcode}\" shortcode already exists"}) - {:error, :not_found} -> - conn - |> put_status(:not_found) - |> json(%{error: "pack \"#{pack_name}\" is not found"}) - {:error, :empty_values} -> conn |> put_status(:unprocessable_entity) |> json(%{error: "pack name, shortcode or filename cannot be empty"}) - {:error, _} -> - render_error( - conn, - :internal_server_error, - "Unexpected error occurred while adding file to pack." - ) + {:error, _} = error -> + handle_error(conn, error, %{pack_name: pack_name}) end end @@ -60,11 +51,6 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do {:ok, pack} <- Pack.update_file(pack, shortcode, new_shortcode, new_filename, force) do json(conn, pack.files) else - {:error, :doesnt_exist} -> - conn - |> put_status(:bad_request) - |> json(%{error: "Emoji \"#{shortcode}\" does not exist"}) - {:error, :already_exists} -> conn |> put_status(:conflict) @@ -73,22 +59,13 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do "New shortcode \"#{new_shortcode}\" is already used. If you want to override emoji use 'force' option" }) - {:error, :not_found} -> - conn - |> put_status(:not_found) - |> json(%{error: "pack \"#{pack_name}\" is not found"}) - {:error, :empty_values} -> conn - |> put_status(:bad_request) + |> put_status(:unprocessable_entity) |> json(%{error: "new_shortcode or new_filename cannot be empty"}) - {:error, _} -> - render_error( - conn, - :internal_server_error, - "Unexpected error occurred while updating file in pack." - ) + {:error, _} = error -> + handle_error(conn, error, %{pack_name: pack_name, code: shortcode}) end end @@ -97,30 +74,36 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do {:ok, pack} <- Pack.delete_file(pack, shortcode) do json(conn, pack.files) else - {:error, :doesnt_exist} -> - conn - |> put_status(:bad_request) - |> json(%{error: "Emoji \"#{shortcode}\" does not exist"}) - - {:error, :not_found} -> - conn - |> put_status(:not_found) - |> json(%{error: "pack \"#{pack_name}\" is not found"}) - {:error, :empty_values} -> conn - |> put_status(:bad_request) + |> put_status(:unprocessable_entity) |> json(%{error: "pack name or shortcode cannot be empty"}) - {:error, _} -> - render_error( - conn, - :internal_server_error, - "Unexpected error occurred while removing file from pack." - ) + {:error, _} = error -> + handle_error(conn, error, %{pack_name: pack_name, code: shortcode}) end end + defp handle_error(conn, {:error, :doesnt_exist}, %{code: emoji_code}) do + conn + |> put_status(:bad_request) + |> json(%{error: "Emoji \"#{emoji_code}\" does not exist"}) + end + + defp handle_error(conn, {:error, :not_found}, %{pack_name: pack_name}) do + conn + |> put_status(:not_found) + |> json(%{error: "pack \"#{pack_name}\" is not found"}) + end + + defp handle_error(conn, {:error, _}, _) do + render_error( + conn, + :internal_server_error, + "Unexpected error occurred while adding file to pack." + ) + end + defp get_filename(%Plug.Upload{filename: filename}), do: filename defp get_filename(url) when is_binary(url), do: Path.basename(url) -- cgit v1.2.3 From 14ec12ac956ffa9964254cb3be390c9903103da3 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Mon, 24 Aug 2020 09:47:25 +0300 Subject: added tests --- lib/pleroma/emoji.ex | 3 ++ lib/pleroma/emoji/pack.ex | 129 +++++++++++++++++++++++----------------------- lib/pleroma/utils.ex | 16 +++--- 3 files changed, 77 insertions(+), 71 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex index f6016d73f..04936155b 100644 --- a/lib/pleroma/emoji.ex +++ b/lib/pleroma/emoji.ex @@ -56,6 +56,9 @@ defmodule Pleroma.Emoji do end end + @spec exist?(String.t()) :: boolean() + def exist?(name), do: not is_nil(get(name)) + @doc "Returns all the emojos!!" @spec get_all() :: list({String.t(), String.t(), String.t()}) def get_all do diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex index 03aed33bb..dd79bdfab 100644 --- a/lib/pleroma/emoji/pack.ex +++ b/lib/pleroma/emoji/pack.ex @@ -65,71 +65,73 @@ defmodule Pleroma.Emoji.Pack do end end + @spec unpack_zip_emojies(list(tuple())) :: list(map()) + defp unpack_zip_emojies(zip_files) do + Enum.reduce(zip_files, [], fn + {_, path, s, _, _, _}, acc when elem(s, 2) == :regular -> + with( + filename <- Path.basename(path), + shortcode <- Path.basename(filename, Path.extname(filename)), + false <- Emoji.exist?(shortcode) + ) do + acc ++ [%{path: path, filename: path, shortcode: shortcode}] + else + _ -> acc + end + + _, acc -> + acc + end) + end + @spec add_file(String.t(), String.t(), Path.t(), Plug.Upload.t()) :: {:ok, t()} | {:error, File.posix() | atom()} def add_file(%Pack{} = pack, _, _, %Plug.Upload{content_type: "application/zip"} = file) do - with {:ok, zip_items} <- :zip.table(to_charlist(file.path)) do - emojies = - for {_, path, s, _, _, _} <- zip_items, elem(s, 2) == :regular do - filename = Path.basename(path) - shortcode = Path.basename(filename, Path.extname(filename)) - - %{ - path: path, - filename: path, - shortcode: shortcode, - exist: not is_nil(Pleroma.Emoji.get(shortcode)) - } - end - |> Enum.group_by(& &1[:exist]) - - case Map.get(emojies, false, []) do - [_ | _] = new_emojies -> - {:ok, tmp_dir} = Pleroma.Utils.tmp_dir("emoji") - - try do - {:ok, _emoji_files} = - :zip.unzip( - to_charlist(file.path), - [ - {:file_list, Enum.map(new_emojies, & &1[:path])}, - {:cwd, tmp_dir} - ] + with {:ok, zip_files} <- :zip.table(to_charlist(file.path)), + [_ | _] = emojies <- unpack_zip_emojies(zip_files), + {:ok, tmp_dir} <- Pleroma.Utils.tmp_dir("emoji") do + try do + {:ok, _emoji_files} = + :zip.unzip( + to_charlist(file.path), + [{:file_list, Enum.map(emojies, & &1[:path])}, {:cwd, tmp_dir}] + ) + + {_, updated_pack} = + Enum.map_reduce(emojies, pack, fn item, emoji_pack -> + emoji_file = %Plug.Upload{ + filename: item[:filename], + path: Path.join(tmp_dir, item[:path]) + } + + {:ok, updated_pack} = + do_add_file( + emoji_pack, + item[:shortcode], + to_string(item[:filename]), + emoji_file ) - {_, updated_pack} = - Enum.map_reduce(new_emojies, pack, fn item, emoji_pack -> - emoji_file = %Plug.Upload{ - filename: item[:filename], - path: Path.join(tmp_dir, item[:path]) - } - - {:ok, updated_pack} = - do_add_file( - emoji_pack, - item[:shortcode], - to_string(item[:filename]), - emoji_file - ) - - {item, updated_pack} - end) - - Emoji.reload() - - {:ok, updated_pack} - after - File.rm_rf(tmp_dir) - end + {item, updated_pack} + end) + + Emoji.reload() - _ -> - {:ok, pack} + {:ok, updated_pack} + after + File.rm_rf(tmp_dir) end + else + {:error, _} = error -> + error + + _ -> + {:ok, pack} end end - def add_file(%Pack{} = pack, shortcode, filename, file) do + def add_file(%Pack{} = pack, shortcode, filename, %Plug.Upload{} = file) do with :ok <- validate_not_empty([shortcode, filename]), :ok <- validate_emoji_not_exists(shortcode), {:ok, updated_pack} <- do_add_file(pack, shortcode, filename, file) do @@ -139,12 +141,10 @@ defmodule Pleroma.Emoji.Pack do end defp do_add_file(pack, shortcode, filename, file) do - with :ok <- save_file(file, pack, filename), - {:ok, updated_pack} <- - pack - |> put_emoji(shortcode, filename) - |> save_pack() do - {:ok, updated_pack} + with :ok <- save_file(file, pack, filename) do + pack + |> put_emoji(shortcode, filename) + |> save_pack() end end @@ -312,9 +312,10 @@ defmodule Pleroma.Emoji.Pack do defp validate_emoji_not_exists(_shortcode, true), do: :ok defp validate_emoji_not_exists(shortcode, _) do - case Emoji.get(shortcode) do - nil -> :ok - _ -> {:error, :already_exists} + if Emoji.exist?(shortcode) do + {:error, :already_exists} + else + :ok end end @@ -466,7 +467,7 @@ defmodule Pleroma.Emoji.Pack do defp put_emoji(pack, shortcode, filename) do files = Map.put(pack.files, shortcode, filename) - %{pack | files: files} + %{pack | files: files, files_count: length(Map.keys(files))} end defp delete_emoji(pack, shortcode) do diff --git a/lib/pleroma/utils.ex b/lib/pleroma/utils.ex index fcb8c64c7..e95766223 100644 --- a/lib/pleroma/utils.ex +++ b/lib/pleroma/utils.ex @@ -28,14 +28,16 @@ defmodule Pleroma.Utils do @doc "creates the uniq temporary directory" @spec tmp_dir(String.t()) :: {:ok, String.t()} | {:error, :file.posix()} def tmp_dir(prefix \\ "") do - sub_dir = [ - prefix, - Timex.to_unix(Timex.now()), - :os.getpid(), - String.downcase(Integer.to_string(:rand.uniform(0x100000000), 36)) - ] + sub_dir = + [ + prefix, + Timex.to_unix(Timex.now()), + :os.getpid(), + String.downcase(Integer.to_string(:rand.uniform(0x100000000), 36)) + ] + |> Enum.join("-") - tmp_dir = Path.join(System.tmp_dir!(), Enum.join(sub_dir, "-")) + tmp_dir = Path.join(System.tmp_dir!(), sub_dir) case File.mkdir(tmp_dir) do :ok -> {:ok, tmp_dir} -- cgit v1.2.3 From b267b751d451508bd655142a4711776ea15484f4 Mon Sep 17 00:00:00 2001 From: Maksim Date: Tue, 25 Aug 2020 05:38:25 +0000 Subject: Apply 1 suggestion(s) to 1 file(s) --- lib/pleroma/emoji/pack.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex index dd79bdfab..930bbb422 100644 --- a/lib/pleroma/emoji/pack.ex +++ b/lib/pleroma/emoji/pack.ex @@ -74,7 +74,7 @@ defmodule Pleroma.Emoji.Pack do shortcode <- Path.basename(filename, Path.extname(filename)), false <- Emoji.exist?(shortcode) ) do - acc ++ [%{path: path, filename: path, shortcode: shortcode}] + [%{path: path, filename: path, shortcode: shortcode} | acc] else _ -> acc end -- cgit v1.2.3 From 489a107cf449a10c7f6ac9a4b8d4a7f9f7314c5c Mon Sep 17 00:00:00 2001 From: Maksim Date: Sun, 13 Sep 2020 11:54:15 +0000 Subject: Apply 1 suggestion(s) to 1 file(s) --- lib/pleroma/emoji/pack.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex index 930bbb422..0b3f8f00b 100644 --- a/lib/pleroma/emoji/pack.ex +++ b/lib/pleroma/emoji/pack.ex @@ -84,7 +84,7 @@ defmodule Pleroma.Emoji.Pack do end) end - @spec add_file(String.t(), String.t(), Path.t(), Plug.Upload.t()) :: + @spec add_file(t(), String.t(), Path.t(), Plug.Upload.t()) :: {:ok, t()} | {:error, File.posix() | atom()} def add_file(%Pack{} = pack, _, _, %Plug.Upload{content_type: "application/zip"} = file) do -- cgit v1.2.3