Add OpenAPI spec for PleromaAPI.EmojiAPIController
authorEgor Kislitsyn <egor@kislitsyn.com>
Thu, 14 May 2020 15:21:51 +0000 (19:21 +0400)
committerEgor Kislitsyn <egor@kislitsyn.com>
Mon, 18 May 2020 15:28:46 +0000 (19:28 +0400)
lib/pleroma/emoji/pack.ex
lib/pleroma/web/api_spec/operations/pleroma_emoji_operation.ex [new file with mode: 0644]
lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex
lib/pleroma/web/router.ex
test/web/api_spec/schema_examples_test.exs
test/web/pleroma_api/controllers/emoji_api_controller_test.exs

index 242344374ffdb8a0b2708dd4301a93f4bd73233e..c7b423fbd85a5ea696b584751e2ccfd2783014fa 100644 (file)
@@ -443,10 +443,10 @@ defmodule Pleroma.Emoji.Pack do
     pack = load_pack(name)
 
     fb_sha_changed? =
-      not is_nil(data["fallback-src"]) and data["fallback-src"] != pack.pack["fallback-src"]
+      not is_nil(data[:"fallback-src"]) and data[:"fallback-src"] != pack.pack[:"fallback-src"]
 
     with {_, true} <- {:update?, fb_sha_changed?},
-         {:ok, %{body: zip}} <- Tesla.get(data["fallback-src"]),
+         {:ok, %{body: zip}} <- Tesla.get(data[:"fallback-src"]),
          {:ok, f_list} <- :zip.unzip(zip, [:memory]),
          {_, true} <- {:has_all_files?, has_all_files?(pack.files, f_list)} do
       fallback_sha = :crypto.hash(:sha256, zip) |> Base.encode16()
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_emoji_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_emoji_operation.ex
new file mode 100644 (file)
index 0000000..fc881e6
--- /dev/null
@@ -0,0 +1,390 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.PleromaEmojiOperation 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 remote_operation do
+    %Operation{
+      tags: ["Emoji Packs"],
+      summary: "Make request to another instance for emoji packs list",
+      security: [%{"oAuth" => ["write"]}],
+      parameters: [url_param()],
+      operationId: "PleromaAPI.EmojiAPIController.remote",
+      responses: %{
+        200 => emoji_packs_response(),
+        500 => Operation.response("Error", "application/json", ApiError)
+      }
+    }
+  end
+
+  def index_operation do
+    %Operation{
+      tags: ["Emoji Packs"],
+      summary: "Lists local custom emoji packs",
+      operationId: "PleromaAPI.EmojiAPIController.index",
+      responses: %{
+        200 => emoji_packs_response()
+      }
+    }
+  end
+
+  def show_operation do
+    %Operation{
+      tags: ["Emoji Packs"],
+      summary: "Show emoji pack",
+      operationId: "PleromaAPI.EmojiAPIController.show",
+      parameters: [name_param()],
+      responses: %{
+        200 => Operation.response("Emoji Pack", "application/json", emoji_pack()),
+        400 => Operation.response("Bad Request", "application/json", ApiError),
+        404 => Operation.response("Not Found", "application/json", ApiError)
+      }
+    }
+  end
+
+  def archive_operation do
+    %Operation{
+      tags: ["Emoji Packs"],
+      summary: "Requests a local pack archive from the instance",
+      operationId: "PleromaAPI.EmojiAPIController.archive",
+      parameters: [name_param()],
+      responses: %{
+        200 =>
+          Operation.response("Archive file", "application/octet-stream", %Schema{
+            type: :string,
+            format: :binary
+          }),
+        403 => Operation.response("Forbidden", "application/json", ApiError),
+        404 => Operation.response("Not Found", "application/json", ApiError)
+      }
+    }
+  end
+
+  def download_operation do
+    %Operation{
+      tags: ["Emoji Packs"],
+      summary: "Download pack from another instance",
+      operationId: "PleromaAPI.EmojiAPIController.download",
+      security: [%{"oAuth" => ["write"]}],
+      requestBody: request_body("Parameters", download_request(), required: true),
+      responses: %{
+        200 => ok_response(),
+        500 => Operation.response("Error", "application/json", ApiError)
+      }
+    }
+  end
+
+  defp download_request do
+    %Schema{
+      type: :object,
+      required: [:url, :name],
+      properties: %{
+        url: %Schema{
+          type: :string,
+          format: :uri,
+          description: "URL of the instance to download from"
+        },
+        name: %Schema{type: :string, format: :uri, description: "Pack Name"},
+        as: %Schema{type: :string, format: :uri, description: "Save as"}
+      }
+    }
+  end
+
+  def create_operation do
+    %Operation{
+      tags: ["Emoji Packs"],
+      summary: "Create an empty pack",
+      operationId: "PleromaAPI.EmojiAPIController.create",
+      security: [%{"oAuth" => ["write"]}],
+      parameters: [name_param()],
+      responses: %{
+        200 => ok_response(),
+        400 => Operation.response("Not Found", "application/json", ApiError),
+        409 => Operation.response("Conflict", "application/json", ApiError),
+        500 => Operation.response("Error", "application/json", ApiError)
+      }
+    }
+  end
+
+  def delete_operation do
+    %Operation{
+      tags: ["Emoji Packs"],
+      summary: "Delete a custom emoji pack",
+      operationId: "PleromaAPI.EmojiAPIController.delete",
+      security: [%{"oAuth" => ["write"]}],
+      parameters: [name_param()],
+      responses: %{
+        200 => ok_response(),
+        400 => Operation.response("Bad Request", "application/json", ApiError),
+        404 => Operation.response("Not Found", "application/json", ApiError)
+      }
+    }
+  end
+
+  def update_operation do
+    %Operation{
+      tags: ["Emoji Packs"],
+      summary: "Updates (replaces) pack metadata",
+      operationId: "PleromaAPI.EmojiAPIController.update",
+      security: [%{"oAuth" => ["write"]}],
+      requestBody: request_body("Parameters", update_request(), required: true),
+      parameters: [name_param()],
+      responses: %{
+        200 => Operation.response("Metadata", "application/json", metadata()),
+        400 => Operation.response("Bad Request", "application/json", ApiError)
+      }
+    }
+  end
+
+  def add_file_operation do
+    %Operation{
+      tags: ["Emoji Packs"],
+      summary: "Add new file to the pack",
+      operationId: "PleromaAPI.EmojiAPIController.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 uniq 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.EmojiAPIController.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.EmojiAPIController.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"],
+      summary: "Imports packs from filesystem",
+      operationId: "PleromaAPI.EmojiAPIController.import",
+      security: [%{"oAuth" => ["write"]}],
+      responses: %{
+        200 =>
+          Operation.response("Array of imported pack names", "application/json", %Schema{
+            type: :array,
+            items: %Schema{type: :string}
+          })
+      }
+    }
+  end
+
+  defp name_param do
+    Operation.parameter(:name, :path, :string, "Pack Name", example: "cofe", required: true)
+  end
+
+  defp url_param do
+    Operation.parameter(
+      :url,
+      :query,
+      %Schema{type: :string, format: :uri},
+      "URL of the instance",
+      required: true
+    )
+  end
+
+  defp ok_response do
+    Operation.response("Ok", "application/json", %Schema{type: :string, example: "ok"})
+  end
+
+  defp emoji_packs_response do
+    Operation.response(
+      "Object with pack names as keys and pack contents as values",
+      "application/json",
+      %Schema{
+        type: :object,
+        additionalProperties: emoji_pack(),
+        example: %{
+          "emojos" => emoji_pack().example
+        }
+      }
+    )
+  end
+
+  defp emoji_pack do
+    %Schema{
+      title: "EmojiPack",
+      type: :object,
+      properties: %{
+        files: files_object(),
+        pack: %Schema{
+          type: :object,
+          properties: %{
+            license: %Schema{type: :string},
+            homepage: %Schema{type: :string, format: :uri},
+            description: %Schema{type: :string},
+            "can-download": %Schema{type: :boolean},
+            "share-files": %Schema{type: :boolean},
+            "download-sha256": %Schema{type: :string}
+          }
+        }
+      },
+      example: %{
+        "files" => %{"emacs" => "emacs.png", "guix" => "guix.png"},
+        "pack" => %{
+          "license" => "Test license",
+          "homepage" => "https://pleroma.social",
+          "description" => "Test description",
+          "can-download" => true,
+          "share-files" => true,
+          "download-sha256" => "57482F30674FD3DE821FF48C81C00DA4D4AF1F300209253684ABA7075E5FC238"
+        }
+      }
+    }
+  end
+
+  defp files_object do
+    %Schema{
+      type: :object,
+      additionalProperties: %Schema{type: :string},
+      description: "Object with emoji names as keys and filenames as values"
+    }
+  end
+
+  defp update_request do
+    %Schema{
+      type: :object,
+      properties: %{
+        metadata: %Schema{
+          type: :object,
+          description: "Metadata to replace the old one",
+          properties: %{
+            license: %Schema{type: :string},
+            homepage: %Schema{type: :string, format: :uri},
+            description: %Schema{type: :string},
+            "fallback-src": %Schema{
+              type: :string,
+              format: :uri,
+              description: "Fallback url to download pack from"
+            },
+            "fallback-src-sha256": %Schema{
+              type: :string,
+              description: "SHA256 encoded for fallback pack archive"
+            },
+            "share-files": %Schema{type: :boolean, description: "Is pack allowed for sharing?"}
+          }
+        }
+      }
+    }
+  end
+
+  defp metadata do
+    %Schema{
+      type: :object,
+      properties: %{
+        license: %Schema{type: :string},
+        homepage: %Schema{type: :string, format: :uri},
+        description: %Schema{type: :string},
+        "fallback-src": %Schema{
+          type: :string,
+          format: :uri,
+          description: "Fallback url to download pack from"
+        },
+        "fallback-src-sha256": %Schema{
+          type: :string,
+          description: "SHA256 encoded for fallback pack archive"
+        },
+        "share-files": %Schema{type: :boolean, description: "Is pack allowed for sharing?"}
+      }
+    }
+  end
+end
index d276b96a41a9343e16567220eb6451da2603c371..e20c118600d81d742361896a305a4489de9f4ed1 100644 (file)
@@ -3,6 +3,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
 
   alias Pleroma.Emoji.Pack
 
+  plug(Pleroma.Web.ApiSpec.CastAndValidate)
+
   plug(
     Pleroma.Plugs.OAuthScopesPlug,
     %{scopes: ["write"], admin: true}
@@ -19,13 +21,12 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
          ]
   )
 
-  plug(
-    :skip_plug,
-    [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug]
-    when action in [:archive, :show, :list]
-  )
+  @skip_plugs [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug]
+  plug(:skip_plug, @skip_plugs when action in [:archive, :show, :list])
+
+  defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaEmojiOperation
 
-  def remote(conn, %{"url" => url}) do
+  def remote(conn, %{url: url}) do
     with {:ok, packs} <- Pack.list_remote(url) do
       json(conn, packs)
     else
@@ -36,12 +37,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
     end
   end
 
-  def list(conn, _params) do
+  def index(conn, _params) do
     emoji_path =
-      Path.join(
-        Pleroma.Config.get!([:instance, :static_dir]),
-        "emoji"
-      )
+      [:instance, :static_dir]
+      |> Pleroma.Config.get!()
+      |> Path.join("emoji")
 
     with {:ok, packs} <- Pack.list_local() do
       json(conn, packs)
@@ -60,7 +60,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
     end
   end
 
-  def show(conn, %{"name" => name}) do
+  def show(conn, %{name: name}) do
     name = String.trim(name)
 
     with {:ok, pack} <- Pack.show(name) do
@@ -78,7 +78,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
     end
   end
 
-  def archive(conn, %{"name" => name}) do
+  def archive(conn, %{name: name}) do
     with {:ok, archive} <- Pack.get_archive(name) do
       send_download(conn, {:binary, archive}, filename: "#{name}.zip")
     else
@@ -97,8 +97,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
     end
   end
 
-  def download(conn, %{"url" => url, "name" => name} = params) do
-    with :ok <- Pack.download(name, url, params["as"]) do
+  def download(%{body_params: %{url: url, name: name} = params} = conn, _) do
+    with :ok <- Pack.download(name, url, params[:as]) do
       json(conn, "ok")
     else
       {:shareable, _} ->
@@ -118,7 +118,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
     end
   end
 
-  def create(conn, %{"name" => name}) do
+  def create(conn, %{name: name}) do
     name = String.trim(name)
 
     with :ok <- Pack.create(name) do
@@ -143,7 +143,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
     end
   end
 
-  def delete(conn, %{"name" => name}) do
+  def delete(conn, %{name: name}) do
     name = String.trim(name)
 
     with {:ok, deleted} when deleted != [] <- Pack.delete(name) do
@@ -166,7 +166,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
     end
   end
 
-  def update(conn, %{"name" => name, "metadata" => metadata}) do
+  def update(%{body_params: %{metadata: metadata}} = conn, %{name: name}) do
     with {:ok, pack} <- Pack.update_metadata(name, metadata) do
       json(conn, pack.pack)
     else
@@ -184,11 +184,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
     end
   end
 
