Browse Source

Move reaction actions to EmojiReactionController

chore/expose-invalidation-to-adminfe
Egor Kislitsyn 4 years ago
parent
commit
9a5de0f454
No known key found for this signature in database GPG Key ID: 1B49CB15B71E7805
8 changed files with 327 additions and 286 deletions
  1. +102
    -0
      lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex
  2. +2
    -90
      lib/pleroma/web/api_spec/operations/pleroma_operation.ex
  3. +61
    -0
      lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex
  4. +0
    -77
      lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex
  5. +33
    -0
      lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex
  6. +4
    -4
      lib/pleroma/web/router.ex
  7. +125
    -0
      test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs
  8. +0
    -115
      test/web/pleroma_api/controllers/pleroma_api_controller_test.exs

+ 102
- 0
lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex View File

@@ -0,0 +1,102 @@
# 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.EmojiReactionOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.Account
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
alias Pleroma.Web.ApiSpec.Schemas.Status

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

def index_operation do
%Operation{
tags: ["Emoji Reactions"],
summary:
"Get an object of emoji to account mappings with accounts that reacted to the post",
parameters: [
Operation.parameter(:id, :path, FlakeID, "Status ID", required: true),
Operation.parameter(:emoji, :path, :string, "Filter by a single unicode emoji",
required: false
)
],
security: [%{"oAuth" => ["read:statuses"]}],
operationId: "EmojiReactionController.index",
responses: %{
200 => array_of_reactions_response()
}
}
end

def create_operation do
%Operation{
tags: ["Emoji Reactions"],
summary: "React to a post with a unicode emoji",
parameters: [
Operation.parameter(:id, :path, FlakeID, "Status ID", required: true),
Operation.parameter(:emoji, :path, :string, "A single character unicode emoji",
required: true
)
],
security: [%{"oAuth" => ["write:statuses"]}],
operationId: "EmojiReactionController.create",
responses: %{
200 => Operation.response("Status", "application/json", Status)
}
}
end

def delete_operation do
%Operation{
tags: ["Emoji Reactions"],
summary: "Remove a reaction to a post with a unicode emoji",
parameters: [
Operation.parameter(:id, :path, FlakeID, "Status ID", required: true),
Operation.parameter(:emoji, :path, :string, "A single character unicode emoji",
required: true
)
],
security: [%{"oAuth" => ["write:statuses"]}],
operationId: "EmojiReactionController.delete",
responses: %{
200 => Operation.response("Status", "application/json", Status)
}
}
end

defp array_of_reactions_response do
Operation.response("Array of Emoji Reactions", "application/json", %Schema{
type: :array,
items: emoji_reaction(),
example: [emoji_reaction().example]
})
end

defp emoji_reaction do
%Schema{
title: "EmojiReaction",
type: :object,
properties: %{
name: %Schema{type: :string, description: "Emoji"},
count: %Schema{type: :integer, description: "Count of reactions with this emoji"},
me: %Schema{type: :boolean, description: "Did I react with this emoji?"},
accounts: %Schema{
type: :array,
items: Account,
description: "Array of accounts reacted with this emoji"
}
},
example: %{
"name" => "😱",
"count" => 1,
"me" => false,
"accounts" => [Account.schema().example]
}
}
end
end

+ 2
- 90
lib/pleroma/web/api_spec/operations/pleroma_operation.ex View File

@@ -5,13 +5,11 @@
defmodule Pleroma.Web.ApiSpec.PleromaOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.Account
alias Pleroma.Web.ApiSpec.NotificationOperation
alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
alias Pleroma.Web.ApiSpec.Schemas.Status
alias Pleroma.Web.ApiSpec.Schemas.Conversation
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
alias Pleroma.Web.ApiSpec.StatusOperation
alias Pleroma.Web.ApiSpec.NotificationOperation

import Pleroma.Web.ApiSpec.Helpers

@@ -20,92 +18,6 @@ defmodule Pleroma.Web.ApiSpec.PleromaOperation do
apply(__MODULE__, operation, [])
end

