Merge branch 'v2-suggestions' into 'develop'
authorAlex Gleason <alex@alexgleason.me>
Sun, 19 Dec 2021 17:31:17 +0000 (17:31 +0000)
committerAlex Gleason <alex@alexgleason.me>
Sun, 19 Dec 2021 17:31:17 +0000 (17:31 +0000)
V2 suggestions

See merge request pleroma/pleroma!3547

20 files changed:
docs/development/API/admin_api.md
lib/pleroma/ecto_enums.ex
lib/pleroma/moderation_log.ex
lib/pleroma/user.ex
lib/pleroma/user/query.ex
lib/pleroma/web/admin_api/controllers/user_controller.ex
lib/pleroma/web/admin_api/views/account_view.ex
lib/pleroma/web/api_spec/operations/admin/user_operation.ex
lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex
lib/pleroma/web/mastodon_api/views/account_view.ex
lib/pleroma/web/mastodon_api/views/instance_view.ex
lib/pleroma/web/mastodon_api/views/suggestion_view.ex [new file with mode: 0644]
lib/pleroma/web/router.ex
priv/repo/migrations/20211126191138_add_suggestions.exs [new file with mode: 0644]
test/pleroma/user/query_test.exs
test/pleroma/user_test.exs
test/pleroma/web/admin_api/controllers/user_controller_test.exs
test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs
test/pleroma/web/mastodon_api/views/account_view_test.exs
test/pleroma/web/mastodon_api/views/suggestion_view_test.exs [new file with mode: 0644]

