Add OpenAPI spec for PleromaAPI.PleromaAPIController
[akkoma] / lib / pleroma / web / pleroma_api / controllers / pleroma_api_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
6 use Pleroma.Web, :controller
7
8 import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
9
10 alias Pleroma.Activity
11 alias Pleroma.Conversation.Participation
12 alias Pleroma.Notification
13 alias Pleroma.Object
14 alias Pleroma.Plugs.OAuthScopesPlug
15 alias Pleroma.User
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.AccountView
19 alias Pleroma.Web.MastodonAPI.ConversationView
20 alias Pleroma.Web.MastodonAPI.NotificationView
21 alias Pleroma.Web.MastodonAPI.StatusView
22
23 plug(Pleroma.Web.ApiSpec.CastAndValidate)
24
25 plug(
26 OAuthScopesPlug,
27 %{scopes: ["read:statuses"]}
28 when action in [:conversation, :conversation_statuses]
29 )
30
31 plug(
32 OAuthScopesPlug,
33 %{scopes: ["read:statuses"], fallback: :proceed_unauthenticated}
34 when action == :emoji_reactions_by
35 )
36
37 plug(
38 OAuthScopesPlug,
39 %{scopes: ["write:statuses"]}
40 when action in [:react_with_emoji, :unreact_with_emoji]
41 )
42
43 plug(
44 OAuthScopesPlug,
45 %{scopes: ["write:conversations"]}
46 when action in [:update_conversation, :mark_conversations_as_read]
47 )
48
49 plug(
50 OAuthScopesPlug,
51 %{scopes: ["write:notifications"]} when action == :mark_notifications_as_read
52 )
53
54 defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaOperation
55
56 def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{id: activity_id} = params) do
57 with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
58 %Object{data: %{"reactions" => emoji_reactions}} when is_list(emoji_reactions) <-
59 Object.normalize(activity) do
60 reactions =
61 emoji_reactions
62 |> Enum.map(fn [emoji, user_ap_ids] ->
63 if params[:emoji] && params[:emoji] != emoji do
64 nil
65 else
66 users =
67 Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1)
68 |> Enum.filter(fn
69 %{deactivated: false} -> true
70 _ -> false
71 end)
72
73 %{
74 name: emoji,
75 count: length(users),
76 accounts:
77 AccountView.render("index.json", %{
78 users: users,
79 for: user,
80 as: :user
81 }),
82 me: !!(user && user.ap_id in user_ap_ids)
83 }
84 end
85 end)
86 |> Enum.reject(&is_nil/1)
87
88 conn
89 |> json(reactions)
90 else
91 _e ->
92 conn
93 |> json([])
94 end
95 end
96
97 def react_with_emoji(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) do
98 with {:ok, _activity} <- CommonAPI.react_with_emoji(activity_id, user, emoji),
99 activity <- Activity.get_by_id(activity_id) do
100 conn
101 |> put_view(StatusView)
102 |> render("show.json", %{activity: activity, for: user, as: :activity})
103 end
104 end
105
106 def unreact_with_emoji(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) do
107 with {:ok, _activity} <-
108 CommonAPI.unreact_with_emoji(activity_id, user, emoji),
109 activity <- Activity.get_by_id(activity_id) do
110 conn
111 |> put_view(StatusView)
112 |> render("show.json", %{activity: activity, for: user, as: :activity})
113 end
114 end
115
116 def conversation(%{assigns: %{user: user}} = conn, %{id: participation_id}) do
117 with %Participation{} = participation <- Participation.get(participation_id),
118 true <- user.id == participation.user_id do
119 conn
120 |> put_view(ConversationView)
121 |> render("participation.json", %{participation: participation, for: user})
122 else
123 _error ->
124 conn
125 |> put_status(404)
126 |> json(%{"error" => "Unknown conversation id"})
127 end
128 end
129
130 def conversation_statuses(
131 %{assigns: %{user: %{id: user_id} = user}} = conn,
132 %{id: participation_id} = params
133 ) do
134 with %Participation{user_id: ^user_id} = participation <-
135 Participation.get(participation_id, preload: [:conversation]) do
136 params =
137 params
138 |> Map.new(fn {key, value} -> {to_string(key), value} end)
139 |> Map.put("blocking_user", user)
140 |> Map.put("muting_user", user)
141 |> Map.put("user", user)
142
143 activities =
144 participation.conversation.ap_id
145 |> ActivityPub.fetch_activities_for_context_query(params)
146 |> Pleroma.Pagination.fetch_paginated(Map.put(params, "total", false))
147 |> Enum.reverse()
148
149 conn
150 |> add_link_headers(activities)
151 |> put_view(StatusView)
152 |> render("index.json",
153 activities: activities,
154 for: user,
155 as: :activity
156 )
157 else
158 _error ->
159 conn
160 |> put_status(404)
161 |> json(%{"error" => "Unknown conversation id"})
162 end
163 end
164
165 def update_conversation(
166 %{assigns: %{user: user}} = conn,
167 %{id: participation_id, recipients: recipients}
168 ) do
169 with %Participation{} = participation <- Participation.get(participation_id),
170 true <- user.id == participation.user_id,
171 {:ok, participation} <- Participation.set_recipients(participation, recipients) do
172 conn
173 |> put_view(ConversationView)
174 |> render("participation.json", %{participation: participation, for: user})
175 else
176 {:error, message} ->
177 conn
178 |> put_status(:bad_request)
179 |> json(%{"error" => message})
180
181 _error ->
182 conn
183 |> put_status(404)
184 |> json(%{"error" => "Unknown conversation id"})
185 end
186 end
187
188 def mark_conversations_as_read(%{assigns: %{user: user}} = conn, _params) do
189 with {:ok, _, participations} <- Participation.mark_all_as_read(user) do
190 conn
191 |> add_link_headers(participations)
192 |> put_view(ConversationView)
193 |> render("participations.json", participations: participations, for: user)
194 end
195 end
196
197 def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{id: notification_id}) do
198 with {:ok, notification} <- Notification.read_one(user, notification_id) do
199 conn
200 |> put_view(NotificationView)
201 |> render("show.json", %{notification: notification, for: user})
202 else
203 {:error, message} ->
204 conn
205 |> put_status(:bad_request)
206 |> json(%{"error" => message})
207 end
208 end
209
210 def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{max_id: max_id}) do
211 with notifications <- Notification.set_read_up_to(user, max_id) do
212 notifications = Enum.take(notifications, 80)
213
214 conn
215 |> put_view(NotificationView)
216 |> render("index.json",
217 notifications: notifications,
218 for: user
219 )
220 end
221 end
222 end