1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ApiSpec.StatusOperation do
6 alias OpenApiSpex.Operation
7 alias OpenApiSpex.Schema
8 alias Pleroma.Web.ApiSpec.AccountOperation
9 alias Pleroma.Web.ApiSpec.Schemas.ApiError
10 alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
11 alias Pleroma.Web.ApiSpec.Schemas.FlakeID
12 alias Pleroma.Web.ApiSpec.Schemas.ScheduledStatus
13 alias Pleroma.Web.ApiSpec.Schemas.Status
14 alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
16 import Pleroma.Web.ApiSpec.Helpers
18 def open_api_operation(action) do
19 operation = String.to_existing_atom("#{action}_operation")
20 apply(__MODULE__, operation, [])
23 def index_operation do
25 tags: ["Retrieve status information"],
26 summary: "Multiple statuses",
27 security: [%{"oAuth" => ["read:statuses"]}],
32 %Schema{type: :array, items: FlakeID},
39 "Include reactions from muted acccounts."
42 operationId: "StatusController.index",
44 200 => Operation.response("Array of Status", "application/json", array_of_statuses())
49 def create_operation do
51 tags: ["Status actions"],
52 summary: "Publish new status",
53 security: [%{"oAuth" => ["write:statuses"]}],
54 description: "Post a new status",
55 operationId: "StatusController.create",
56 requestBody: request_body("Parameters", create_request(), required: true),
60 "Status. When `scheduled_at` is present, ScheduledStatus is returned instead",
62 %Schema{anyOf: [Status, ScheduledStatus]}
64 422 => Operation.response("Bad Request / MRF Rejection", "application/json", ApiError)
71 tags: ["Retrieve status information"],
73 description: "View information about a status",
74 operationId: "StatusController.show",
75 security: [%{"oAuth" => ["read:statuses"]}],
82 "Include reactions from muted acccounts."
86 200 => status_response(),
87 404 => Operation.response("Not Found", "application/json", ApiError)
92 def delete_operation do
94 tags: ["Status actions"],
96 security: [%{"oAuth" => ["write:statuses"]}],
97 description: "Delete one of your own statuses",
98 operationId: "StatusController.delete",
99 parameters: [id_param()],
101 200 => status_response(),
102 403 => Operation.response("Forbidden", "application/json", ApiError),
103 404 => Operation.response("Not Found", "application/json", ApiError)
108 def reblog_operation do
110 tags: ["Status actions"],
112 security: [%{"oAuth" => ["write:statuses"]}],
113 description: "Share a status",
114 operationId: "StatusController.reblog",
115 parameters: [id_param()],
117 request_body("Parameters", %Schema{
120 visibility: %Schema{allOf: [VisibilityScope]}
124 200 => status_response(),
125 404 => Operation.response("Not Found", "application/json", ApiError)
130 def unreblog_operation do
132 tags: ["Status actions"],
133 summary: "Undo reblog",
134 security: [%{"oAuth" => ["write:statuses"]}],
135 description: "Undo a reshare of a status",
136 operationId: "StatusController.unreblog",
137 parameters: [id_param()],
139 200 => status_response(),
140 404 => Operation.response("Not Found", "application/json", ApiError)
145 def favourite_operation do
147 tags: ["Status actions"],
148 summary: "Favourite",
149 security: [%{"oAuth" => ["write:favourites"]}],
150 description: "Add a status to your favourites list",
151 operationId: "StatusController.favourite",
152 parameters: [id_param()],
154 200 => status_response(),
155 404 => Operation.response("Not Found", "application/json", ApiError)
160 def unfavourite_operation do
162 tags: ["Status actions"],
163 summary: "Undo favourite",
164 security: [%{"oAuth" => ["write:favourites"]}],
165 description: "Remove a status from your favourites list",
166 operationId: "StatusController.unfavourite",
167 parameters: [id_param()],
169 200 => status_response(),
170 404 => Operation.response("Not Found", "application/json", ApiError)
177 tags: ["Status actions"],
178 summary: "Pin to profile",
179 security: [%{"oAuth" => ["write:accounts"]}],
180 description: "Feature one of your own public statuses at the top of your profile",
181 operationId: "StatusController.pin",
182 parameters: [id_param()],
184 200 => status_response(),
186 Operation.response("Bad Request", "application/json", %Schema{
188 title: "Unprocessable Entity",
190 "error" => "You have already pinned the maximum number of statuses"
194 Operation.response("Not found", "application/json", %Schema{
196 title: "Unprocessable Entity",
198 "error" => "Record not found"
203 "Unprocessable Entity",
207 title: "Unprocessable Entity",
209 "error" => "Someone else's status cannot be pinned"
217 def unpin_operation do
219 tags: ["Status actions"],
220 summary: "Unpin from profile",
221 security: [%{"oAuth" => ["write:accounts"]}],
222 description: "Unfeature a status from the top of your profile",
223 operationId: "StatusController.unpin",
224 parameters: [id_param()],
226 200 => status_response(),
228 Operation.response("Bad Request", "application/json", %Schema{
230 title: "Unprocessable Entity",
232 "error" => "You have already pinned the maximum number of statuses"
236 Operation.response("Not found", "application/json", %Schema{
238 title: "Unprocessable Entity",
240 "error" => "Record not found"
247 def bookmark_operation do
249 tags: ["Status actions"],
251 security: [%{"oAuth" => ["write:bookmarks"]}],
252 description: "Privately bookmark a status",
253 operationId: "StatusController.bookmark",
254 parameters: [id_param()],
256 200 => status_response()
261 def unbookmark_operation do
263 tags: ["Status actions"],
264 summary: "Undo bookmark",
265 security: [%{"oAuth" => ["write:bookmarks"]}],
266 description: "Remove a status from your private bookmarks",
267 operationId: "StatusController.unbookmark",
268 parameters: [id_param()],
270 200 => status_response()
275 def mute_conversation_operation do
277 tags: ["Status actions"],
278 summary: "Mute conversation",
279 security: [%{"oAuth" => ["write:mutes"]}],
280 description: "Do not receive notifications for the thread that this status is part of.",
281 operationId: "StatusController.mute_conversation",
283 request_body("Parameters", %Schema{
289 description: "Expire the mute in `expires_in` seconds. Default 0 for infinity",
299 %Schema{type: :integer, default: 0},
300 "Expire the mute in `expires_in` seconds. Default 0 for infinity"
304 200 => status_response(),
305 400 => Operation.response("Error", "application/json", ApiError)
310 def unmute_conversation_operation do
312 tags: ["Status actions"],
313 summary: "Unmute conversation",
314 security: [%{"oAuth" => ["write:mutes"]}],
316 "Start receiving notifications again for the thread that this status is part of",
317 operationId: "StatusController.unmute_conversation",
318 parameters: [id_param()],
320 200 => status_response(),
321 400 => Operation.response("Error", "application/json", ApiError)
326 def favourited_by_operation do
328 tags: ["Retrieve status information"],
329 summary: "Favourited by",
330 description: "View who favourited a given status",
331 operationId: "StatusController.favourited_by",
332 security: [%{"oAuth" => ["read:accounts"]}],
333 parameters: [id_param()],
339 AccountOperation.array_of_accounts()
341 404 => Operation.response("Not Found", "application/json", ApiError)
346 def reblogged_by_operation do
348 tags: ["Retrieve status information"],
349 summary: "Reblogged by",
350 description: "View who reblogged a given status",
351 operationId: "StatusController.reblogged_by",
352 security: [%{"oAuth" => ["read:accounts"]}],
353 parameters: [id_param()],
359 AccountOperation.array_of_accounts()
361 404 => Operation.response("Not Found", "application/json", ApiError)
366 def context_operation do
368 tags: ["Retrieve status information"],
369 summary: "Parent and child statuses",
370 description: "View statuses above and below this status in the thread",
371 operationId: "StatusController.context",
372 security: [%{"oAuth" => ["read:statuses"]}],
373 parameters: [id_param()],
375 200 => Operation.response("Context", "application/json", context())
380 def favourites_operation do
383 summary: "Favourited statuses",
385 "Statuses the user has favourited. Please note that you have to use the link headers to paginate this. You can not build the query parameters yourself.",
386 operationId: "StatusController.favourites",
387 parameters: pagination_params(),
388 security: [%{"oAuth" => ["read:favourites"]}],
390 200 => Operation.response("Array of Statuses", "application/json", array_of_statuses())
395 def bookmarks_operation do
398 summary: "Bookmarked statuses",
399 description: "Statuses the user has bookmarked",
400 operationId: "StatusController.bookmarks",
401 parameters: pagination_params(),
402 security: [%{"oAuth" => ["read:bookmarks"]}],
404 200 => Operation.response("Array of Statuses", "application/json", array_of_statuses())
409 def array_of_statuses do
410 %Schema{type: :array, items: Status, example: [Status.schema().example]}
413 defp create_request do
415 title: "StatusCreateRequest",
422 "Text content of the status. If `media_ids` is provided, this becomes optional. Attaching a `poll` is optional while `status` is provided."
427 items: %Schema{type: :string},
428 description: "Array of Attachment ids to be attached as media."
431 in_reply_to_id: %Schema{
434 description: "ID of the status being replied to, if status is a reply"
437 allOf: [BooleanLike],
439 description: "Mark status and attached media as sensitive?"
441 spoiler_text: %Schema{
445 "Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field."
447 scheduled_at: %Schema{
449 format: :"date-time",
452 "ISO 8601 Datetime at which to schedule a status. Providing this paramter will cause ScheduledStatus to be returned instead of Status. Must be at least 5 minutes in the future."
457 description: "ISO 639 language code for this status."
459 # Pleroma-specific properties:
461 allOf: [BooleanLike],
464 "If set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example"
466 content_type: %Schema{
470 "The MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint."
475 items: %Schema{type: :string},
477 "A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for for post visibility are not affected by this and will still apply"
483 %Schema{type: :string, description: "`list:LIST_ID`", example: "LIST:123"}
486 "Visibility of the posted status. Besides standard MastoAPI values (`direct`, `private`, `unlisted` or `public`) it can be used to address a List by setting it to `list:LIST_ID`"
492 "The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour."
494 in_reply_to_conversation_id: %Schema{
498 "Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`."
502 "status" => "What time is it?",
503 "sensitive" => "false",
505 "options" => ["Cofe", "Adventure"],
516 required: [:options, :expires_in],
520 items: %Schema{type: :string},
521 description: "Array of possible answers. Must be provided with `poll[expires_in]`."
527 "Duration the poll should be open, in seconds. Must be provided with `poll[options]`"
530 allOf: [BooleanLike],
532 description: "Allow multiple choices?"
534 hide_totals: %Schema{
535 allOf: [BooleanLike],
537 description: "Hide vote counts until the poll ends?"
544 Operation.parameter(:id, :path, FlakeID, "Status ID",
545 example: "9umDrYheeY451cQnEe",
550 defp status_response do
551 Operation.response("Status", "application/json", Status)
556 title: "StatusContext",
558 "Represents the tree around a given status. Used for reconstructing threads of statuses.",
560 required: [:ancestors, :descendants],
562 ancestors: array_of_statuses(),
563 descendants: array_of_statuses()
566 "ancestors" => [Status.schema().example],
567 "descendants" => [Status.schema().example]