def emoji_reactions_by_operation do
%Operation{
tags: ["Emoji Reactions"],
summary:
"Get an object of emoji to account mappings with accounts that reacted to the post",
parameters: [
Operation.parameter(:id, :path, FlakeID, "Status ID", required: true),
Operation.parameter(:emoji, :path, :string, "Filter by a single unicode emoji",
required: false
)
],
security: [%{"oAuth" => ["read:statuses"]}],
operationId: "PleromaController.emoji_reactions_by",
responses: %{
200 => array_of_reactions_response()
}
}
end

def react_with_emoji_operation do
%Operation{
tags: ["Emoji Reactions"],
summary: "React to a post with a unicode emoji",
parameters: [
Operation.parameter(:id, :path, FlakeID, "Status ID", required: true),
Operation.parameter(:emoji, :path, :string, "A single character unicode emoji",
required: true
)
],
security: [%{"oAuth" => ["write:statuses"]}],
operationId: "PleromaController.react_with_emoji",
responses: %{
200 => Operation.response("Status", "application/json", Status)
}
}
end

def unreact_with_emoji_operation do
%Operation{
tags: ["Emoji Reactions"],
summary: "Remove a reaction to a post with a unicode emoji",
parameters: [
Operation.parameter(:id, :path, FlakeID, "Status ID", required: true),
Operation.parameter(:emoji, :path, :string, "A single character unicode emoji",
required: true
)
],
security: [%{"oAuth" => ["write:statuses"]}],
operationId: "PleromaController.unreact_with_emoji",
responses: %{
200 => Operation.response("Status", "application/json", Status)
}
}
end

defp array_of_reactions_response do
Operation.response("Array of Emoji Reactions", "application/json", %Schema{
type: :array,
items: emoji_reaction(),
example: [emoji_reaction().example]
})
end

defp emoji_reaction do
%Schema{
title: "EmojiReaction",
type: :object,
properties: %{
name: %Schema{type: :string, description: "Emoji"},
count: %Schema{type: :integer, description: "Count of reactions with this emoji"},
me: %Schema{type: :boolean, description: "Did I react with this emoji?"},
accounts: %Schema{
type: :array,
items: Account,
description: "Array of accounts reacted with this emoji"
}
},
example: %{
"name" => "😱",
"count" => 1,
"me" => false,
"accounts" => [Account.schema().example]
}
}
end

def conversation_operation do
%Operation{
tags: ["Conversations"],


+ 61
- 0
lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex View File

@@ -0,0 +1,61 @@
# 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.EmojiReactionController do
use Pleroma.Web, :controller

alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI.StatusView

plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action in [:create, :delete])

plug(
OAuthScopesPlug,
%{scopes: ["read:statuses"], fallback: :proceed_unauthenticated}
when action == :index
)

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

def index(%{assigns: %{user: user}} = conn, %{id: activity_id} = params) do
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
%Object{data: %{"reactions" => reactions}} when is_list(reactions) <-
Object.normalize(activity) do
reactions = filter(reactions, params)
render(conn, "index.json", emoji_reactions: reactions, user: user)
else
_e -> json(conn, [])
end
end

defp filter(reactions, %{emoji: emoji}) when is_binary(emoji) do
Enum.filter(reactions, fn [e, _] -> e == emoji end)
end

defp filter(reactions, _), do: reactions

def create(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) do
with {:ok, _activity} <- CommonAPI.react_with_emoji(activity_id, user, emoji) do
activity = Activity.get_by_id(activity_id)

conn
|> put_view(StatusView)
|> render("show.json", activity: activity, for: user, as: :activity)
end
end

def delete(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) do
with {:ok, _activity} <- CommonAPI.unreact_with_emoji(activity_id, user, emoji) do
activity = Activity.get_by_id(activity_id)

conn
|> put_view(StatusView)
|> render("show.json", activity: activity, for: user, as: :activity)
end
end
end

+ 0
- 77
lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex View File

@@ -7,15 +7,10 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do

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

