forked from AkkomaGang/akkoma
Merge branch 'feat/chat-list-pagination' into 'develop'
Chats: Introduce /api/v2/pleroma/chats which implements pagination Closes #2140 See merge request pleroma/pleroma!3325
This commit is contained in:
commit
e7b1f0f5f4
5 changed files with 196 additions and 127 deletions
|
@ -32,6 +32,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- **Breaking:** AdminAPI `GET /api/pleroma/admin/users/:nickname_or_id/statuses` changed response format and added the number of total users posts.
|
- **Breaking:** AdminAPI `GET /api/pleroma/admin/users/:nickname_or_id/statuses` changed response format and added the number of total users posts.
|
||||||
- **Breaking:** AdminAPI `GET /api/pleroma/admin/instances/:instance/statuses` changed response format and added the number of total users posts.
|
- **Breaking:** AdminAPI `GET /api/pleroma/admin/instances/:instance/statuses` changed response format and added the number of total users posts.
|
||||||
- Admin API: Reports now ordered by newest
|
- Admin API: Reports now ordered by newest
|
||||||
|
- Pleroma API: `GET /api/v1/pleroma/chats` is deprecated in favor of `GET /api/v2/pleroma/chats`.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
@ -58,6 +59,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
- Admin API: (`GET /api/pleroma/admin/users`) filter users by `unconfirmed` status and `actor_type`.
|
- Admin API: (`GET /api/pleroma/admin/users`) filter users by `unconfirmed` status and `actor_type`.
|
||||||
|
- Pleroma API: `GET /api/v2/pleroma/chats` added. It is exactly like `GET /api/v1/pleroma/chats` except supports pagination.
|
||||||
- Pleroma API: Add `idempotency_key` to the chat message entity that can be used for optimistic message sending.
|
- Pleroma API: Add `idempotency_key` to the chat message entity that can be used for optimistic message sending.
|
||||||
- Pleroma API: (`GET /api/v1/pleroma/federation_status`) Add a way to get a list of unreachable instances.
|
- Pleroma API: (`GET /api/v1/pleroma/federation_status`) Add a way to get a list of unreachable instances.
|
||||||
- Mastodon API: User and conversation mutes can now auto-expire if `expires_in` parameter was given while adding the mute.
|
- Mastodon API: User and conversation mutes can now auto-expire if `expires_in` parameter was given while adding the mute.
|
||||||
|
|
|
@ -131,8 +131,30 @@ def create_operation do
|
||||||
def index_operation do
|
def index_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Chats"],
|
tags: ["Chats"],
|
||||||
summary: "Retrieve list of chats",
|
summary: "Retrieve list of chats (unpaginated)",
|
||||||
|
deprecated: true,
|
||||||
|
description:
|
||||||
|
"Deprecated due to no support for pagination. Using [/api/v2/pleroma/chats](#operation/ChatController.index2) instead is recommended.",
|
||||||
operationId: "ChatController.index",
|
operationId: "ChatController.index",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users")
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("The chats of the user", "application/json", chats_response())
|
||||||
|
},
|
||||||
|
security: [
|
||||||
|
%{
|
||||||
|
"oAuth" => ["read:chats"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def index2_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Chats"],
|
||||||
|
summary: "Retrieve list of chats",
|
||||||
|
operationId: "ChatController.index2",
|
||||||
parameters: [
|
parameters: [
|
||||||
Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users")
|
Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users")
|
||||||
| pagination_params()
|
| pagination_params()
|
||||||
|
|
|
@ -35,7 +35,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:chats"]} when action in [:messages, :index, :show]
|
%{scopes: ["read:chats"]} when action in [:messages, :index, :index2, :show]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
|
plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
|
||||||
|
@ -138,18 +138,30 @@ def messages(%{assigns: %{user: user}} = conn, %{id: id} = params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def index(%{assigns: %{user: %{id: user_id} = user}} = conn, params) do
|
def index(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
chats =
|
||||||
|
index_query(user, params)
|
||||||
|
|> Repo.all()
|
||||||
|
|
||||||
|
render(conn, "index.json", chats: chats)
|
||||||
|
end
|
||||||
|
|
||||||
|
def index2(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
chats =
|
||||||
|
index_query(user, params)
|
||||||
|
|> Pagination.fetch_paginated(params)
|
||||||
|
|
||||||
|
render(conn, "index.json", chats: chats)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp index_query(%{id: user_id} = user, params) do
|
||||||
exclude_users =
|
exclude_users =
|
||||||
User.cached_blocked_users_ap_ids(user) ++
|
User.cached_blocked_users_ap_ids(user) ++
|
||||||
if params[:with_muted], do: [], else: User.cached_muted_users_ap_ids(user)
|
if params[:with_muted], do: [], else: User.cached_muted_users_ap_ids(user)
|
||||||
|
|
||||||
chats =
|
user_id
|
||||||
user_id
|
|> Chat.for_user_query()
|
||||||
|> Chat.for_user_query()
|
|> where([c], c.recipient not in ^exclude_users)
|
||||||
|> where([c], c.recipient not in ^exclude_users)
|
|
||||||
|> Repo.all()
|
|
||||||
|
|
||||||
render(conn, "index.json", chats: chats)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(%{assigns: %{user: user}} = conn, %{id: id}) do
|
def create(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||||
|
|
|
@ -420,6 +420,13 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/federation_status", InstancesController, :show)
|
get("/federation_status", InstancesController, :show)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scope "/api/v2/pleroma", Pleroma.Web.PleromaAPI do
|
||||||
|
scope [] do
|
||||||
|
pipe_through(:authenticated_api)
|
||||||
|
get("/chats", ChatController, :index2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||||
pipe_through(:authenticated_api)
|
pipe_through(:authenticated_api)
|
||||||
|
|
||||||
|
|
|
@ -304,139 +304,165 @@ test "it returns a chat", %{conn: conn, user: user} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /api/v1/pleroma/chats" do
|
for tested_endpoint <- ["/api/v1/pleroma/chats", "/api/v2/pleroma/chats"] do
|
||||||
setup do: oauth_access(["read:chats"])
|
describe "GET #{tested_endpoint}" do
|
||||||
|
setup do: oauth_access(["read:chats"])
|
||||||
|
|
||||||
test "it does not return chats with deleted users", %{conn: conn, user: user} do
|
test "it does not return chats with deleted users", %{conn: conn, user: user} do
|
||||||
recipient = insert(:user)
|
|
||||||
{:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
|
|
||||||
|
|
||||||
Pleroma.Repo.delete(recipient)
|
|
||||||
User.invalidate_cache(recipient)
|
|
||||||
|
|
||||||
result =
|
|
||||||
conn
|
|
||||||
|> get("/api/v1/pleroma/chats")
|
|
||||||
|> json_response_and_validate_schema(200)
|
|
||||||
|
|
||||||
assert length(result) == 0
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it does not return chats with users you blocked", %{conn: conn, user: user} do
|
|
||||||
recipient = insert(:user)
|
|
||||||
|
|
||||||
{:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
|
|
||||||
|
|
||||||
result =
|
|
||||||
conn
|
|
||||||
|> get("/api/v1/pleroma/chats")
|
|
||||||
|> json_response_and_validate_schema(200)
|
|
||||||
|
|
||||||
assert length(result) == 1
|
|
||||||
|
|
||||||
User.block(user, recipient)
|
|
||||||
|
|
||||||
result =
|
|
||||||
conn
|
|
||||||
|> get("/api/v1/pleroma/chats")
|
|
||||||
|> json_response_and_validate_schema(200)
|
|
||||||
|
|
||||||
assert length(result) == 0
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it does not return chats with users you muted", %{conn: conn, user: user} do
|
|
||||||
recipient = insert(:user)
|
|
||||||
|
|
||||||
{:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
|
|
||||||
|
|
||||||
result =
|
|
||||||
conn
|
|
||||||
|> get("/api/v1/pleroma/chats")
|
|
||||||
|> json_response_and_validate_schema(200)
|
|
||||||
|
|
||||||
assert length(result) == 1
|
|
||||||
|
|
||||||
User.mute(user, recipient)
|
|
||||||
|
|
||||||
result =
|
|
||||||
conn
|
|
||||||
|> get("/api/v1/pleroma/chats")
|
|
||||||
|> json_response_and_validate_schema(200)
|
|
||||||
|
|
||||||
assert length(result) == 0
|
|
||||||
|
|
||||||
result =
|
|
||||||
conn
|
|
||||||
|> get("/api/v1/pleroma/chats?with_muted=true")
|
|
||||||
|> json_response_and_validate_schema(200)
|
|
||||||
|
|
||||||
assert length(result) == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it returns all chats", %{conn: conn, user: user} do
|
|
||||||
Enum.each(1..30, fn _ ->
|
|
||||||
recipient = insert(:user)
|
recipient = insert(:user)
|
||||||
{:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
|
{:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
|
||||||
end)
|
|
||||||
|
|
||||||
result =
|
Pleroma.Repo.delete(recipient)
|
||||||
conn
|
User.invalidate_cache(recipient)
|
||||||
|> get("/api/v1/pleroma/chats")
|
|
||||||
|> json_response_and_validate_schema(200)
|
|
||||||
|
|
||||||
assert length(result) == 30
|
result =
|
||||||
end
|
conn
|
||||||
|
|> get(unquote(tested_endpoint))
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
test "it return a list of chats the current user is participating in, in descending order of updates",
|
assert length(result) == 0
|
||||||
%{conn: conn, user: user} do
|
end
|
||||||
har = insert(:user)
|
|
||||||
jafnhar = insert(:user)
|
|
||||||
tridi = insert(:user)
|
|
||||||
|
|
||||||
{:ok, chat_1} = Chat.get_or_create(user.id, har.ap_id)
|
test "it does not return chats with users you blocked", %{conn: conn, user: user} do
|
||||||
{:ok, chat_1} = time_travel(chat_1, -3)
|
recipient = insert(:user)
|
||||||
{:ok, chat_2} = Chat.get_or_create(user.id, jafnhar.ap_id)
|
|
||||||
{:ok, _chat_2} = time_travel(chat_2, -2)
|
|
||||||
{:ok, chat_3} = Chat.get_or_create(user.id, tridi.ap_id)
|
|
||||||
{:ok, chat_3} = time_travel(chat_3, -1)
|
|
||||||
|
|
||||||
# bump the second one
|
{:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
|
||||||
{:ok, chat_2} = Chat.bump_or_create(user.id, jafnhar.ap_id)
|
|
||||||
|
|
||||||
result =
|
result =
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/pleroma/chats")
|
|> get(unquote(tested_endpoint))
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
ids = Enum.map(result, & &1["id"])
|
assert length(result) == 1
|
||||||
|
|
||||||
assert ids == [
|
User.block(user, recipient)
|
||||||
chat_2.id |> to_string(),
|
|
||||||
chat_3.id |> to_string(),
|
|
||||||
chat_1.id |> to_string()
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it is not affected by :restrict_unauthenticated setting (issue #1973)", %{
|
result =
|
||||||
conn: conn,
|
conn
|
||||||
user: user
|
|> get(unquote(tested_endpoint))
|
||||||
} do
|
|> json_response_and_validate_schema(200)
|
||||||
clear_config([:restrict_unauthenticated, :profiles, :local], true)
|
|
||||||
clear_config([:restrict_unauthenticated, :profiles, :remote], true)
|
|
||||||
|
|
||||||
user2 = insert(:user)
|
assert length(result) == 0
|
||||||
user3 = insert(:user, local: false)
|
end
|
||||||
|
|
||||||
{:ok, _chat_12} = Chat.get_or_create(user.id, user2.ap_id)
|
test "it does not return chats with users you muted", %{conn: conn, user: user} do
|
||||||
{:ok, _chat_13} = Chat.get_or_create(user.id, user3.ap_id)
|
recipient = insert(:user)
|
||||||
|
|
||||||
result =
|
{:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
|
||||||
conn
|
|
||||||
|> get("/api/v1/pleroma/chats")
|
|
||||||
|> json_response_and_validate_schema(200)
|
|
||||||
|
|
||||||
account_ids = Enum.map(result, &get_in(&1, ["account", "id"]))
|
result =
|
||||||
assert Enum.sort(account_ids) == Enum.sort([user2.id, user3.id])
|
conn
|
||||||
|
|> get(unquote(tested_endpoint))
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert length(result) == 1
|
||||||
|
|
||||||
|
User.mute(user, recipient)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get(unquote(tested_endpoint))
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert length(result) == 0
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get("#{unquote(tested_endpoint)}?with_muted=true")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert length(result) == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if tested_endpoint == "/api/v1/pleroma/chats" do
|
||||||
|
test "it returns all chats", %{conn: conn, user: user} do
|
||||||
|
Enum.each(1..30, fn _ ->
|
||||||
|
recipient = insert(:user)
|
||||||
|
{:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
|
||||||
|
end)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get(unquote(tested_endpoint))
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert length(result) == 30
|
||||||
|
end
|
||||||
|
else
|
||||||
|
test "it paginates chats", %{conn: conn, user: user} do
|
||||||
|
Enum.each(1..30, fn _ ->
|
||||||
|
recipient = insert(:user)
|
||||||
|
{:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
|
||||||
|
end)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get(unquote(tested_endpoint))
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert length(result) == 20
|
||||||
|
last_id = List.last(result)["id"]
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get(unquote(tested_endpoint) <> "?max_id=#{last_id}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert length(result) == 10
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it return a list of chats the current user is participating in, in descending order of updates",
|
||||||
|
%{conn: conn, user: user} do
|
||||||
|
har = insert(:user)
|
||||||
|
jafnhar = insert(:user)
|
||||||
|
tridi = insert(:user)
|
||||||
|
|
||||||
|
{:ok, chat_1} = Chat.get_or_create(user.id, har.ap_id)
|
||||||
|
{:ok, chat_1} = time_travel(chat_1, -3)
|
||||||
|
{:ok, chat_2} = Chat.get_or_create(user.id, jafnhar.ap_id)
|
||||||
|
{:ok, _chat_2} = time_travel(chat_2, -2)
|
||||||
|
{:ok, chat_3} = Chat.get_or_create(user.id, tridi.ap_id)
|
||||||
|
{:ok, chat_3} = time_travel(chat_3, -1)
|
||||||
|
|
||||||
|
# bump the second one
|
||||||
|
{:ok, chat_2} = Chat.bump_or_create(user.id, jafnhar.ap_id)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get(unquote(tested_endpoint))
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
ids = Enum.map(result, & &1["id"])
|
||||||
|
|
||||||
|
assert ids == [
|
||||||
|
chat_2.id |> to_string(),
|
||||||
|
chat_3.id |> to_string(),
|
||||||
|
chat_1.id |> to_string()
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it is not affected by :restrict_unauthenticated setting (issue #1973)", %{
|
||||||
|
conn: conn,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
clear_config([:restrict_unauthenticated, :profiles, :local], true)
|
||||||
|
clear_config([:restrict_unauthenticated, :profiles, :remote], true)
|
||||||
|
|
||||||
|
user2 = insert(:user)
|
||||||
|
user3 = insert(:user, local: false)
|
||||||
|
|
||||||
|
{:ok, _chat_12} = Chat.get_or_create(user.id, user2.ap_id)
|
||||||
|
{:ok, _chat_13} = Chat.get_or_create(user.id, user3.ap_id)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get(unquote(tested_endpoint))
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
account_ids = Enum.map(result, &get_in(&1, ["account", "id"]))
|
||||||
|
assert Enum.sort(account_ids) == Enum.sort([user2.id, user3.id])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue