From b17360cd7c92d8b2337fa4fd175c3e1312eb352e Mon Sep 17 00:00:00 2001
From: Alex Gleason <alex@alexgleason.me>
Date: Fri, 26 Nov 2021 14:33:27 -0600
Subject: [PATCH] v2 Suggestions: rudimentary API response

---
 lib/pleroma/user.ex                           |  1 +
 lib/pleroma/user/query.ex                     |  4 +++
 .../controllers/suggestion_controller.ex      | 15 ++++++--
 .../web/mastodon_api/views/suggestion_view.ex | 28 +++++++++++++++
 .../20211126191138_add_suggestions.exs        |  9 +++++
 test/pleroma/user/query_test.exs              | 10 ++++++
 .../suggestion_controller_test.exs            |  7 ++--
 .../views/suggestion_view_test.exs            | 34 +++++++++++++++++++
 8 files changed, 103 insertions(+), 5 deletions(-)
 create mode 100644 lib/pleroma/web/mastodon_api/views/suggestion_view.ex
 create mode 100644 priv/repo/migrations/20211126191138_add_suggestions.exs
 create mode 100644 test/pleroma/web/mastodon_api/views/suggestion_view_test.exs

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 @@ 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)
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 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController 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 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do
     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 <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
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 @@ 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
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 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest 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 <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
-- 
2.49.0