Procházet zdrojové kódy

Move conversation actions to PleromaAPI.ConversationController

chore/expose-invalidation-to-adminfe
Egor Kislitsyn před 4 roky
rodič
revize
f3fc8b22b1
V databázi nebyl nalezen žádný známý klíč pro tento podpis ID GPG klíče: 1B49CB15B71E7805
7 změnil soubory, kde provedl 341 přidání a 324 odebrání
  1. +106
    -0
      lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex
  2. +0
    -93
      lib/pleroma/web/api_spec/operations/pleroma_operation.ex
  3. +95
    -0
      lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex
  4. +0
    -99
      lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex
  5. +4
    -8
      lib/pleroma/web/router.ex
  6. +136
    -0
      test/web/pleroma_api/controllers/conversation_controller_test.exs
  7. +0
    -124
      test/web/pleroma_api/controllers/pleroma_api_controller_test.exs

+ 106
- 0
lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex Zobrazit soubor

@@ -0,0 +1,106 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ApiSpec.PleromaConversationOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.Conversation
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
alias Pleroma.Web.ApiSpec.StatusOperation

import Pleroma.Web.ApiSpec.Helpers

def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, [])
end

def show_operation do
%Operation{
tags: ["Conversations"],
summary: "The conversation with the given ID",
parameters: [
Operation.parameter(:id, :path, :string, "Conversation ID",
example: "123",
required: true
)
],
security: [%{"oAuth" => ["read:statuses"]}],
operationId: "PleromaAPI.ConversationController.show",
responses: %{
200 => Operation.response("Conversation", "application/json", Conversation)
}
}
end

def statuses_operation do
%Operation{
tags: ["Conversations"],
summary: "Timeline for a given conversation",
parameters: [
Operation.parameter(:id, :path, :string, "Conversation ID",
example: "123",
required: true
)
| pagination_params()
],
security: [%{"oAuth" => ["read:statuses"]}],
operationId: "PleromaAPI.ConversationController.statuses",
responses: %{
200 =>
Operation.response(
"Array of Statuses",
"application/json",
StatusOperation.array_of_statuses()
)
}
}
end

def update_operation do
%Operation{
tags: ["Conversations"],
summary: "Update a conversation. Used to change the set of recipients.",
parameters: [
Operation.parameter(:id, :path, :string, "Conversation ID",
example: "123",
required: true
),
Operation.parameter(
:recipients,
:query,
%Schema{type: :array, items: FlakeID},
"A list of ids of users that should receive posts to this conversation. This will replace the current list of recipients, so submit the full list. The owner of owner of the conversation will always be part of the set of recipients, though.",
required: true
)
],
security: [%{"oAuth" => ["write:conversations"]}],
operationId: "PleromaAPI.ConversationController.update",
responses: %{
200 => Operation.response("Conversation", "application/json", Conversation)
}
}
end

def mark_as_read_operation do
%Operation{
tags: ["Conversations"],
summary: "Marks all user's conversations as read",
security: [%{"oAuth" => ["write:conversations"]}],
operationId: "PleromaAPI.ConversationController.mark_as_read",
responses: %{
200 =>
Operation.response(
"Array of Conversations that were marked as read",
"application/json",
%Schema{
type: :array,
items: Conversation,
example: [Conversation.schema().example]
}
)
}
}
end
end

+ 0
- 93
lib/pleroma/web/api_spec/operations/pleroma_operation.ex Zobrazit soubor

@@ -7,105 +7,12 @@ defmodule Pleroma.Web.ApiSpec.PleromaOperation do
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.NotificationOperation
alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.Conversation
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
alias Pleroma.Web.ApiSpec.StatusOperation

import Pleroma.Web.ApiSpec.Helpers

def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, [])
end

def conversation_operation do
%Operation{
tags: ["Conversations"],
summary: "The conversation with the given ID",
parameters: [
Operation.parameter(:id, :path, :string, "Conversation ID",
example: "123",
required: true
)
],
security: [%{"oAuth" => ["read:statuses"]}],
operationId: "PleromaController.conversation",
responses: %{
200 => Operation.response("Conversation", "application/json", Conversation)
}
}
end

