MastoAPI: Add user notes on accounts
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
c97f99ccf2
commit
40414bf177
|
@ -162,7 +162,8 @@ See [Admin-API](admin_api.md)
|
||||||
"requested": false,
|
"requested": false,
|
||||||
"domain_blocking": false,
|
"domain_blocking": false,
|
||||||
"showing_reblogs": true,
|
"showing_reblogs": true,
|
||||||
"endorsed": false
|
"endorsed": false,
|
||||||
|
"note": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -186,7 +187,8 @@ See [Admin-API](admin_api.md)
|
||||||
"requested": false,
|
"requested": false,
|
||||||
"domain_blocking": false,
|
"domain_blocking": false,
|
||||||
"showing_reblogs": true,
|
"showing_reblogs": true,
|
||||||
"endorsed": false
|
"endorsed": false,
|
||||||
|
"note": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# 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
|
|
@ -328,6 +328,29 @@ def unblock_operation do
|
||||||
}
|
}
|
||||||
end
|
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
|
def follow_by_uri_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Account actions"],
|
tags: ["Account actions"],
|
||||||
|
@ -685,6 +708,7 @@ defp array_of_relationships do
|
||||||
"blocked_by" => true,
|
"blocked_by" => true,
|
||||||
"muting" => false,
|
"muting" => false,
|
||||||
"muting_notifications" => false,
|
"muting_notifications" => false,
|
||||||
|
"note" => "",
|
||||||
"requested" => false,
|
"requested" => false,
|
||||||
"domain_blocking" => false,
|
"domain_blocking" => false,
|
||||||
"subscribing" => false,
|
"subscribing" => false,
|
||||||
|
@ -699,6 +723,7 @@ defp array_of_relationships do
|
||||||
"blocked_by" => true,
|
"blocked_by" => true,
|
||||||
"muting" => true,
|
"muting" => true,
|
||||||
"muting_notifications" => false,
|
"muting_notifications" => false,
|
||||||
|
"note" => "",
|
||||||
"requested" => true,
|
"requested" => true,
|
||||||
"domain_blocking" => false,
|
"domain_blocking" => false,
|
||||||
"subscribing" => false,
|
"subscribing" => false,
|
||||||
|
@ -713,6 +738,7 @@ defp array_of_relationships do
|
||||||
"blocked_by" => false,
|
"blocked_by" => false,
|
||||||
"muting" => true,
|
"muting" => true,
|
||||||
"muting_notifications" => false,
|
"muting_notifications" => false,
|
||||||
|
"note" => "",
|
||||||
"requested" => false,
|
"requested" => false,
|
||||||
"domain_blocking" => true,
|
"domain_blocking" => true,
|
||||||
"subscribing" => true,
|
"subscribing" => true,
|
||||||
|
@ -760,6 +786,23 @@ defp mute_request do
|
||||||
}
|
}
|
||||||
end
|
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
|
defp array_of_lists do
|
||||||
%Schema{
|
%Schema{
|
||||||
title: "ArrayOfLists",
|
title: "ArrayOfLists",
|
||||||
|
|
|
@ -194,6 +194,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
||||||
"id" => "9tKi3esbG7OQgZ2920",
|
"id" => "9tKi3esbG7OQgZ2920",
|
||||||
"muting" => false,
|
"muting" => false,
|
||||||
"muting_notifications" => false,
|
"muting_notifications" => false,
|
||||||
|
"note" => "",
|
||||||
"requested" => false,
|
"requested" => false,
|
||||||
"showing_reblogs" => true,
|
"showing_reblogs" => true,
|
||||||
"subscribing" => false
|
"subscribing" => false
|
||||||
|
|
|
@ -22,6 +22,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
|
||||||
id: FlakeID,
|
id: FlakeID,
|
||||||
muting: %Schema{type: :boolean},
|
muting: %Schema{type: :boolean},
|
||||||
muting_notifications: %Schema{type: :boolean},
|
muting_notifications: %Schema{type: :boolean},
|
||||||
|
note: %Schema{type: :string},
|
||||||
requested: %Schema{type: :boolean},
|
requested: %Schema{type: :boolean},
|
||||||
showing_reblogs: %Schema{type: :boolean},
|
showing_reblogs: %Schema{type: :boolean},
|
||||||
subscribing: %Schema{type: :boolean}
|
subscribing: %Schema{type: :boolean}
|
||||||
|
@ -36,6 +37,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
|
||||||
"id" => "9tKi3esbG7OQgZ2920",
|
"id" => "9tKi3esbG7OQgZ2920",
|
||||||
"muting" => false,
|
"muting" => false,
|
||||||
"muting_notifications" => false,
|
"muting_notifications" => false,
|
||||||
|
"note" => "",
|
||||||
"requested" => false,
|
"requested" => false,
|
||||||
"showing_reblogs" => true,
|
"showing_reblogs" => true,
|
||||||
"subscribing" => false
|
"subscribing" => false
|
||||||
|
|
|
@ -282,6 +282,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
||||||
"id" => "9toJCsKN7SmSf3aj5c",
|
"id" => "9toJCsKN7SmSf3aj5c",
|
||||||
"muting" => false,
|
"muting" => false,
|
||||||
"muting_notifications" => false,
|
"muting_notifications" => false,
|
||||||
|
"note" => "",
|
||||||
"requested" => false,
|
"requested" => false,
|
||||||
"showing_reblogs" => true,
|
"showing_reblogs" => true,
|
||||||
"subscribing" => false
|
"subscribing" => false
|
||||||
|
|
|
@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
|
|
||||||
alias Pleroma.Maps
|
alias Pleroma.Maps
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserNote
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Builder
|
alias Pleroma.Web.ActivityPub.Builder
|
||||||
alias Pleroma.Web.ActivityPub.Pipeline
|
alias Pleroma.Web.ActivityPub.Pipeline
|
||||||
|
@ -53,7 +54,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
when action in [:verify_credentials, :endorsements, :identity_proofs]
|
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)
|
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])
|
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
|
||||||
|
|
||||||
@relationship_actions [:follow, :unfollow]
|
@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(
|
plug(
|
||||||
RateLimiter,
|
RateLimiter,
|
||||||
|
@ -435,6 +440,15 @@ def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do
|
||||||
end
|
end
|
||||||
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"
|
@doc "POST /api/v1/follows"
|
||||||
def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
|
def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
|
||||||
case User.get_cached_by_nickname(uri) do
|
case User.get_cached_by_nickname(uri) do
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||||
|
|
||||||
alias Pleroma.FollowingRelationship
|
alias Pleroma.FollowingRelationship
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserNote
|
||||||
alias Pleroma.UserRelationship
|
alias Pleroma.UserRelationship
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
|
@ -156,7 +157,12 @@ def render(
|
||||||
target,
|
target,
|
||||||
&User.muting_reblogs?(&1, &2)
|
&User.muting_reblogs?(&1, &2)
|
||||||
),
|
),
|
||||||
endorsed: false
|
endorsed: false,
|
||||||
|
note:
|
||||||
|
UserNote.show(
|
||||||
|
reading_user,
|
||||||
|
target
|
||||||
|
)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -456,6 +456,7 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/accounts/:id/unblock", AccountController, :unblock)
|
post("/accounts/:id/unblock", AccountController, :unblock)
|
||||||
post("/accounts/:id/mute", AccountController, :mute)
|
post("/accounts/:id/mute", AccountController, :mute)
|
||||||
post("/accounts/:id/unmute", AccountController, :unmute)
|
post("/accounts/:id/unmute", AccountController, :unmute)
|
||||||
|
post("/accounts/:id/note", AccountController, :note)
|
||||||
|
|
||||||
get("/conversations", ConversationController, :index)
|
get("/conversations", ConversationController, :index)
|
||||||
post("/conversations/:id/read", ConversationController, :mark_as_read)
|
post("/conversations/:id/read", ConversationController, :mark_as_read)
|
||||||
|
|
|
@ -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
|
|
@ -1776,4 +1776,18 @@ test "getting a list of blocks" do
|
||||||
|
|
||||||
assert [%{"id" => ^id2}] = result
|
assert [%{"id" => ^id2}] = result
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -271,7 +271,8 @@ defp test_relationship_rendering(user, other_user, expected_result) do
|
||||||
requested: false,
|
requested: false,
|
||||||
domain_blocking: false,
|
domain_blocking: false,
|
||||||
showing_reblogs: true,
|
showing_reblogs: true,
|
||||||
endorsed: false
|
endorsed: false,
|
||||||
|
note: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
test "represent a relationship for the following and followed user" do
|
test "represent a relationship for the following and followed user" do
|
||||||
|
|
Loading…
Reference in New Issue