diff options
| -rw-r--r-- | lib/pleroma/web/api_spec/operations/account_operation.ex | 19 | ||||
| -rw-r--r-- | lib/pleroma/web/api_spec/operations/list_operation.ex | 188 | ||||
| -rw-r--r-- | lib/pleroma/web/api_spec/schemas/list.ex | 23 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/controllers/list_controller.ex | 26 | ||||
| -rw-r--r-- | test/support/conn_case.ex | 2 | ||||
| -rw-r--r-- | test/web/mastodon_api/controllers/list_controller_test.exs | 60 | 
6 files changed, 265 insertions, 53 deletions
diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index fe9548b1b..470fc0215 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do    alias Pleroma.Web.ApiSpec.Schemas.ActorType    alias Pleroma.Web.ApiSpec.Schemas.ApiError    alias Pleroma.Web.ApiSpec.Schemas.BooleanLike +  alias Pleroma.Web.ApiSpec.Schemas.List    alias Pleroma.Web.ApiSpec.Schemas.Status    alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope @@ -646,28 +647,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do      }    end -  defp list do -    %Schema{ -      title: "List", -      description: "Response schema for a list", -      type: :object, -      properties: %{ -        id: %Schema{type: :string}, -        title: %Schema{type: :string} -      }, -      example: %{ -        "id" => "123", -        "title" => "my list" -      } -    } -  end -    defp array_of_lists do      %Schema{        title: "ArrayOfLists",        description: "Response schema for lists",        type: :array, -      items: list(), +      items: List,        example: [          %{"id" => "123", "title" => "my list"},          %{"id" => "1337", "title" => "anotehr list"} diff --git a/lib/pleroma/web/api_spec/operations/list_operation.ex b/lib/pleroma/web/api_spec/operations/list_operation.ex new file mode 100644 index 000000000..c88ed5dd0 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/list_operation.ex @@ -0,0 +1,188 @@ +# 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.ListOperation do +  alias OpenApiSpex.Operation +  alias OpenApiSpex.Schema +  alias Pleroma.Web.ApiSpec.Schemas.Account +  alias Pleroma.Web.ApiSpec.Schemas.ApiError +  alias Pleroma.Web.ApiSpec.Schemas.FlakeID +  alias Pleroma.Web.ApiSpec.Schemas.List + +  import Pleroma.Web.ApiSpec.Helpers + +  def open_api_operation(action) do +    operation = String.to_existing_atom("#{action}_operation") +    apply(__MODULE__, operation, []) +  end + +  def index_operation do +    %Operation{ +      tags: ["Lists"], +      summary: "Show user's lists", +      description: "Fetch all lists that the user owns", +      security: [%{"oAuth" => ["read:lists"]}], +      operationId: "ListController.index", +      responses: %{ +        200 => Operation.response("Array of List", "application/json", array_of_lists()) +      } +    } +  end + +  def create_operation do +    %Operation{ +      tags: ["Lists"], +      summary: "Create  a list", +      description: "Fetch the list with the given ID. Used for verifying the title of a list.", +      operationId: "ListController.create", +      requestBody: create_update_request(), +      security: [%{"oAuth" => ["write:lists"]}], +      responses: %{ +        200 => Operation.response("List", "application/json", List), +        400 => Operation.response("Error", "application/json", ApiError), +        404 => Operation.response("Error", "application/json", ApiError) +      } +    } +  end + +  def show_operation do +    %Operation{ +      tags: ["Lists"], +      summary: "Show a single list", +      description: "Fetch the list with the given ID. Used for verifying the title of a list.", +      operationId: "ListController.show", +      parameters: [id_param()], +      security: [%{"oAuth" => ["read:lists"]}], +      responses: %{ +        200 => Operation.response("List", "application/json", List), +        404 => Operation.response("Error", "application/json", ApiError) +      } +    } +  end + +  def update_operation do +    %Operation{ +      tags: ["Lists"], +      summary: "Update a list", +      description: "Change the title of a list", +      operationId: "ListController.update", +      parameters: [id_param()], +      requestBody: create_update_request(), +      security: [%{"oAuth" => ["write:lists"]}], +      responses: %{ +        200 => Operation.response("List", "application/json", List), +        422 => Operation.response("Error", "application/json", ApiError) +      } +    } +  end + +  def delete_operation do +    %Operation{ +      tags: ["Lists"], +      summary: "Delete a list", +      operationId: "ListController.delete", +      parameters: [id_param()], +      security: [%{"oAuth" => ["write:lists"]}], +      responses: %{ +        200 => Operation.response("Empty object", "application/json", %Schema{type: :object}) +      } +    } +  end + +  def list_accounts_operation do +    %Operation{ +      tags: ["Lists"], +      summary: "View accounts in list", +      operationId: "ListController.list_accounts", +      parameters: [id_param()], +      security: [%{"oAuth" => ["read:lists"]}], +      responses: %{ +        200 => +          Operation.response("Array of Account", "application/json", %Schema{ +            type: :array, +            items: Account +          }) +      } +    } +  end + +  def add_to_list_operation do +    %Operation{ +      tags: ["Lists"], +      summary: "Add accounts to list", +      description: "Add accounts to the given list.", +      operationId: "ListController.add_to_list", +      parameters: [id_param()], +      requestBody: add_remove_accounts_request(), +      security: [%{"oAuth" => ["write:lists"]}], +      responses: %{ +        200 => Operation.response("Empty object", "application/json", %Schema{type: :object}) +      } +    } +  end + +  def remove_from_list_operation do +    %Operation{ +      tags: ["Lists"], +      summary: "Remove accounts from list", +      operationId: "ListController.remove_from_list", +      parameters: [id_param()], +      requestBody: add_remove_accounts_request(), +      security: [%{"oAuth" => ["write:lists"]}], +      responses: %{ +        200 => Operation.response("Empty object", "application/json", %Schema{type: :object}) +      } +    } +  end + +  defp array_of_lists do +    %Schema{ +      title: "ArrayOfLists", +      description: "Response schema for lists", +      type: :array, +      items: List, +      example: [ +        %{"id" => "123", "title" => "my list"}, +        %{"id" => "1337", "title" => "another list"} +      ] +    } +  end + +  defp id_param do +    Operation.parameter(:id, :path, :string, "List ID", +      example: "123", +      required: true +    ) +  end + +  defp create_update_request do +    request_body( +      "Parameters", +      %Schema{ +        description: "POST body for creating or updating a List", +        type: :object, +        properties: %{ +          title: %Schema{type: :string, description: "List title"} +        }, +        required: [:title] +      }, +      required: true +    ) +  end + +  defp add_remove_accounts_request do +    request_body( +      "Parameters", +      %Schema{ +        description: "POST body for adding/removing accounts to/from a List", +        type: :object, +        properties: %{ +          account_ids: %Schema{type: :array, description: "Array of account IDs", items: FlakeID} +        }, +        required: [:account_ids] +      }, +      required: true +    ) +  end +end diff --git a/lib/pleroma/web/api_spec/schemas/list.ex b/lib/pleroma/web/api_spec/schemas/list.ex new file mode 100644 index 000000000..b7d1685c9 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/list.ex @@ -0,0 +1,23 @@ +# 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.Schemas.List do +  alias OpenApiSpex.Schema + +  require OpenApiSpex + +  OpenApiSpex.schema(%{ +    title: "List", +    description: "Represents a list of users", +    type: :object, +    properties: %{ +      id: %Schema{type: :string, description: "The internal database ID of the list"}, +      title: %Schema{type: :string, description: "The user-defined title of the list"} +    }, +    example: %{ +      "id" => "12249", +      "title" => "Friends" +    } +  }) +end diff --git a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex index bfe856025..acdc76fd2 100644 --- a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex @@ -9,20 +9,17 @@ defmodule Pleroma.Web.MastodonAPI.ListController do    alias Pleroma.User    alias Pleroma.Web.MastodonAPI.AccountView -  plug(:list_by_id_and_user when action not in [:index, :create]) -    @oauth_read_actions [:index, :show, :list_accounts] +  plug(Pleroma.Web.ApiSpec.CastAndValidate) +  plug(:list_by_id_and_user when action not in [:index, :create])    plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action in @oauth_read_actions) - -  plug( -    OAuthScopesPlug, -    %{scopes: ["write:lists"]} -    when action not in @oauth_read_actions -  ) +  plug(OAuthScopesPlug, %{scopes: ["write:lists"]} when action not in @oauth_read_actions)    action_fallback(Pleroma.Web.MastodonAPI.FallbackController) +  defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ListOperation +    # GET /api/v1/lists    def index(%{assigns: %{user: user}} = conn, opts) do      lists = Pleroma.List.for_user(user, opts) @@ -30,7 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.ListController do    end    # POST /api/v1/lists -  def create(%{assigns: %{user: user}} = conn, %{"title" => title}) do +  def create(%{assigns: %{user: user}, body_params: %{title: title}} = conn, _) do      with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(title, user) do        render(conn, "show.json", list: list)      end @@ -42,7 +39,7 @@ defmodule Pleroma.Web.MastodonAPI.ListController do    end    # PUT /api/v1/lists/:id -  def update(%{assigns: %{list: list}} = conn, %{"title" => title}) do +  def update(%{assigns: %{list: list}, body_params: %{title: title}} = conn, _) do      with {:ok, list} <- Pleroma.List.rename(list, title) do        render(conn, "show.json", list: list)      end @@ -65,7 +62,7 @@ defmodule Pleroma.Web.MastodonAPI.ListController do    end    # POST /api/v1/lists/:id/accounts -  def add_to_list(%{assigns: %{list: list}} = conn, %{"account_ids" => account_ids}) do +  def add_to_list(%{assigns: %{list: list}, body_params: %{account_ids: account_ids}} = conn, _) do      Enum.each(account_ids, fn account_id ->        with %User{} = followed <- User.get_cached_by_id(account_id) do          Pleroma.List.follow(list, followed) @@ -76,7 +73,10 @@ defmodule Pleroma.Web.MastodonAPI.ListController do    end    # DELETE /api/v1/lists/:id/accounts -  def remove_from_list(%{assigns: %{list: list}} = conn, %{"account_ids" => account_ids}) do +  def remove_from_list( +        %{assigns: %{list: list}, body_params: %{account_ids: account_ids}} = conn, +        _ +      ) do      Enum.each(account_ids, fn account_id ->        with %User{} = followed <- User.get_cached_by_id(account_id) do          Pleroma.List.unfollow(list, followed) @@ -86,7 +86,7 @@ defmodule Pleroma.Web.MastodonAPI.ListController do      json(conn, %{})    end -  defp list_by_id_and_user(%{assigns: %{user: user}, params: %{"id" => id}} = conn, _) do +  defp list_by_id_and_user(%{assigns: %{user: user}, params: %{id: id}} = conn, _) do      case Pleroma.List.get(id, user) do        %Pleroma.List{} = list -> assign(conn, :list, list)        nil -> conn |> render_error(:not_found, "List not found") |> halt() diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index fa30a0c41..91c03b1a8 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -74,7 +74,7 @@ defmodule Pleroma.Web.ConnCase do          status = Plug.Conn.Status.code(status)          unless lookup[op_id].responses[status] do -          err = "Response schema not found for #{conn.status} #{conn.method} #{conn.request_path}" +          err = "Response schema not found for #{status} #{conn.method} #{conn.request_path}"            flunk(err)          end diff --git a/test/web/mastodon_api/controllers/list_controller_test.exs b/test/web/mastodon_api/controllers/list_controller_test.exs index c9c4cbb49..57a9ef4a4 100644 --- a/test/web/mastodon_api/controllers/list_controller_test.exs +++ b/test/web/mastodon_api/controllers/list_controller_test.exs @@ -12,37 +12,44 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do    test "creating a list" do      %{conn: conn} = oauth_access(["write:lists"]) -    conn = post(conn, "/api/v1/lists", %{"title" => "cuties"}) - -    assert %{"title" => title} = json_response(conn, 200) -    assert title == "cuties" +    assert %{"title" => "cuties"} = +             conn +             |> put_req_header("content-type", "application/json") +             |> post("/api/v1/lists", %{"title" => "cuties"}) +             |> json_response_and_validate_schema(:ok)    end    test "renders error for invalid params" do      %{conn: conn} = oauth_access(["write:lists"]) -    conn = post(conn, "/api/v1/lists", %{"title" => nil}) +    conn = +      conn +      |> put_req_header("content-type", "application/json") +      |> post("/api/v1/lists", %{"title" => nil}) -    assert %{"error" => "can't be blank"} == json_response(conn, :unprocessable_entity) +    assert %{"error" => "title - null value where string expected."} = +             json_response_and_validate_schema(conn, 400)    end    test "listing a user's lists" do      %{conn: conn} = oauth_access(["read:lists", "write:lists"])      conn +    |> put_req_header("content-type", "application/json")      |> post("/api/v1/lists", %{"title" => "cuties"}) -    |> json_response(:ok) +    |> json_response_and_validate_schema(:ok)      conn +    |> put_req_header("content-type", "application/json")      |> post("/api/v1/lists", %{"title" => "cofe"}) -    |> json_response(:ok) +    |> json_response_and_validate_schema(:ok)      conn = get(conn, "/api/v1/lists")      assert [               %{"id" => _, "title" => "cofe"},               %{"id" => _, "title" => "cuties"} -           ] = json_response(conn, :ok) +           ] = json_response_and_validate_schema(conn, :ok)    end    test "adding users to a list" do @@ -50,9 +57,12 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do      other_user = insert(:user)      {:ok, list} = Pleroma.List.create("name", user) -    conn = post(conn, "/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) +    assert %{} == +             conn +             |> put_req_header("content-type", "application/json") +             |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) +             |> json_response_and_validate_schema(:ok) -    assert %{} == json_response(conn, 200)      %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)      assert following == [other_user.follower_address]    end @@ -65,9 +75,12 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do      {:ok, list} = Pleroma.List.follow(list, other_user)      {:ok, list} = Pleroma.List.follow(list, third_user) -    conn = delete(conn, "/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) +    assert %{} == +             conn +             |> put_req_header("content-type", "application/json") +             |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) +             |> json_response_and_validate_schema(:ok) -    assert %{} == json_response(conn, 200)      %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)      assert following == [third_user.follower_address]    end @@ -83,7 +96,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do        |> assign(:user, user)        |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) -    assert [%{"id" => id}] = json_response(conn, 200) +    assert [%{"id" => id}] = json_response_and_validate_schema(conn, 200)      assert id == to_string(other_user.id)    end @@ -96,7 +109,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do        |> assign(:user, user)        |> get("/api/v1/lists/#{list.id}") -    assert %{"id" => id} = json_response(conn, 200) +    assert %{"id" => id} = json_response_and_validate_schema(conn, 200)      assert id == to_string(list.id)    end @@ -105,17 +118,18 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do      conn = get(conn, "/api/v1/lists/666") -    assert %{"error" => "List not found"} = json_response(conn, :not_found) +    assert %{"error" => "List not found"} = json_response_and_validate_schema(conn, :not_found)    end    test "renaming a list" do      %{user: user, conn: conn} = oauth_access(["write:lists"])      {:ok, list} = Pleroma.List.create("name", user) -    conn = put(conn, "/api/v1/lists/#{list.id}", %{"title" => "newname"}) - -    assert %{"title" => name} = json_response(conn, 200) -    assert name == "newname" +    assert %{"title" => "newname"} = +             conn +             |> put_req_header("content-type", "application/json") +             |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"}) +             |> json_response_and_validate_schema(:ok)    end    test "validates title when renaming a list" do @@ -125,9 +139,11 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do      conn =        conn        |> assign(:user, user) +      |> put_req_header("content-type", "application/json")        |> put("/api/v1/lists/#{list.id}", %{"title" => "  "}) -    assert %{"error" => "can't be blank"} == json_response(conn, :unprocessable_entity) +    assert %{"error" => "can't be blank"} == +             json_response_and_validate_schema(conn, :unprocessable_entity)    end    test "deleting a list" do @@ -136,7 +152,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do      conn = delete(conn, "/api/v1/lists/#{list.id}") -    assert %{} = json_response(conn, 200) +    assert %{} = json_response_and_validate_schema(conn, 200)      assert is_nil(Repo.get(Pleroma.List, list.id))    end  end  | 
