From 40414bf177c93b39d75c6091ef0ce1db093edb6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 21 Nov 2021 16:53:30 +0100 Subject: [PATCH 1/5] MastoAPI: Add user notes on accounts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- docs/development/API/pleroma_api.md | 6 ++- lib/pleroma/user_note.ex | 52 +++++++++++++++++++ .../api_spec/operations/account_operation.ex | 43 +++++++++++++++ lib/pleroma/web/api_spec/schemas/account.ex | 1 + .../api_spec/schemas/account_relationship.ex | 2 + lib/pleroma/web/api_spec/schemas/status.ex | 1 + .../controllers/account_controller.ex | 18 ++++++- .../web/mastodon_api/views/account_view.ex | 8 ++- lib/pleroma/web/router.ex | 1 + .../20211121000000_create_user_notes.exs | 17 ++++++ .../controllers/account_controller_test.exs | 14 +++++ .../mastodon_api/views/account_view_test.exs | 3 +- 12 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 lib/pleroma/user_note.ex create mode 100644 priv/repo/migrations/20211121000000_create_user_notes.exs diff --git a/docs/development/API/pleroma_api.md b/docs/development/API/pleroma_api.md index 8f6422da0..b401a7cc7 100644 --- a/docs/development/API/pleroma_api.md +++ b/docs/development/API/pleroma_api.md @@ -162,7 +162,8 @@ See [Admin-API](admin_api.md) "requested": false, "domain_blocking": false, "showing_reblogs": true, - "endorsed": false + "endorsed": false, + "note": "" } ``` @@ -186,7 +187,8 @@ See [Admin-API](admin_api.md) "requested": false, "domain_blocking": false, "showing_reblogs": true, - "endorsed": false + "endorsed": false, + "note": "" } ``` diff --git a/lib/pleroma/user_note.ex b/lib/pleroma/user_note.ex new file mode 100644 index 000000000..5e82d359f --- /dev/null +++ b/lib/pleroma/user_note.ex @@ -0,0 +1,52 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.UserNote do + use Ecto.Schema + + import Ecto.Changeset + import Ecto.Query + + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.UserNote + + schema "user_notes" do + belongs_to(:source, User, type: FlakeId.Ecto.CompatType) + belongs_to(:target, User, type: FlakeId.Ecto.CompatType) + field(:comment, :string) + + timestamps() + end + + def changeset(%UserNote{} = user_note, params \\ %{}) do + user_note + |> cast(params, [:source_id, :target_id, :comment]) + |> validate_required([:source_id, :target_id]) + end + + def show(%User{} = source, %User{} = target) do + with %UserNote{} = note <- + UserNote + |> where(source_id: ^source.id, target_id: ^target.id) + |> Repo.one() do + note.comment + else + _ -> "" + end + end + + def create(%User{} = source, %User{} = target, comment) do + %UserNote{} + |> changeset(%{ + source_id: source.id, + target_id: target.id, + comment: comment + }) + |> Repo.insert( + on_conflict: {:replace, [:comment]}, + conflict_target: [:source_id, :target_id] + ) + end +end diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 54e5ebc76..6bec9f178 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -328,6 +328,29 @@ def unblock_operation do } end + def note_operation do + %Operation{ + tags: ["Account actions"], + summary: "Create note", + operationId: "AccountController.note", + security: [%{"oAuth" => ["follow", "write:accounts"]}], + requestBody: request_body("Parameters", note_request()), + description: "Create a note for the given account.", + parameters: [ + %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, + Operation.parameter( + :comment, + :query, + %Schema{type: :string}, + "Account note body" + ) + ], + responses: %{ + 200 => Operation.response("Relationship", "application/json", AccountRelationship) + } + } + end + def follow_by_uri_operation do %Operation{ tags: ["Account actions"], @@ -685,6 +708,7 @@ defp array_of_relationships do "blocked_by" => true, "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "domain_blocking" => false, "subscribing" => false, @@ -699,6 +723,7 @@ defp array_of_relationships do "blocked_by" => true, "muting" => true, "muting_notifications" => false, + "note" => "", "requested" => true, "domain_blocking" => false, "subscribing" => false, @@ -713,6 +738,7 @@ defp array_of_relationships do "blocked_by" => false, "muting" => true, "muting_notifications" => false, + "note" => "", "requested" => false, "domain_blocking" => true, "subscribing" => true, @@ -760,6 +786,23 @@ defp mute_request do } end + defp note_request do + %Schema{ + title: "AccountNoteRequest", + description: "POST body for adding anote for an account", + type: :object, + properties: %{ + comment: %Schema{ + type: :string, + description: "Account note body", + } + }, + example: %{ + "comment" => "Example note" + } + } + end + defp array_of_lists do %Schema{ title: "ArrayOfLists", diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index bd7143ab9..e0bd2728b 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -194,6 +194,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do "id" => "9tKi3esbG7OQgZ2920", "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "showing_reblogs" => true, "subscribing" => false diff --git a/lib/pleroma/web/api_spec/schemas/account_relationship.ex b/lib/pleroma/web/api_spec/schemas/account_relationship.ex index 16b73ebb4..163066032 100644 --- a/lib/pleroma/web/api_spec/schemas/account_relationship.ex +++ b/lib/pleroma/web/api_spec/schemas/account_relationship.ex @@ -22,6 +22,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do id: FlakeID, muting: %Schema{type: :boolean}, muting_notifications: %Schema{type: :boolean}, + note: %Schema{type: :string}, requested: %Schema{type: :boolean}, showing_reblogs: %Schema{type: :boolean}, subscribing: %Schema{type: :boolean} @@ -36,6 +37,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do "id" => "9tKi3esbG7OQgZ2920", "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "showing_reblogs" => true, "subscribing" => false diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 3d042dc19..60801f322 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -282,6 +282,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "id" => "9toJCsKN7SmSf3aj5c", "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "showing_reblogs" => true, "subscribing" => false diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 5fcbffc34..8a43d49d3 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do alias Pleroma.Maps alias Pleroma.User + alias Pleroma.UserNote alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.Pipeline @@ -53,7 +54,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do when action in [:verify_credentials, :endorsements, :identity_proofs] ) - plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :update_credentials) + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"]} + when action in [:update_credentials, :note] + ) plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists) @@ -79,7 +84,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute]) @relationship_actions [:follow, :unfollow] - @needs_account ~W(followers following lists follow unfollow mute unmute block unblock)a + @needs_account ~W(followers following lists follow unfollow mute unmute block unblock note)a plug( RateLimiter, @@ -435,6 +440,15 @@ def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do end end + @doc "POST /api/v1/accounts/:id/note" + def note(%{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn, _params) do + with {:ok, _user_note} <- UserNote.create(noter, target, comment) do + render(conn, "relationship.json", user: noter, target: target) + else + {:error, message} -> json_response(conn, :forbidden, %{error: message}) + end + end + @doc "POST /api/v1/follows" def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do case User.get_cached_by_nickname(uri) do diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 9e9de33f6..a3a9f9548 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do alias Pleroma.FollowingRelationship alias Pleroma.User + alias Pleroma.UserNote alias Pleroma.UserRelationship alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.MastodonAPI.AccountView @@ -156,7 +157,12 @@ def render( target, &User.muting_reblogs?(&1, &2) ), - endorsed: false + endorsed: false, + note: + UserNote.show( + reading_user, + target + ) } end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index abb332ec2..ca5db8ea3 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -456,6 +456,7 @@ defmodule Pleroma.Web.Router do post("/accounts/:id/unblock", AccountController, :unblock) post("/accounts/:id/mute", AccountController, :mute) post("/accounts/:id/unmute", AccountController, :unmute) + post("/accounts/:id/note", AccountController, :note) get("/conversations", ConversationController, :index) post("/conversations/:id/read", ConversationController, :mark_as_read) diff --git a/priv/repo/migrations/20211121000000_create_user_notes.exs b/priv/repo/migrations/20211121000000_create_user_notes.exs new file mode 100644 index 000000000..8fc23749f --- /dev/null +++ b/priv/repo/migrations/20211121000000_create_user_notes.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.CreateUserNotes do + use Ecto.Migration + + def change do + create_if_not_exists table(:user_notes) do + add(:source_id, references(:users, type: :uuid, on_delete: :delete_all)) + add(:target_id, references(:users, type: :uuid, on_delete: :delete_all)) + add(:comment, :string) + + timestamps() + end + + create_if_not_exists( + unique_index(:user_notes, [:source_id, :target_id]) + ) + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index a92a58224..48e658dd2 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1776,4 +1776,18 @@ test "getting a list of blocks" do assert [%{"id" => ^id2}] = result end + + test "create a note on a user" do + %{conn: conn} = oauth_access(["write:accounts"]) + other_user = insert(:user) + + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{other_user.id}/note", %{ + "comment" => "Example note" + }) + + assert %{"note" => "Example note"} = json_response_and_validate_schema(ret_conn, 200) + end end diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs index 60881756d..9fe9d73bc 100644 --- a/test/pleroma/web/mastodon_api/views/account_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -271,7 +271,8 @@ defp test_relationship_rendering(user, other_user, expected_result) do requested: false, domain_blocking: false, showing_reblogs: true, - endorsed: false + endorsed: false, + note: "" } test "represent a relationship for the following and followed user" do From 106b5c26781dd1e92b6cd820b3dff41a27a4c4d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 21 Nov 2021 17:36:37 +0100 Subject: [PATCH 2/5] Fix a typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/api_spec/operations/account_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 6bec9f178..6cedada2c 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -789,7 +789,7 @@ defp mute_request do defp note_request do %Schema{ title: "AccountNoteRequest", - description: "POST body for adding anote for an account", + description: "POST body for adding a note for an account", type: :object, properties: %{ comment: %Schema{ From cb76faece99c706685b71ad5a13943036b481645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 21 Nov 2021 17:50:42 +0100 Subject: [PATCH 3/5] Update test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../controllers/account_controller_test.exs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index 48e658dd2..4f855ac5c 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1778,16 +1778,19 @@ test "getting a list of blocks" do end test "create a note on a user" do - %{conn: conn} = oauth_access(["write:accounts"]) + %{conn: conn} = oauth_access(["write:accounts", "read:follows"]) other_user = insert(:user) - ret_conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/accounts/#{other_user.id}/note", %{ - "comment" => "Example note" - }) + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{other_user.id}/note", %{ + "comment" => "Example note" + }) - assert %{"note" => "Example note"} = json_response_and_validate_schema(ret_conn, 200) + assert [%{"note" => "Example note"}] = + conn + |> put_req_header("content-type", "application/json") + |> get("/api/v1/accounts/relationships?id=#{other_user.id}") + |> json_response_and_validate_schema(200) end end From 8e040e098b1176098123e52608a9a73adec2b5e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 21 Nov 2021 18:17:06 +0100 Subject: [PATCH 4/5] Lint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/api_spec/operations/account_operation.ex | 2 +- .../web/mastodon_api/controllers/account_controller.ex | 5 ++++- priv/repo/migrations/20211121000000_create_user_notes.exs | 4 +--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 6cedada2c..4aca16e72 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -794,7 +794,7 @@ defp note_request do properties: %{ comment: %Schema{ type: :string, - description: "Account note body", + description: "Account note body" } }, example: %{ diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 8a43d49d3..a2c4920c1 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -441,7 +441,10 @@ def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do end @doc "POST /api/v1/accounts/:id/note" - def note(%{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn, _params) do + def note( + %{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn, + _params + ) do with {:ok, _user_note} <- UserNote.create(noter, target, comment) do render(conn, "relationship.json", user: noter, target: target) else diff --git a/priv/repo/migrations/20211121000000_create_user_notes.exs b/priv/repo/migrations/20211121000000_create_user_notes.exs index 8fc23749f..b75e11695 100644 --- a/priv/repo/migrations/20211121000000_create_user_notes.exs +++ b/priv/repo/migrations/20211121000000_create_user_notes.exs @@ -10,8 +10,6 @@ def change do timestamps() end - create_if_not_exists( - unique_index(:user_notes, [:source_id, :target_id]) - ) + create_if_not_exists(unique_index(:user_notes, [:source_id, :target_id])) end end From 588bcbac55ebbaa1ea68792a1f60aa92c9915f69 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 22 Nov 2021 10:54:44 +0000 Subject: [PATCH 5/5] Apply 2 suggestion(s) to 2 file(s) --- lib/pleroma/web/api_spec/operations/account_operation.ex | 2 +- lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 4aca16e72..8613f3a98 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -331,7 +331,7 @@ def unblock_operation do def note_operation do %Operation{ tags: ["Account actions"], - summary: "Create note", + summary: "Set a private note about a user.", operationId: "AccountController.note", security: [%{"oAuth" => ["follow", "write:accounts"]}], requestBody: request_body("Parameters", note_request()), diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index a2c4920c1..5dfbecf5a 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -447,8 +447,6 @@ def note( ) do with {:ok, _user_note} <- UserNote.create(noter, target, comment) do render(conn, "relationship.json", user: noter, target: target) - else - {:error, message} -> json_response(conn, :forbidden, %{error: message}) end end