-  def add_file(conn, %{"name" => name} = params) do
-    filename = params["filename"] || get_filename(params["file"])
-    shortcode = params["shortcode"] || Path.basename(filename, Path.extname(filename))
+  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
+    with {:ok, pack} <- Pack.add_file(name, shortcode, filename, params[:file]) do
       json(conn, pack.files)
     else
       {:exists, _} ->
@@ -215,10 +215,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
     end
   end
 
-  def update_file(conn, %{"name" => name, "shortcode" => shortcode} = params) do
-    new_shortcode = params["new_shortcode"]
-    new_filename = params["new_filename"]
-    force = params["force"] == true
+  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)
@@ -255,7 +255,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
     end
   end
 
-  def delete_file(conn, %{"name" => name, "shortcode" => shortcode}) do
+  def delete_file(conn, %{name: name, shortcode: shortcode}) do
     with {:ok, pack} <- Pack.delete_file(name, shortcode) do
       json(conn, pack.files)
     else
index d77a61361285c6e5f992b91298dcb58fe471f6b0..0d4ebf4cef9cbe432f2e9e639237715d280326f5 100644 (file)
@@ -231,7 +231,8 @@ defmodule Pleroma.Web.Router do
 
     # Pack info / downloading
     scope "/packs" do
-      get("/", EmojiAPIController, :list)
+      pipe_through(:api)
+      get("/", EmojiAPIController, :index)
       get("/:name", EmojiAPIController, :show)
       get("/:name/archive", EmojiAPIController, :archive)
     end
index 88b6f07cb0729bd0fe4c846530016105e4ffbbee..f00e834fc42a48f8abbcae4a10eb7e60bacd4a5b 100644 (file)
@@ -28,7 +28,7 @@ defmodule Pleroma.Web.ApiSpec.SchemaExamplesTest do
       end
     end
 
-    for {status, response} <- operation.responses do
+    for {status, response} <- operation.responses, is_map(response.content[@content_type]) do
       describe "#{operation.operationId} - #{status} Response" do
         @schema resolve_schema(response.content[@content_type].schema)
 
index d343256fe2652473e5e4dd8f9d9668808460b806..c625a5c43c09492e4ac9700ab1e315bcf90a5bf4 100644 (file)
@@ -28,7 +28,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
   end
 
   test "GET /api/pleroma/emoji/packs", %{conn: conn} do
-    resp = conn |> get("/api/pleroma/emoji/packs") |> json_response(200)
+    resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
 
     shared = resp["test_pack"]
     assert shared["files"] == %{"blank" => "blank.png"}
@@ -46,7 +46,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       resp =
         conn
         |> get("/api/pleroma/emoji/packs")
-        |> json_response(200)
+        |> json_response_and_validate_schema(200)
 
       mock(fn
         %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
@@ -60,10 +60,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       end)
 
       assert admin_conn
-             |> get("/api/pleroma/emoji/packs/remote", %{
-               url: "https://example.com"
-             })
-             |> json_response(200) == resp
+             |> get("/api/pleroma/emoji/packs/remote?url=https://example.com")
+             |> json_response_and_validate_schema(200) == resp
     end
 
     test "non shareable instance", %{admin_conn: admin_conn} do
@@ -76,8 +74,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       end)
 
       assert admin_conn
-             |> get("/api/pleroma/emoji/packs/remote", %{url: "https://example.com"})
-             |> json_response(500) == %{
+             |> get("/api/pleroma/emoji/packs/remote?url=https://example.com")
+             |> json_response_and_validate_schema(500) == %{
                "error" => "The requested instance does not support sharing emoji packs"
              }
     end
@@ -99,7 +97,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
     test "non existing pack", %{conn: conn} do
       assert conn
              |> get("/api/pleroma/emoji/packs/test_pack_for_import/archive")
-             |> json_response(:not_found) == %{
+             |> json_response_and_validate_schema(:not_found) == %{
                "error" => "Pack test_pack_for_import does not exist"
              }
     end
@@ -107,7 +105,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
     test "non downloadable pack", %{conn: conn} do
       assert conn
              |> get("/api/pleroma/emoji/packs/test_pack_nonshared/archive")
-             |> json_response(:forbidden) == %{
+             |> json_response_and_validate_schema(:forbidden) == %{
                "error" =>
                  "Pack test_pack_nonshared cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing"
              }
@@ -132,7 +130,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
         } ->
           conn
           |> get("/api/pleroma/emoji/packs/test_pack")
-          |> json_response(200)
+          |> json_response_and_validate_schema(200)
           |> json()
 
         %{
@@ -150,7 +148,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
         } ->
           conn
           |> get("/api/pleroma/emoji/packs/test_pack_nonshared")
-          |> json_response(200)
+          |> json_response_and_validate_schema(200)
           |> json()
 
         %{
@@ -161,23 +159,25 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       end)
 
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> post("/api/pleroma/emoji/packs/download", %{
                url: "https://example.com",
                name: "test_pack",
                as: "test_pack2"
              })
-             |> json_response(200) == "ok"
+             |> json_response_and_validate_schema(200) == "ok"
 
       assert File.exists?("#{@emoji_path}/test_pack2/pack.json")
       assert File.exists?("#{@emoji_path}/test_pack2/blank.png")
 
       assert admin_conn
              |> delete("/api/pleroma/emoji/packs/test_pack2")
-             |> json_response(200) == "ok"
+             |> json_response_and_validate_schema(200) == "ok"
 
       refute File.exists?("#{@emoji_path}/test_pack2")
 
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> post(
                "/api/pleroma/emoji/packs/download",
                %{
@@ -186,14 +186,14 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
                  as: "test_pack_nonshared2"
                }
              )
-             |> json_response(200) == "ok"
+             |> json_response_and_validate_schema(200) == "ok"
 
       assert File.exists?("#{@emoji_path}/test_pack_nonshared2/pack.json")
       assert File.exists?("#{@emoji_path}/test_pack_nonshared2/blank.png")
 
       assert admin_conn
              |> delete("/api/pleroma/emoji/packs/test_pack_nonshared2")
-             |> json_response(200) == "ok"
+             |> json_response_and_validate_schema(200) == "ok"
 
       refute File.exists?("#{@emoji_path}/test_pack_nonshared2")
     end
@@ -208,6 +208,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       end)
 
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> post(
                "/api/pleroma/emoji/packs/download",
                %{
@@ -216,7 +217,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
                  as: "test_pack2"
                }
              )
-             |> json_response(500) == %{
+             |> json_response_and_validate_schema(500) == %{
                "error" => "The requested instance does not support sharing emoji packs"
              }
     end
@@ -249,12 +250,13 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       end)
 
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> post("/api/pleroma/emoji/packs/download", %{
                url: "https://example.com",
                name: "pack_bad_sha",
                as: "pack_bad_sha2"
              })
-             |> json_response(:internal_server_error) == %{
+             |> json_response_and_validate_schema(:internal_server_error) == %{
                "error" => "SHA256 for the pack doesn't match the one sent by the server"
              }
     end
@@ -278,12 +280,13 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       end)
 
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> post("/api/pleroma/emoji/packs/download", %{
                url: "https://example.com",
                name: "test_pack",
                as: "test_pack2"
              })
-             |> json_response(:internal_server_error) == %{
+             |> json_response_and_validate_schema(:internal_server_error) == %{
                "error" =>
                  "The pack was not set as shared and there is no fallback src to download from"
              }
@@ -311,8 +314,9 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
 
     test "for a pack without a fallback source", ctx do
       assert ctx[:admin_conn]
+             |> put_req_header("content-type", "multipart/form-data")
              |> patch("/api/pleroma/emoji/packs/test_pack", %{"metadata" => ctx[:new_data]})
-             |> json_response(200) == ctx[:new_data]
+             |> json_response_and_validate_schema(200) == ctx[:new_data]
 
       assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == ctx[:new_data]
     end
@@ -336,8 +340,9 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
         )
 
       assert ctx[:admin_conn]
+             |> put_req_header("content-type", "multipart/form-data")
              |> patch("/api/pleroma/emoji/packs/test_pack", %{metadata: new_data})
-             |> json_response(200) == new_data_with_sha
+             |> json_response_and_validate_schema(200) == new_data_with_sha
 
       assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha
     end
@@ -355,8 +360,9 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack")
 
       assert ctx[:admin_conn]
+             |> put_req_header("content-type", "multipart/form-data")
              |> patch("/api/pleroma/emoji/packs/test_pack", %{metadata: new_data})
-             |> json_response(:bad_request) == %{
+             |> json_response_and_validate_schema(:bad_request) == %{
                "error" => "The fallback archive does not have all files specified in pack.json"
              }
     end
@@ -376,6 +382,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
 
     test "create shortcode exists", %{admin_conn: admin_conn} do
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> post("/api/pleroma/emoji/packs/test_pack/files", %{
                shortcode: "blank",
                filename: "dir/blank.png",
@@ -384,7 +391,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
                  path: "#{@emoji_path}/test_pack/blank.png"
                }
              })
-             |> json_response(:conflict) == %{
+             |> json_response_and_validate_schema(:conflict) == %{
                "error" => "An emoji with the \"blank\" shortcode already exists"
              }
     end
@@ -393,6 +400,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir/") end)
 
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> post("/api/pleroma/emoji/packs/test_pack/files", %{
                shortcode: "blank2",
                filename: "dir/blank.png",
@@ -401,17 +409,21 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
                  path: "#{@emoji_path}/test_pack/blank.png"
                }
              })
-             |> json_response(200) == %{"blank" => "blank.png", "blank2" => "dir/blank.png"}
+             |> json_response_and_validate_schema(200) == %{
+               "blank" => "blank.png",
+               "blank2" => "dir/blank.png"
+             }
 
       assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png")
 
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> patch("/api/pleroma/emoji/packs/test_pack/files", %{
                shortcode: "blank",
                new_shortcode: "blank2",
                new_filename: "dir_2/blank_3.png"
              })
-             |> json_response(:conflict) == %{
+             |> json_response_and_validate_schema(:conflict) == %{
                "error" =>
                  "New shortcode \"blank2\" is already used. If you want to override emoji use 'force' option"
              }
@@ -421,6 +433,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir_2/") end)
 
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> post("/api/pleroma/emoji/packs/test_pack/files", %{
                shortcode: "blank2",
                filename: "dir/blank.png",
@@ -429,18 +442,22 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
                  path: "#{@emoji_path}/test_pack/blank.png"
                }
              })
-             |> json_response(200) == %{"blank" => "blank.png", "blank2" => "dir/blank.png"}
+             |> json_response_and_validate_schema(200) == %{
+               "blank" => "blank.png",
+               "blank2" => "dir/blank.png"
+             }
 
       assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png")
 
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> patch("/api/pleroma/emoji/packs/test_pack/files", %{
                shortcode: "blank2",
                new_shortcode: "blank3",
                new_filename: "dir_2/blank_3.png",
                force: true
              })
-             |> json_response(200) == %{
+             |> json_response_and_validate_schema(200) == %{
                "blank" => "blank.png",
                "blank3" => "dir_2/blank_3.png"
              }
@@ -450,6 +467,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
 
     test "with empty filename", %{admin_conn: admin_conn} do
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> post("/api/pleroma/emoji/packs/test_pack/files", %{
                shortcode: "blank2",
                filename: "",
@@ -458,13 +476,14 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
                  path: "#{@emoji_path}/test_pack/blank.png"
                }
              })
-             |> json_response(:bad_request) == %{
+             |> json_response_and_validate_schema(:bad_request) == %{
                "error" => "pack name, shortcode or filename cannot be empty"
              }
     end
 
     test "add file with not loaded pack", %{admin_conn: admin_conn} do
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> post("/api/pleroma/emoji/packs/not_loaded/files", %{
                shortcode: "blank2",
                filename: "dir/blank.png",
@@ -473,37 +492,43 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
                  path: "#{@emoji_path}/test_pack/blank.png"
                }
              })
-             |> json_response(:bad_request) == %{
+             |> json_response_and_validate_schema(:bad_request) == %{
                "error" => "pack \"not_loaded\" is not found"
              }
     end
 
     test "remove file with not loaded pack", %{admin_conn: admin_conn} do
       assert admin_conn
-             |> delete("/api/pleroma/emoji/packs/not_loaded/files", %{shortcode: "blank3"})
-             |> json_response(:bad_request) == %{"error" => "pack \"not_loaded\" is not found"}
+             |> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=blank3")
+             |> json_response_and_validate_schema(:bad_request) == %{
+               "error" => "pack \"not_loaded\" is not found"
+             }
     end
 
     test "remove file with empty shortcode", %{admin_conn: admin_conn} do
       assert admin_conn
-             |> delete("/api/pleroma/emoji/packs/not_loaded/files", %{shortcode: ""})
-             |> json_response(:bad_request) == %{
+             |> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=")
+             |> json_response_and_validate_schema(:bad_request) == %{
                "error" => "pack name or shortcode cannot be empty"
              }
     end
 
     test "update file with not loaded pack", %{admin_conn: admin_conn} do
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> patch("/api/pleroma/emoji/packs/not_loaded/files", %{
                shortcode: "blank4",
                new_shortcode: "blank3",
                new_filename: "dir_2/blank_3.png"
              })
-             |> json_response(:bad_request) == %{"error" => "pack \"not_loaded\" is not found"}
+             |> json_response_and_validate_schema(:bad_request) == %{
+               "error" => "pack \"not_loaded\" is not found"
+             }
     end
 
     test "new with shortcode as file with update", %{admin_conn: admin_conn} do
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> post("/api/pleroma/emoji/packs/test_pack/files", %{
                shortcode: "blank4",
                filename: "dir/blank.png",
@@ -512,24 +537,31 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
                  path: "#{@emoji_path}/test_pack/blank.png"
                }
              })
-             |> json_response(200) == %{"blank" => "blank.png", "blank4" => "dir/blank.png"}
+             |> json_response_and_validate_schema(200) == %{
+               "blank" => "blank.png",
+               "blank4" => "dir/blank.png"
+             }
 
       assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png")
 
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> patch("/api/pleroma/emoji/packs/test_pack/files", %{
                shortcode: "blank4",
                new_shortcode: "blank3",
                new_filename: "dir_2/blank_3.png"
              })
-             |> json_response(200) == %{"blank3" => "dir_2/blank_3.png", "blank" => "blank.png"}
+             |> json_response_and_validate_schema(200) == %{
+               "blank3" => "dir_2/blank_3.png",
+               "blank" => "blank.png"
+             }
 
       refute File.exists?("#{@emoji_path}/test_pack/dir/")
       assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png")
 
       assert admin_conn
-             |> delete("/api/pleroma/emoji/packs/test_pack/files", %{shortcode: "blank3"})
-             |> json_response(200) == %{"blank" => "blank.png"}
+             |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3")
+             |> json_response_and_validate_schema(200) == %{"blank" => "blank.png"}
 
       refute File.exists?("#{@emoji_path}/test_pack/dir_2/")
 
@@ -546,11 +578,12 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       end)
 
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> post("/api/pleroma/emoji/packs/test_pack/files", %{
                shortcode: "blank_url",
                file: "https://test-blank/blank_url.png"
              })
-             |> json_response(200) == %{
+             |> json_response_and_validate_schema(200) == %{
                "blank_url" => "blank_url.png",
                "blank" => "blank.png"
              }
@@ -564,40 +597,51 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/shortcode.png") end)
 
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> post("/api/pleroma/emoji/packs/test_pack/files", %{
                file: %Plug.Upload{
                  filename: "shortcode.png",
                  path: "#{Pleroma.Config.get([:instance, :static_dir])}/add/shortcode.png"
                }
              })
-             |> json_response(200) == %{"shortcode" => "shortcode.png", "blank" => "blank.png"}
+             |> json_response_and_validate_schema(200) == %{
+               "shortcode" => "shortcode.png",
+               "blank" => "blank.png"
+             }
     end
 
     test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do
       assert admin_conn
-             |> delete("/api/pleroma/emoji/packs/test_pack/files", %{shortcode: "blank2"})
-             |> json_response(:bad_request) == %{"error" => "Emoji \"blank2\" does not exist"}
+             |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank2")
+             |> json_response_and_validate_schema(:bad_request) == %{
+               "error" => "Emoji \"blank2\" does not exist"
+             }
     end
 
     test "update non existing emoji", %{admin_conn: admin_conn} do
       assert admin_conn
+             |> put_req_header("content-type", "multipart/form-data")
              |> patch("/api/pleroma/emoji/packs/test_pack/files", %{
                shortcode: "blank2",
                new_shortcode: "blank3",
                new_filename: "dir_2/blank_3.png"
              })
-             |> json_response(:bad_request) == %{"error" => "Emoji \"blank2\" does not exist"}
+             |> json_response_and_validate_schema(:bad_request) == %{
+               "error" => "Emoji \"blank2\" does not exist"
+             }
     end
 
     test "update with empty shortcode", %{admin_conn: admin_conn} do
-      assert admin_conn
-             |> patch("/api/pleroma/emoji/packs/test_pack/files", %{
-               shortcode: "blank",
-               new_filename: "dir_2/blank_3.png"
-             })
-             |> json_response(:bad_request) == %{
-               "error" => "new_shortcode or new_filename cannot be empty"
-             }
+      assert %{
+               "error" => "Missing field: new_shortcode."
+             } =
+               admin_conn
+               |> put_req_header("content-type", "multipart/form-data")
+               |> patch("/api/pleroma/emoji/packs/test_pack/files", %{
+                 shortcode: "blank",
+                 new_filename: "dir_2/blank_3.png"
+               })
+               |> json_response_and_validate_schema(:bad_request)
     end
   end
 
@@ -605,7 +649,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
     test "creating and deleting a pack", %{admin_conn: admin_conn} do
       assert admin_conn
              |> post("/api/pleroma/emoji/packs/test_created")
-             |> json_response(200) == "ok"
+             |> json_response_and_validate_schema(200) == "ok"
 
       assert File.exists?("#{@emoji_path}/test_created/pack.json")
 
@@ -616,7 +660,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
 
       assert admin_conn
              |> delete("/api/pleroma/emoji/packs/test_created")
-             |> json_response(200) == "ok"
+             |> json_response_and_validate_schema(200) == "ok"
 
       refute File.exists?("#{@emoji_path}/test_created/pack.json")
     end
@@ -629,7 +673,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
 
       assert admin_conn
              |> post("/api/pleroma/emoji/packs/test_created")
-             |> json_response(:conflict) == %{
+             |> json_response_and_validate_schema(:conflict) == %{
                "error" => "A pack named \"test_created\" already exists"
              }
 
@@ -639,20 +683,26 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
     test "with empty name", %{admin_conn: admin_conn} do
       assert admin_conn
              |> post("/api/pleroma/emoji/packs/ ")
-             |> json_response(:bad_request) == %{"error" => "pack name cannot be empty"}
+             |> json_response_and_validate_schema(:bad_request) == %{
+               "error" => "pack name cannot be empty"
+             }
     end
   end
 
   test "deleting nonexisting pack", %{admin_conn: admin_conn} do
     assert admin_conn
            |> delete("/api/pleroma/emoji/packs/non_existing")
-           |> json_response(:not_found) == %{"error" => "Pack non_existing does not exist"}
+           |> json_response_and_validate_schema(:not_found) == %{
+             "error" => "Pack non_existing does not exist"
+           }
   end
 
   test "deleting with empty name", %{admin_conn: admin_conn} do
     assert admin_conn
            |> delete("/api/pleroma/emoji/packs/ ")
-           |> json_response(:bad_request) == %{"error" => "pack name cannot be empty"}
+           |> json_response_and_validate_schema(:bad_request) == %{
+             "error" => "pack name cannot be empty"
+           }
   end
 
   test "filesystem import", %{admin_conn: admin_conn, conn: conn} do
@@ -661,15 +711,15 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       File.rm!("#{@emoji_path}/test_pack_for_import/pack.json")
     end)
 
-    resp = conn |> get("/api/pleroma/emoji/packs") |> json_response(200)
+    resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
 
     refute Map.has_key?(resp, "test_pack_for_import")
 
     assert admin_conn
            |> get("/api/pleroma/emoji/packs/import")
-           |> json_response(200) == ["test_pack_for_import"]
+           |> json_response_and_validate_schema(200) == ["test_pack_for_import"]
 
-    resp = conn |> get("/api/pleroma/emoji/packs") |> json_response(200)
+    resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
     assert resp["test_pack_for_import"]["files"] == %{"blank" => "blank.png"}
 
     File.rm!("#{@emoji_path}/test_pack_for_import/pack.json")
@@ -686,9 +736,9 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
 
     assert admin_conn
            |> get("/api/pleroma/emoji/packs/import")
-           |> json_response(200) == ["test_pack_for_import"]
+           |> json_response_and_validate_schema(200) == ["test_pack_for_import"]
 
-    resp = conn |> get("/api/pleroma/emoji/packs") |> json_response(200)
+    resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
 
     assert resp["test_pack_for_import"]["files"] == %{
              "blank" => "blank.png",
@@ -712,19 +762,23 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
              } =
                conn
                |> get("/api/pleroma/emoji/packs/test_pack")
-               |> json_response(200)
+               |> json_response_and_validate_schema(200)
     end
 
     test "non existing pack", %{conn: conn} do
       assert conn
              |> get("/api/pleroma/emoji/packs/non_existing")
-             |> json_response(:not_found) == %{"error" => "Pack non_existing does not exist"}
+             |> json_response_and_validate_schema(:not_found) == %{
+               "error" => "Pack non_existing does not exist"
+             }
     end
 
     test "error name", %{conn: conn} do
       assert conn
              |> get("/api/pleroma/emoji/packs/ ")
-             |> json_response(:bad_request) == %{"error" => "pack name cannot be empty"}
+             |> json_response_and_validate_schema(:bad_request) == %{
+               "error" => "pack name cannot be empty"
+             }
     end
   end
 end