def conversation_statuses_operation do
%Operation{
tags: ["Conversations"],
summary: "Timeline for a given conversation",
parameters: [
Operation.parameter(:id, :path, :string, "Conversation ID",
example: "123",
required: true
)
| pagination_params()
],
security: [%{"oAuth" => ["read:statuses"]}],
operationId: "PleromaController.conversation_statuses",
responses: %{
200 =>
Operation.response(
"Array of Statuses",
"application/json",
StatusOperation.array_of_statuses()
)
}
}
end

def update_conversation_operation do
%Operation{
tags: ["Conversations"],
summary: "Update a conversation. Used to change the set of recipients.",
parameters: [
Operation.parameter(:id, :path, :string, "Conversation ID",
example: "123",
required: true
),
Operation.parameter(
:recipients,
:query,
%Schema{type: :array, items: FlakeID},
"A list of ids of users that should receive posts to this conversation. This will replace the current list of recipients, so submit the full list. The owner of owner of the conversation will always be part of the set of recipients, though.",
required: true
)
],
security: [%{"oAuth" => ["write:conversations"]}],
operationId: "PleromaController.update_conversation",
responses: %{
200 => Operation.response("Conversation", "application/json", Conversation)
}
}
end

def mark_conversations_as_read_operation do
%Operation{
tags: ["Conversations"],
summary: "Marks all user's conversations as read",
security: [%{"oAuth" => ["write:conversations"]}],
operationId: "PleromaController.mark_conversations_as_read",
responses: %{
200 =>
Operation.response(
"Array of Conversations that were marked as read",
"application/json",
%Schema{
type: :array,
items: Conversation,
example: [Conversation.schema().example]
}
)
}
}
end

def mark_notifications_as_read_operation do
%Operation{
tags: ["Notifications"],


+ 95
- 0
lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex Zobrazit soubor

@@ -0,0 +1,95 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.PleromaAPI.ConversationController do
use Pleroma.Web, :controller

import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]

alias Pleroma.Conversation.Participation
alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.MastodonAPI.StatusView

plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(:put_view, Pleroma.Web.MastodonAPI.ConversationView)
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:show, :statuses])

plug(
OAuthScopesPlug,
%{scopes: ["write:conversations"]} when action in [:update, :mark_as_read]
)

defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaConversationOperation

def show(%{assigns: %{user: %{id: user_id} = user}} = conn, %{id: participation_id}) do
with %Participation{user_id: ^user_id} = participation <- Participation.get(participation_id) do
render(conn, "participation.json", participation: participation, for: user)
else
_error ->
conn
|> put_status(:not_found)
|> json(%{"error" => "Unknown conversation id"})
end
end

def statuses(
%{assigns: %{user: %{id: user_id} = user}} = conn,
%{id: participation_id} = params
) do
with %Participation{user_id: ^user_id} = participation <-
Participation.get(participation_id, preload: [:conversation]) do
params =
params
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)

activities =
participation.conversation.ap_id
|> ActivityPub.fetch_activities_for_context_query(params)
|> Pleroma.Pagination.fetch_paginated(Map.put(params, "total", false))
|> Enum.reverse()

conn
|> add_link_headers(activities)
|> put_view(StatusView)
|> render("index.json", activities: activities, for: user, as: :activity)
else
_error ->
conn
|> put_status(:not_found)
|> json(%{"error" => "Unknown conversation id"})
end
end

def update(
%{assigns: %{user: %{id: user_id} = user}} = conn,
%{id: participation_id, recipients: recipients}
) do
with %Participation{user_id: ^user_id} = participation <- Participation.get(participation_id),
{:ok, participation} <- Participation.set_recipients(participation, recipients) do
render(conn, "participation.json", participation: participation, for: user)
else
{:error, message} ->
conn
|> put_status(:bad_request)
|> json(%{"error" => message})

_error ->
conn
|> put_status(:not_found)
|> json(%{"error" => "Unknown conversation id"})
end
end

def mark_as_read(%{assigns: %{user: user}} = conn, _params) do
with {:ok, _, participations} <- Participation.mark_all_as_read(user) do
conn
|> add_link_headers(participations)
|> render("participations.json", participations: participations, for: user)
end
end
end

+ 0
- 99
lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex Zobrazit soubor

@@ -5,118 +5,19 @@
defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
use Pleroma.Web, :controller

import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]

alias Pleroma.Conversation.Participation
alias Pleroma.Notification
alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.MastodonAPI.ConversationView
alias Pleroma.Web.MastodonAPI.NotificationView
alias Pleroma.Web.MastodonAPI.StatusView

plug(Pleroma.Web.ApiSpec.CastAndValidate)

plug(
OAuthScopesPlug,
%{scopes: ["read:statuses"]}
when action in [:conversation, :conversation_statuses]
)

plug(
OAuthScopesPlug,
%{scopes: ["write:conversations"]}
when action in [:update_conversation, :mark_conversations_as_read]
)

plug(
OAuthScopesPlug,
%{scopes: ["write:notifications"]} when action == :mark_notifications_as_read
)

defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaOperation

def conversation(%{assigns: %{user: user}} = conn, %{id: participation_id}) do
with %Participation{} = participation <- Participation.get(participation_id),
true <- user.id == participation.user_id do
conn
|> put_view(ConversationView)
|> render("participation.json", %{participation: participation, for: user})
else
_error ->
conn
|> put_status(404)
|> json(%{"error" => "Unknown conversation id"})
end
end

def conversation_statuses(
%{assigns: %{user: %{id: user_id} = user}} = conn,
%{id: participation_id} = params
) do
with %Participation{user_id: ^user_id} = participation <-
Participation.get(participation_id, preload: [:conversation]) do
params =
params
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)

activities =
participation.conversation.ap_id
|> ActivityPub.fetch_activities_for_context_query(params)
|> Pleroma.Pagination.fetch_paginated(Map.put(params, "total", false))
|> Enum.reverse()

conn
|> add_link_headers(activities)
|> put_view(StatusView)
|> render("index.json",
activities: activities,
for: user,
as: :activity
)
else
_error ->
conn
|> put_status(404)
|> json(%{"error" => "Unknown conversation id"})
end
end

def update_conversation(
%{assigns: %{user: user}} = conn,
%{id: participation_id, recipients: recipients}
) do
with %Participation{} = participation <- Participation.get(participation_id),
true <- user.id == participation.user_id,
{:ok, participation} <- Participation.set_recipients(participation, recipients) do
conn
|> put_view(ConversationView)
|> render("participation.json", %{participation: participation, for: user})
else
{:error, message} ->
conn
|> put_status(:bad_request)
|> json(%{"error" => message})

_error ->
conn
|> put_status(404)
|> json(%{"error" => "Unknown conversation id"})
end
end

def mark_conversations_as_read(%{assigns: %{user: user}} = conn, _params) do
with {:ok, _, participations} <- Participation.mark_all_as_read(user) do
conn
|> add_link_headers(participations)
|> put_view(ConversationView)
|> render("participations.json", participations: participations, for: user)
end
end

def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{id: notification_id}) do
with {:ok, notification} <- Notification.read_one(user, notification_id) do
conn


+ 4
- 8
lib/pleroma/web/router.ex Zobrazit soubor

@@ -305,15 +305,11 @@ defmodule Pleroma.Web.Router do
scope [] do
pipe_through(:authenticated_api)

get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses)
get("/conversations/:id", PleromaAPIController, :conversation)
post("/conversations/read", PleromaAPIController, :mark_conversations_as_read)
end

scope [] do
pipe_through(:authenticated_api)
get("/conversations/:id/statuses", ConversationController, :statuses)
get("/conversations/:id", ConversationController, :show)
post("/conversations/read", ConversationController, :mark_as_read)
patch("/conversations/:id", ConversationController, :update)

patch("/conversations/:id", PleromaAPIController, :update_conversation)
put("/statuses/:id/reactions/:emoji", EmojiReactionController, :create)
delete("/statuses/:id/reactions/:emoji", EmojiReactionController, :delete)
post("/notifications/read", PleromaAPIController, :mark_notifications_as_read)


+ 136
- 0
test/web/pleroma_api/controllers/conversation_controller_test.exs Zobrazit soubor

@@ -0,0 +1,136 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.PleromaAPI.ConversationControllerTest do
use Pleroma.Web.ConnCase

alias Pleroma.Conversation.Participation
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.CommonAPI

import Pleroma.Factory

test "/api/v1/pleroma/conversations/:id" do
user = insert(:user)
%{user: other_user, conn: conn} = oauth_access(["read:statuses"])

{:ok, _activity} =
CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"})

[participation] = Participation.for_user(other_user)

result =
conn
|> get("/api/v1/pleroma/conversations/#{participation.id}")
|> json_response_and_validate_schema(200)

assert result["id"] == participation.id |> to_string()
end

test "/api/v1/pleroma/conversations/:id/statuses" do
user = insert(:user)
%{user: other_user, conn: conn} = oauth_access(["read:statuses"])
third_user = insert(:user)

{:ok, _activity} =
CommonAPI.post(user, %{status: "Hi @#{third_user.nickname}!", visibility: "direct"})

{:ok, activity} =
CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"})

[participation] = Participation.for_user(other_user)

{:ok, activity_two} =
CommonAPI.post(other_user, %{
status: "Hi!",
in_reply_to_status_id: activity.id,
in_reply_to_conversation_id: participation.id
})

result =
conn
|> get("/api/v1/pleroma/conversations/#{participation.id}/statuses")
|> json_response_and_validate_schema(200)

assert length(result) == 2

id_one = activity.id
id_two = activity_two.id
assert [%{"id" => ^id_one}, %{"id" => ^id_two}] = result

{:ok, %{id: id_three}} =
CommonAPI.post(other_user, %{
status: "Bye!",
in_reply_to_status_id: activity.id,
in_reply_to_conversation_id: participation.id
})

assert [%{"id" => ^id_two}, %{"id" => ^id_three}] =
conn
|> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?limit=2")
|> json_response_and_validate_schema(:ok)

assert [%{"id" => ^id_three}] =
conn
|> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?min_id=#{id_two}")
|> json_response_and_validate_schema(:ok)
end

test "PATCH /api/v1/pleroma/conversations/:id" do
%{user: user, conn: conn} = oauth_access(["write:conversations"])
other_user = insert(:user)

{:ok, _activity} = CommonAPI.post(user, %{status: "Hi", visibility: "direct"})

[participation] = Participation.for_user(user)

participation = Repo.preload(participation, :recipients)

user = User.get_cached_by_id(user.id)
assert [user] == participation.recipients
assert other_user not in participation.recipients

query = "recipients[]=#{user.id}&recipients[]=#{other_user.id}"

result =
conn
|> patch("/api/v1/pleroma/conversations/#{participation.id}?#{query}")
|> json_response_and_validate_schema(200)

assert result["id"] == participation.id |> to_string

[participation] = Participation.for_user(user)
participation = Repo.preload(participation, :recipients)

assert user in participation.recipients
assert other_user in participation.recipients
end

test "POST /api/v1/pleroma/conversations/read" do
user = insert(:user)
%{user: other_user, conn: conn} = oauth_access(["write:conversations"])

{:ok, _activity} =
CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"})

{:ok, _activity} =
CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"})

[participation2, participation1] = Participation.for_user(other_user)
assert Participation.get(participation2.id).read == false
assert Participation.get(participation1.id).read == false
assert User.get_cached_by_id(other_user.id).unread_conversation_count == 2

[%{"unread" => false}, %{"unread" => false}] =
conn
|> post("/api/v1/pleroma/conversations/read", %{})
|> json_response_and_validate_schema(200)

[participation2, participation1] = Participation.for_user(other_user)
assert Participation.get(participation2.id).read == true
assert Participation.get(participation1.id).read == true
assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0
end
end

+ 0
- 124
test/web/pleroma_api/controllers/pleroma_api_controller_test.exs Zobrazit soubor

@@ -5,136 +5,12 @@
defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
use Pleroma.Web.ConnCase

alias Pleroma.Conversation.Participation
alias Pleroma.Notification
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.CommonAPI

import Pleroma.Factory

test "/api/v1/pleroma/conversations/:id" do
user = insert(:user)
%{user: other_user, conn: conn} = oauth_access(["read:statuses"])

{:ok, _activity} =
CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"})

[participation] = Participation.for_user(other_user)

result =
conn
|> get("/api/v1/pleroma/conversations/#{participation.id}")
|> json_response_and_validate_schema(200)

assert result["id"] == participation.id |> to_string()
end

test "/api/v1/pleroma/conversations/:id/statuses" do
user = insert(:user)
%{user: other_user, conn: conn} = oauth_access(["read:statuses"])
third_user = insert(:user)

{:ok, _activity} =
CommonAPI.post(user, %{status: "Hi @#{third_user.nickname}!", visibility: "direct"})

{:ok, activity} =
CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"})

[participation] = Participation.for_user(other_user)

{:ok, activity_two} =
CommonAPI.post(other_user, %{
status: "Hi!",
in_reply_to_status_id: activity.id,
in_reply_to_conversation_id: participation.id
})

result =
conn
|> get("/api/v1/pleroma/conversations/#{participation.id}/statuses")
|> json_response_and_validate_schema(200)

assert length(result) == 2

id_one = activity.id
id_two = activity_two.id
assert [%{"id" => ^id_one}, %{"id" => ^id_two}] = result

{:ok, %{id: id_three}} =
CommonAPI.post(other_user, %{
status: "Bye!",
in_reply_to_status_id: activity.id,
in_reply_to_conversation_id: participation.id
})

assert [%{"id" => ^id_two}, %{"id" => ^id_three}] =
conn
|> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?limit=2")
|> json_response_and_validate_schema(:ok)

assert [%{"id" => ^id_three}] =
conn
|> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?min_id=#{id_two}")
|> json_response_and_validate_schema(:ok)
end

test "PATCH /api/v1/pleroma/conversations/:id" do
%{user: user, conn: conn} = oauth_access(["write:conversations"])
other_user = insert(:user)

{:ok, _activity} = CommonAPI.post(user, %{status: "Hi", visibility: "direct"})

[participation] = Participation.for_user(user)

participation = Repo.preload(participation, :recipients)

user = User.get_cached_by_id(user.id)
assert [user] == participation.recipients
assert other_user not in participation.recipients

query = "recipients[]=#{user.id}&recipients[]=#{other_user.id}"

result =
conn
|> patch("/api/v1/pleroma/conversations/#{participation.id}?#{query}")
|> json_response_and_validate_schema(200)

assert result["id"] == participation.id |> to_string

[participation] = Participation.for_user(user)
participation = Repo.preload(participation, :recipients)

assert user in participation.recipients
assert other_user in participation.recipients
end

test "POST /api/v1/pleroma/conversations/read" do
user = insert(:user)
%{user: other_user, conn: conn} = oauth_access(["write:conversations"])

{:ok, _activity} =
CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"})

{:ok, _activity} =
CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"})

[participation2, participation1] = Participation.for_user(other_user)
assert Participation.get(participation2.id).read == false
assert Participation.get(participation1.id).read == false
assert User.get_cached_by_id(other_user.id).unread_conversation_count == 2

[%{"unread" => false}, %{"unread" => false}] =
conn
|> post("/api/v1/pleroma/conversations/read", %{})
|> json_response_and_validate_schema(200)

[participation2, participation1] = Participation.for_user(other_user)
assert Participation.get(participation2.id).read == true
assert Participation.get(participation1.id).read == true
assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0
end

describe "POST /api/v1/pleroma/notifications/read" do
setup do: oauth_access(["write:notifications"])



Načítá se…
Zrušit
Uložit