diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 62506f37a..6d62e9b43 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -149,6 +149,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,
diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex
index ac807fc79..334e395fb 100644
--- a/lib/pleroma/user/query.ex
+++ b/lib/pleroma/user/query.ex
@@ -167,6 +167,10 @@ defp compose_query({:unconfirmed, _}, 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)
diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex
index b941849f5..a34da98df 100644
--- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.MastodonAPI.SuggestionController do
use Pleroma.Web, :controller
+ alias Pleroma.User
require Logger
@@ -29,7 +30,7 @@ def index_operation do
def index2_operation do
%OpenApiSpex.Operation{
tags: ["Suggestions"],
- summary: "Follow suggestions (Not implemented)",
+ summary: "Follow suggestions",
operationId: "SuggestionController.index2",
responses: %{
200 => Pleroma.Web.ApiSpec.Helpers.empty_array_response()
@@ -42,6 +43,14 @@ def index(conn, params),
do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
@doc "GET /api/v2/suggestions"
- def index2(conn, params),
- do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
+ def index2(conn, params) do
+ limit = Map.get(params, :limit, 40) |> min(80)
+
+ users =
+ %{is_suggested: true, limit: limit}
+ |> User.Query.build()
+ |> Pleroma.Repo.all()
+
+ render(conn, "index.json", %{users: users, source: :staff, skip_visibility_check: true})
+ end
end
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
index 000000000..865229a88
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/views/suggestion_view.ex
@@ -0,0 +1,28 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors
+# 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
diff --git a/priv/repo/migrations/20211126191138_add_suggestions.exs b/priv/repo/migrations/20211126191138_add_suggestions.exs
new file mode 100644
index 000000000..5ad604e9d
--- /dev/null
+++ b/priv/repo/migrations/20211126191138_add_suggestions.exs
@@ -0,0 +1,9 @@
+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
+ end
+end
diff --git a/test/pleroma/user/query_test.exs b/test/pleroma/user/query_test.exs
index 357016e3e..363da7665 100644
--- a/test/pleroma/user/query_test.exs
+++ b/test/pleroma/user/query_test.exs
@@ -34,4 +34,14 @@ test "it returns internal users when enabled" 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
diff --git a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs
index 5a9aea680..407063fa1 100644
--- a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs
+++ b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do
use Pleroma.Web.ConnCase, async: true
+ import Pleroma.Factory
setup do: oauth_access(["read"])
@@ -16,12 +17,14 @@ test "returns empty result", %{conn: conn} do
assert res == []
end
- test "returns empty result (v2)", %{conn: conn} do
+ 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 res == []
+ assert [%{"source" => "staff", "account" => %{"id" => ^user_id}}] = res
end
end
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
index 000000000..5aae36ce9
--- /dev/null
+++ b/test/pleroma/web/mastodon_api/views/suggestion_view_test.exs
@@ -0,0 +1,34 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors
+# 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