alias Pleroma.Activity
alias Pleroma.Conversation.Participation
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.ConversationView
alias Pleroma.Web.MastodonAPI.NotificationView
alias Pleroma.Web.MastodonAPI.StatusView
@@ -30,18 +25,6 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do

plug(
OAuthScopesPlug,
%{scopes: ["read:statuses"], fallback: :proceed_unauthenticated}
when action == :emoji_reactions_by
)

plug(
OAuthScopesPlug,
%{scopes: ["write:statuses"]}
when action in [:react_with_emoji, :unreact_with_emoji]
)

plug(
OAuthScopesPlug,
%{scopes: ["write:conversations"]}
when action in [:update_conversation, :mark_conversations_as_read]
)
@@ -53,66 +36,6 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do

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

def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{id: activity_id} = params) do
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
%Object{data: %{"reactions" => emoji_reactions}} when is_list(emoji_reactions) <-
Object.normalize(activity) do
reactions =
emoji_reactions
|> Enum.map(fn [emoji, user_ap_ids] ->
if params[:emoji] && params[:emoji] != emoji do
nil
else
users =
Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1)
|> Enum.filter(fn
%{deactivated: false} -> true
_ -> false
end)

%{
name: emoji,
count: length(users),
accounts:
AccountView.render("index.json", %{
users: users,
for: user,
as: :user
}),
me: !!(user && user.ap_id in user_ap_ids)
}
end
end)
|> Enum.reject(&is_nil/1)

conn
|> json(reactions)
else
_e ->
conn
|> json([])
end
end

def react_with_emoji(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) do
with {:ok, _activity} <- CommonAPI.react_with_emoji(activity_id, user, emoji),
activity <- Activity.get_by_id(activity_id) do
conn
|> put_view(StatusView)
|> render("show.json", %{activity: activity, for: user, as: :activity})
end
end

def unreact_with_emoji(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) do
with {:ok, _activity} <-
CommonAPI.unreact_with_emoji(activity_id, user, emoji),
activity <- Activity.get_by_id(activity_id) do
conn
|> put_view(StatusView)
|> render("show.json", %{activity: activity, for: user, as: :activity})
end
end

def conversation(%{assigns: %{user: user}} = conn, %{id: participation_id}) do
with %Participation{} = participation <- Participation.get(participation_id),
true <- user.id == participation.user_id do


+ 33
- 0
lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex View File

@@ -0,0 +1,33 @@
# 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.EmojiReactionView do
use Pleroma.Web, :view

alias Pleroma.Web.MastodonAPI.AccountView

def render("index.json", %{emoji_reactions: emoji_reactions} = opts) do
render_many(emoji_reactions, __MODULE__, "show.json", opts)
end

def render("show.json", %{emoji_reaction: [emoji, user_ap_ids], user: user}) do
users = fetch_users(user_ap_ids)

%{
name: emoji,
count: length(users),
accounts: render(AccountView, "index.json", users: users, for: user, as: :user),
me: !!(user && user.ap_id in user_ap_ids)
}
end

defp fetch_users(user_ap_ids) do
user_ap_ids
|> Enum.map(&Pleroma.User.get_cached_by_ap_id/1)
|> Enum.filter(fn
%{deactivated: false} -> true
_ -> false
end)
end
end

+ 4
- 4
lib/pleroma/web/router.ex View File

@@ -297,8 +297,8 @@ defmodule Pleroma.Web.Router do
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
pipe_through(:api)

get("/statuses/:id/reactions/:emoji", PleromaAPIController, :emoji_reactions_by)
get("/statuses/:id/reactions", PleromaAPIController, :emoji_reactions_by)
get("/statuses/:id/reactions/:emoji", EmojiReactionController, :index)
get("/statuses/:id/reactions", EmojiReactionController, :index)
end

scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
@@ -314,8 +314,8 @@ defmodule Pleroma.Web.Router do
pipe_through(:authenticated_api)

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

patch("/accounts/update_avatar", AccountController, :update_avatar)


+ 125
- 0
test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs View File

@@ -0,0 +1,125 @@
# 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.EmojiReactionControllerTest do
use Oban.Testing, repo: Pleroma.Repo
use Pleroma.Web.ConnCase

alias Pleroma.Object
alias Pleroma.Tests.ObanHelpers
alias Pleroma.User
alias Pleroma.Web.CommonAPI

import Pleroma.Factory

test "PUT /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})

result =
conn
|> assign(:user, other_user)
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
|> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕")
|> json_response_and_validate_schema(200)

# We return the status, but this our implementation detail.
assert %{"id" => id} = result
assert to_string(activity.id) == id

assert result["pleroma"]["emoji_reactions"] == [
%{"name" => "☕", "count" => 1, "me" => true}
]
end

test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
{:ok, _reaction_activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")

ObanHelpers.perform_all()

result =
conn
|> assign(:user, other_user)
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
|> delete("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕")

assert %{"id" => id} = json_response_and_validate_schema(result, 200)
assert to_string(activity.id) == id

ObanHelpers.perform_all()

object = Object.get_by_ap_id(activity.data["object"])

assert object.data["reaction_count"] == 0
end

test "GET /api/v1/pleroma/statuses/:id/reactions", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)
doomed_user = insert(:user)

{:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})

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

assert result == []

{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
{:ok, _} = CommonAPI.react_with_emoji(activity.id, doomed_user, "🎅")

User.perform(:delete, doomed_user)

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

[%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] = result

assert represented_user["id"] == other_user.id

result =
conn
|> assign(:user, other_user)
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:statuses"]))
|> get("/api/v1/pleroma/statuses/#{activity.id}/reactions")
|> json_response_and_validate_schema(200)

assert [%{"name" => "🎅", "count" => 1, "accounts" => [_represented_user], "me" => true}] =
result
end

test "GET /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})

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

assert result == []

{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")

assert [%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] =
conn
|> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅")
|> json_response_and_validate_schema(200)

assert represented_user["id"] == other_user.id
end
end

+ 0
- 115
test/web/pleroma_api/controllers/pleroma_api_controller_test.exs View File

@@ -3,131 +3,16 @@
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
use Oban.Testing, repo: Pleroma.Repo
use Pleroma.Web.ConnCase

alias Pleroma.Conversation.Participation
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.Tests.ObanHelpers
alias Pleroma.User
alias Pleroma.Web.CommonAPI

import Pleroma.Factory

test "PUT /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})

result =
conn
|> assign(:user, other_user)
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
|> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕")
|> json_response_and_validate_schema(200)

# We return the status, but this our implementation detail.
assert %{"id" => id} = result
assert to_string(activity.id) == id

assert result["pleroma"]["emoji_reactions"] == [
%{"name" => "☕", "count" => 1, "me" => true}
]
end

test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
{:ok, _reaction_activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")

ObanHelpers.perform_all()

result =
conn
|> assign(:user, other_user)
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
|> delete("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕")

assert %{"id" => id} = json_response_and_validate_schema(result, 200)
assert to_string(activity.id) == id

ObanHelpers.perform_all()

object = Object.get_by_ap_id(activity.data["object"])

assert object.data["reaction_count"] == 0
end

test "GET /api/v1/pleroma/statuses/:id/reactions", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)
doomed_user = insert(:user)

{:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})

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

assert result == []

{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
{:ok, _} = CommonAPI.react_with_emoji(activity.id, doomed_user, "🎅")

User.perform(:delete, doomed_user)

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

[%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] = result

assert represented_user["id"] == other_user.id

result =
conn
|> assign(:user, other_user)
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:statuses"]))
|> get("/api/v1/pleroma/statuses/#{activity.id}/reactions")
|> json_response_and_validate_schema(200)

assert [%{"name" => "🎅", "count" => 1, "accounts" => [_represented_user], "me" => true}] =
result
end

test "GET /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})

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

assert result == []

{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")

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

[%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] = result

assert represented_user["id"] == other_user.id
end

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


Loading…
Cancel
Save