index 82483fae71deaf9f82c8ff6afb3ef2f913183083..f140818934a05fe41761a9f65ce92d4765357e18 100644 (file)
@@ -261,6 +261,46 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
 }
 ```
 
+## `PATCH /api/v1/pleroma/admin/users/suggest`
+
+### Suggest a user
+
+Adds the user(s) to follower recommendations.
+
+- Params:
+  - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+  users: [
+    {
+      // user object
+    }
+  ]
+}
+```
+
+## `PATCH /api/v1/pleroma/admin/users/unsuggest`
+
+### Unsuggest a user
+
+Removes the user(s) from follower recommendations.
+
+- Params:
+  - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+  users: [
+    {
+      // user object
+    }
+  ]
+}
+```
+
 ## `GET /api/v1/pleroma/admin/users/:nickname_or_id`
 
 ### Retrive the details of a user
index 2a9addabcecff4553bff21c483e9db83c8beb270..0e3e1e5deaee1762268239d5615699b8fa7a6199 100644 (file)
@@ -9,7 +9,8 @@ defenum(Pleroma.UserRelationship.Type,
   mute: 2,
   reblog_mute: 3,
   notification_mute: 4,
-  inverse_subscription: 5
+  inverse_subscription: 5,
+  suggestion_dismiss: 6
 )
 
 defenum(Pleroma.FollowingRelationship.State,
index 993cff09b26cf3c9c6f819a16c1ee74d2fab2c90..adb51d33a17436ad315d42c5ecec88c1238783ee 100644 (file)
@@ -338,6 +338,26 @@ defmodule Pleroma.ModerationLog do
     "@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}"
   end
 
+  def get_log_entry_message(%ModerationLog{
+        data: %{
+          "actor" => %{"nickname" => actor_nickname},
+          "action" => "add_suggestion",
+          "subject" => users
+        }
+      }) do
+    "@#{actor_nickname} added suggested users: #{users_to_nicknames_string(users)}"
+  end
+
+  def get_log_entry_message(%ModerationLog{
+        data: %{
+          "actor" => %{"nickname" => actor_nickname},
+          "action" => "remove_suggestion",
+          "subject" => users
+        }
+      }) do
+    "@#{actor_nickname} removed suggested users: #{users_to_nicknames_string(users)}"
+  end
+
   def get_log_entry_message(%ModerationLog{
         data: %{
           "actor" => %{"nickname" => actor_nickname},
index 8e40dfc0d846b55ee67dfcc6b8c482060a99a6a7..c25023dc12b1c1b8b9b20e1b3b95fae17ec4aa28 100644 (file)
@@ -148,6 +148,7 @@ defmodule Pleroma.User do
     field(:last_active_at, :naive_datetime)
     field(:disclose_client, :boolean, default: true)
     field(:pinned_objects, :map, default: %{})
+    field(:is_suggested, :boolean, default: false)
 
     embeds_one(
       :notification_settings,
@@ -1676,6 +1677,22 @@ defmodule Pleroma.User do
 
   def confirm(%User{} = user), do: {:ok, user}
 
+  def set_suggestion(users, is_suggested) when is_list(users) do
+    Repo.transaction(fn ->
+      Enum.map(users, fn user ->
+        with {:ok, user} <- set_suggestion(user, is_suggested), do: user
+      end)
+    end)
+  end
+
+  def set_suggestion(%User{is_suggested: is_suggested} = user, is_suggested), do: {:ok, user}
+
+  def set_suggestion(%User{} = user, is_suggested) when is_boolean(is_suggested) do
+    user
+    |> change(is_suggested: is_suggested)
+    |> update_and_set_cache()
+  end
+
   def update_notification_settings(%User{} = user, settings) do
     user
     |> cast(%{notification_settings: settings}, [])
index ac807fc7927f5340cf37c9fbc83329b222e987c7..6d4a4ead6c480797ebcc2bed1efdf94389ed99fc 100644 (file)
@@ -46,6 +46,7 @@ defmodule Pleroma.User.Query do
             unconfirmed: boolean(),
             is_admin: boolean(),
             is_moderator: boolean(),
+            is_suggested: boolean(),
             super_users: boolean(),
             invisible: boolean(),
             internal: boolean(),
@@ -167,6 +168,10 @@ defmodule Pleroma.User.Query do
     where(query, [u], u.is_confirmed == false)
   end
 
+  defp compose_query({:is_suggested, bool}, query) do
+    where(query, [u], u.is_suggested == ^bool)
+  end
+
   defp compose_query({:followers, %User{id: id}}, query) do
     query
     |> where([u], u.id != ^id)
index 637a0e702d37003800144b4767d91de8a7a99d22..50208a8b76a1b4ac634632a29d69f58f0326c80f 100644 (file)
@@ -35,7 +35,9 @@ defmodule Pleroma.Web.AdminAPI.UserController do
            :toggle_activation,
            :activate,
            :deactivate,
-           :approve
+           :approve,
+           :suggest,
+           :unsuggest
          ]
   )
 
@@ -239,6 +241,32 @@ defmodule Pleroma.Web.AdminAPI.UserController do
     render(conn, "index.json", users: updated_users)
   end
 
+  def suggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+    {:ok, updated_users} = User.set_suggestion(users, true)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "add_suggestion"
+    })
+
+    render(conn, "index.json", users: updated_users)
+  end
+
+  def unsuggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+    {:ok, updated_users} = User.set_suggestion(users, false)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "remove_suggestion"
+    })
+
+    render(conn, "index.json", users: updated_users)
+  end
+
   def index(conn, params) do
     {page, page_size} = page_params(params)
     filters = maybe_parse_filters(params[:filters])
index fae0c07f009dfca23af1779d5ca0227179548502..2f1f7e627e7b078a9f5b8e1478398ec88ba7e9d2 100644 (file)
@@ -80,6 +80,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
       "tags" => user.tags || [],
       "is_confirmed" => user.is_confirmed,
       "is_approved" => user.is_approved,
+      "is_suggested" => user.is_suggested,
       "url" => user.uri || user.ap_id,
       "registration_reason" => user.registration_reason,
       "actor_type" => user.actor_type,
index c9d0bfd7c1d0f00d6ad42803c093fe7b0f8867a7..57fb1ad65e8c101af4700cfbafd0017a1c6efaab 100644 (file)
@@ -216,7 +216,71 @@ defmodule Pleroma.Web.ApiSpec.Admin.UserOperation do
         request_body(
           "Parameters",
           %Schema{
-            description: "POST body for deleting multiple users",
+            description: "POST body for approving multiple users",
+            type: :object,
+            properties: %{
+              nicknames: %Schema{
+                type: :array,
+                items: %Schema{type: :string}
+              }
+            }
+          }
+        ),
+      responses: %{
+        200 =>
+          Operation.response("Response", "application/json", %Schema{
+            type: :object,
+            properties: %{user: %Schema{type: :array, items: user()}}
+          }),
+        403 => Operation.response("Forbidden", "application/json", ApiError)
+      }
+    }
+  end
+
+  def suggest_operation do
+    %Operation{
+      tags: ["User administration"],
+      summary: "Suggest multiple users",
+      operationId: "AdminAPI.UserController.suggest",
+      security: [%{"oAuth" => ["admin:write:accounts"]}],
+      parameters: admin_api_params(),
+      requestBody:
+        request_body(
+          "Parameters",
+          %Schema{
+            description: "POST body for adding multiple suggested users",
+            type: :object,
+            properties: %{
+              nicknames: %Schema{
+                type: :array,
+                items: %Schema{type: :string}
+              }
+            }
+          }
+        ),
+      responses: %{
+        200 =>
+          Operation.response("Response", "application/json", %Schema{
+            type: :object,
+            properties: %{user: %Schema{type: :array, items: user()}}
+          }),
+        403 => Operation.response("Forbidden", "application/json", ApiError)
+      }
+    }
+  end
+
+  def unsuggest_operation do
+    %Operation{
+      tags: ["User administration"],
+      summary: "Unsuggest multiple users",
+      operationId: "AdminAPI.UserController.unsuggest",
+      security: [%{"oAuth" => ["admin:write:accounts"]}],
+      parameters: admin_api_params(),
+      requestBody:
+        request_body(
+          "Parameters",
+          %Schema{
+            description: "POST body for removing multiple suggested users",
             type: :object,
             properties: %{
               nicknames: %Schema{
index 01e122dd9020dfbbae6ed9d3ec05cadd1237bc83..e913fcf4bbb5c66f8e850ca81fb832e57e3f9bd6 100644 (file)
@@ -4,11 +4,16 @@
 
 defmodule Pleroma.Web.MastodonAPI.SuggestionController do
   use Pleroma.Web, :controller
+  import Ecto.Query
+  alias Pleroma.FollowingRelationship
+  alias Pleroma.User
+  alias Pleroma.UserRelationship
 
   require Logger
 
   plug(Pleroma.Web.ApiSpec.CastAndValidate)
-  plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action == :index)
+  plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action in [:index, :index2])
+  plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["write"]} when action in [:dismiss])
 
   def open_api_operation(action) do
     operation = String.to_existing_atom("#{action}_operation")
@@ -26,7 +31,90 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do
     }
   end
 
+  def index2_operation do
+    %OpenApiSpex.Operation{
+      tags: ["Suggestions"],
+      summary: "Follow suggestions",
+      operationId: "SuggestionController.index2",
+      responses: %{
+        200 => Pleroma.Web.ApiSpec.Helpers.empty_array_response()
+      }
+    }
+  end
+
+  def dismiss_operation do
+    %OpenApiSpex.Operation{
+      tags: ["Suggestions"],
+      summary: "Remove a suggestion",
+      operationId: "SuggestionController.dismiss",
+      parameters: [
+        OpenApiSpex.Operation.parameter(
+          :account_id,
+          :path,
+          %OpenApiSpex.Schema{type: :string},
+          "Account to dismiss",
+          required: true
+        )
+      ],
+      responses: %{
+        200 => Pleroma.Web.ApiSpec.Helpers.empty_object_response()
+      }
+    }
+  end
+
   @doc "GET /api/v1/suggestions"
   def index(conn, params),
     do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
+
+  @doc "GET /api/v2/suggestions"
+  def index2(%{assigns: %{user: user}} = conn, params) do
+    limit = Map.get(params, :limit, 40) |> min(80)
+
+    users =
+      %{is_suggested: true, invisible: false, limit: limit}
+      |> User.Query.build()
+      |> exclude_user(user)
+      |> exclude_relationships(user, [:block, :mute, :suggestion_dismiss])
+      |> exclude_following(user)
+      |> Pleroma.Repo.all()
+
+    render(conn, "index.json", %{
+      users: users,
+      source: :staff,
+      for: user,
+      skip_visibility_check: true
+    })
+  end
+
+  defp exclude_user(query, %User{id: user_id}) do
+    where(query, [u], u.id != ^user_id)
+  end
+
+  defp exclude_relationships(query, %User{id: user_id}, relationship_types) do
+    query
+    |> join(:left, [u], r in UserRelationship,
+      as: :user_relationships,
+      on:
+        r.target_id == u.id and r.source_id == ^user_id and
+          r.relationship_type in ^relationship_types
+    )
+    |> where([user_relationships: r], is_nil(r.target_id))
+  end
+
+  defp exclude_following(query, %User{id: user_id}) do
+    query
+    |> join(:left, [u], r in FollowingRelationship,
+      as: :following_relationships,
+      on: r.following_id == u.id and r.follower_id == ^user_id and r.state == :follow_accept
+    )
+    |> where([following_relationships: r], is_nil(r.following_id))
+  end
+
+  @doc "DELETE /api/v1/suggestions/:account_id"
+  def dismiss(%{assigns: %{user: source}} = conn, %{account_id: user_id}) do
+    with %User{} = target <- User.get_cached_by_id(user_id),
+         {:ok, _} <- UserRelationship.create(:suggestion_dismiss, source, target) do
+      json(conn, %{})
+    end
+  end
 end
index 9e9de33f6167c615158440da34d0f6b3dd877a98..6114e12b14405db25a879c2a14fe2e233e98d7c5 100644 (file)
@@ -269,6 +269,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
         ap_id: user.ap_id,
         also_known_as: user.also_known_as,
         is_confirmed: user.is_confirmed,
+        is_suggested: user.is_suggested,
         tags: user.tags,
         hide_followers_count: user.hide_followers_count,
         hide_follows_count: user.hide_follows_count,
index ef208062bdb121f12ecd77be6c5d5d38979d4587..5810f605a3bf60f9ce8ab8c8d69f12c056bd7910 100644 (file)
@@ -59,6 +59,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
       "mastodon_api",
       "mastodon_api_streaming",
       "polls",
+      "v2_suggestions",
       "pleroma_explicit_addressing",
       "shareable_emoji_packs",
       "multifetch",
diff --git a/lib/pleroma/web/mastodon_api/views/suggestion_view.ex b/lib/pleroma/web/mastodon_api/views/suggestion_view.ex
new file mode 100644 (file)
index 0000000..865229a
--- /dev/null
@@ -0,0 +1,28 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.SuggestionView do
+  use Pleroma.Web, :view
+  alias Pleroma.Web.MastodonAPI.AccountView
+
+  @source_types [:staff, :global, :past_interactions]
+
+  def render("index.json", %{users: users} = opts) do
+    Enum.map(users, fn user ->
+      opts =
+        opts
+        |> Map.put(:user, user)
+        |> Map.delete(:users)
+
+      render("show.json", opts)
+    end)
+  end
+
+  def render("show.json", %{source: source, user: _user} = opts) when source in @source_types do
+    %{
+      source: source,
+      account: AccountView.render("show.json", opts)
+    }
+  end
+end
index dae1136176c794a3fa7f5489b8716a8c41263825..197e1f3462d13b75b4f0b87cfe95fe15dcf5a2b4 100644 (file)
@@ -192,6 +192,9 @@ defmodule Pleroma.Web.Router do
     patch("/users/deactivate", UserController, :deactivate)
     patch("/users/approve", UserController, :approve)
 
+    patch("/users/suggest", UserController, :suggest)
+    patch("/users/unsuggest", UserController, :unsuggest)
+
     get("/relay", RelayController, :index)
     post("/relay", RelayController, :follow)
     delete("/relay", RelayController, :unfollow)
@@ -535,6 +538,7 @@ defmodule Pleroma.Web.Router do
     delete("/push/subscription", SubscriptionController, :delete)
 
     get("/suggestions", SuggestionController, :index)
+    delete("/suggestions/:account_id", SuggestionController, :dismiss)
 
     get("/timelines/home", TimelineController, :home)
     get("/timelines/direct", TimelineController, :direct)
@@ -586,6 +590,8 @@ defmodule Pleroma.Web.Router do
     get("/search", SearchController, :search2)
 
     post("/media", MediaController, :create2)
+
+    get("/suggestions", SuggestionController, :index2)
   end
 
   scope "/api", Pleroma.Web do
diff --git a/priv/repo/migrations/20211126191138_add_suggestions.exs b/priv/repo/migrations/20211126191138_add_suggestions.exs
new file mode 100644 (file)
index 0000000..7cc67d8
--- /dev/null
@@ -0,0 +1,11 @@
+defmodule Pleroma.Repo.Migrations.AddSuggestions do
+  use Ecto.Migration
+
+  def change do
+    alter table(:users) do
+      add(:is_suggested, :boolean, default: false, null: false)
+    end
+
+    create_if_not_exists(index(:users, [:is_suggested]))
+  end
+end
index 357016e3e2ce9874876c3a792ce076f00fdf9c64..363da7665967546d97489a42b8a3109f1d3281fd 100644 (file)
@@ -34,4 +34,14 @@ defmodule Pleroma.User.QueryTest do
       assert %{internal: true} |> Query.build() |> Repo.aggregate(:count) == 2
     end
   end
+
+  test "is_suggested param" do
+    _user1 = insert(:user, is_suggested: false)
+    user2 = insert(:user, is_suggested: true)
+
+    assert [^user2] =
+             %{is_suggested: true}
+             |> User.Query.build()
+             |> Repo.all()
+  end
 end
index 12d5d5db6869c969def860bfff49765cc6c394bf..6cd93c34c28d598a7f83daf9ca27e73307d97058 100644 (file)
@@ -1718,6 +1718,38 @@ defmodule Pleroma.UserTest do
     assert user.banner == %{}
   end
 
+  describe "set_suggestion" do
+    test "suggests a user" do
+      user = insert(:user, is_suggested: false)
+      refute user.is_suggested
+      {:ok, user} = User.set_suggestion(user, true)
+      assert user.is_suggested
+    end
+
+    test "suggests a list of users" do
+      unsuggested_users = [
+        insert(:user, is_suggested: false),
+        insert(:user, is_suggested: false),
+        insert(:user, is_suggested: false)
+      ]
+
+      {:ok, users} = User.set_suggestion(unsuggested_users, true)
+
+      assert Enum.count(users) == 3
+
+      Enum.each(users, fn user ->
+        assert user.is_suggested
+      end)
+    end
+
+    test "unsuggests a user" do
+      user = insert(:user, is_suggested: true)
+      assert user.is_suggested
+      {:ok, user} = User.set_suggestion(user, false)
+      refute user.is_suggested
+    end
+  end
+
   test "get_public_key_for_ap_id fetches a user that's not in the db" do
     assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
   end
index d9da34f6ed5209d49d4253a0dfa23eecb18043ed..b199fa70450a7446e40cef7cb52913827db790f2 100644 (file)
@@ -873,6 +873,56 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
              "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
   end
 
+  test "PATCH /api/pleroma/admin/users/suggest", %{admin: admin, conn: conn} do
+    user1 = insert(:user, is_suggested: false)
+    user2 = insert(:user, is_suggested: false)
+
+    response =
+      conn
+      |> put_req_header("content-type", "application/json")
+      |> patch(
+        "/api/pleroma/admin/users/suggest",
+        %{nicknames: [user1.nickname, user2.nickname]}
+      )
+      |> json_response_and_validate_schema(200)
+
+    assert Enum.map(response["users"], & &1["is_suggested"]) == [true, true]
+    [user1, user2] = Repo.reload!([user1, user2])
+
+    assert user1.is_suggested
+    assert user2.is_suggested
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} added suggested users: @#{user1.nickname}, @#{user2.nickname}"
+  end
+
+  test "PATCH /api/pleroma/admin/users/unsuggest", %{admin: admin, conn: conn} do
+    user1 = insert(:user, is_suggested: true)
+    user2 = insert(:user, is_suggested: true)
+
+    response =
+      conn
+      |> put_req_header("content-type", "application/json")
+      |> patch(
+        "/api/pleroma/admin/users/unsuggest",
+        %{nicknames: [user1.nickname, user2.nickname]}
+      )
+      |> json_response_and_validate_schema(200)
+
+    assert Enum.map(response["users"], & &1["is_suggested"]) == [false, false]
+    [user1, user2] = Repo.reload!([user1, user2])
+
+    refute user1.is_suggested
+    refute user2.is_suggested
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} removed suggested users: @#{user1.nickname}, @#{user2.nickname}"
+  end
+
   test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
     user = insert(:user)
 
@@ -906,6 +956,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
       "display_name" => HTML.strip_tags(user.name || user.nickname),
       "is_confirmed" => true,
       "is_approved" => true,
+      "is_suggested" => false,
       "url" => user.ap_id,
       "registration_reason" => nil,
       "actor_type" => "Person",
index 168966fc9d3a7878dc5c6e6bd87c527085f3d571..89273e67bd3251204ec0c8ee7af2067ec34064ea 100644 (file)
@@ -4,8 +4,11 @@
 
 defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do
   use Pleroma.Web.ConnCase, async: true
+  alias Pleroma.UserRelationship
+  alias Pleroma.Web.CommonAPI
+  import Pleroma.Factory
 
-  setup do: oauth_access(["read"])
+  setup do: oauth_access(["read", "write"])
 
   test "returns empty result", %{conn: conn} do
     res =
@@ -15,4 +18,66 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do
 
     assert res == []
   end
+
+  test "returns v2 suggestions", %{conn: conn} do
+    %{id: user_id} = insert(:user, is_suggested: true)
+
+    res =
+      conn
+      |> get("/api/v2/suggestions")
+      |> json_response_and_validate_schema(200)
+
+    assert [%{"source" => "staff", "account" => %{"id" => ^user_id}}] = res
+  end
+
+  test "returns v2 suggestions excluding dismissed accounts", %{conn: conn} do
+    %{id: user_id} = insert(:user, is_suggested: true)
+
+    conn
+    |> delete("/api/v1/suggestions/#{user_id}")
+    |> json_response_and_validate_schema(200)
+
+    res =
+      conn
+      |> get("/api/v2/suggestions")
+      |> json_response_and_validate_schema(200)
+
+    assert [] = res
+  end
+
+  test "returns v2 suggestions excluding blocked accounts", %{conn: conn, user: blocker} do
+    blocked = insert(:user, is_suggested: true)
+    {:ok, _} = CommonAPI.block(blocker, blocked)
+
+    res =
+      conn
+      |> get("/api/v2/suggestions")
+      |> json_response_and_validate_schema(200)
+
+    assert [] = res
+  end
+
+  test "returns v2 suggestions excluding followed accounts", %{conn: conn, user: follower} do
+    followed = insert(:user, is_suggested: true)
+    {:ok, _, _, _} = CommonAPI.follow(follower, followed)
+
+    res =
+      conn
+      |> get("/api/v2/suggestions")
+      |> json_response_and_validate_schema(200)
+
+    assert [] = res
+  end
+
+  test "dismiss suggestion", %{conn: conn, user: source} do
+    target = insert(:user, is_suggested: true)
+
+    res =
+      conn
+      |> delete("/api/v1/suggestions/#{target.id}")
+      |> json_response_and_validate_schema(200)
+
+    assert res == %{}
+    assert UserRelationship.exists?(:suggestion_dismiss, source, target)
+  end
 end
index 60881756d9b402a3d624f44b17d6239c66d10994..9af5887780baf0129d9d166d72da981484eb2b53 100644 (file)
@@ -83,6 +83,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
         tags: [],
         is_admin: false,
         is_moderator: false,
+        is_suggested: false,
         hide_favorites: true,
         hide_followers: false,
         hide_follows: false,
@@ -183,6 +184,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
         tags: [],
         is_admin: false,
         is_moderator: false,
+        is_suggested: false,
         hide_favorites: true,
         hide_followers: false,
         hide_follows: false,
diff --git a/test/pleroma/web/mastodon_api/views/suggestion_view_test.exs b/test/pleroma/web/mastodon_api/views/suggestion_view_test.exs
new file mode 100644 (file)
index 0000000..5aae36c
--- /dev/null
@@ -0,0 +1,34 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.SuggestionViewTest do
+  use Pleroma.DataCase, async: true
+  import Pleroma.Factory
+  alias Pleroma.Web.MastodonAPI.SuggestionView, as: View
+
+  test "show.json" do
+    user = insert(:user, is_suggested: true)
+    json = View.render("show.json", %{user: user, source: :staff, skip_visibility_check: true})
+
+    assert json.source == :staff
+    assert json.account.id == user.id
+  end
+
+  test "index.json" do
+    user1 = insert(:user, is_suggested: true)
+    user2 = insert(:user, is_suggested: true)
+    user3 = insert(:user, is_suggested: true)
+
+    [suggestion1, suggestion2, suggestion3] =
+      View.render("index.json", %{
+        users: [user1, user2, user3],
+        source: :staff,
+        skip_visibility_check: true
+      })
+
+    assert suggestion1.source == :staff
+    assert suggestion2.account.id == user2.id
+    assert suggestion3.account.url == user3.ap_id
+  end
+end