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{oneOf: [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(),
185 400 => Operation.response("Error", "application/json", ApiError)
190 def unpin_operation do
192 tags: ["Status actions"],
193 summary: "Unpin from profile",
194 security: [%{"oAuth" => ["write:accounts"]}],
195 description: "Unfeature a status from the top of your profile",
196 operationId: "StatusController.unpin",
197 parameters: [id_param()],
199 200 => status_response(),
200 400 => Operation.response("Error", "application/json", ApiError)
205 def bookmark_operation do
207 tags: ["Status actions"],
209 security: [%{"oAuth" => ["write:bookmarks"]}],
210 description: "Privately bookmark a status",
211 operationId: "StatusController.bookmark",
212 parameters: [id_param()],
214 200 => status_response()
219 def unbookmark_operation do
221 tags: ["Status actions"],
222 summary: "Undo bookmark",
223 security: [%{"oAuth" => ["write:bookmarks"]}],
224 description: "Remove a status from your private bookmarks",
225 operationId: "StatusController.unbookmark",
226 parameters: [id_param()],
228 200 => status_response()
233 def mute_conversation_operation do
235 tags: ["Status actions"],
236 summary: "Mute conversation",
237 security: [%{"oAuth" => ["write:mutes"]}],
238 description: "Do not receive notifications for the thread that this status is part of.",
239 operationId: "StatusController.mute_conversation",
241 request_body("Parameters", %Schema{
247 description: "Expire the mute in `expires_in` seconds. Default 0 for infinity",
257 %Schema{type: :integer, default: 0},
258 "Expire the mute in `expires_in` seconds. Default 0 for infinity"
262 200 => status_response(),
263 400 => Operation.response("Error", "application/json", ApiError)
268 def unmute_conversation_operation do
270 tags: ["Status actions"],
271 summary: "Unmute conversation",
272 security: [%{"oAuth" => ["write:mutes"]}],
274 "Start receiving notifications again for the thread that this status is part of",
275 operationId: "StatusController.unmute_conversation",
276 parameters: [id_param()],
278 200 => status_response(),
279 400 => Operation.response("Error", "application/json", ApiError)
284 def card_operation do
286 tags: ["Retrieve status information"],
288 summary: "Preview card",
289 description: "Deprecated in favor of card property inlined on Status entity",
290 operationId: "StatusController.card",
291 parameters: [id_param()],
292 security: [%{"oAuth" => ["read:statuses"]}],
295 Operation.response("Card", "application/json", %Schema{
299 type: %Schema{type: :string, enum: ["link", "photo", "video", "rich"]},
300 provider_name: %Schema{type: :string, nullable: true},
301 provider_url: %Schema{type: :string, format: :uri},
302 url: %Schema{type: :string, format: :uri},
303 image: %Schema{type: :string, nullable: true, format: :uri},
304 title: %Schema{type: :string},
305 description: %Schema{type: :string}
312 def favourited_by_operation do
314 tags: ["Retrieve status information"],
315 summary: "Favourited by",
316 description: "View who favourited a given status",
317 operationId: "StatusController.favourited_by",
318 security: [%{"oAuth" => ["read:accounts"]}],
319 parameters: [id_param()],
325 AccountOperation.array_of_accounts()
327 404 => Operation.response("Not Found", "application/json", ApiError)
332 def reblogged_by_operation do
334 tags: ["Retrieve status information"],
335 summary: "Reblogged by",
336 description: "View who reblogged a given status",
337 operationId: "StatusController.reblogged_by",
338 security: [%{"oAuth" => ["read:accounts"]}],
339 parameters: [id_param()],
345 AccountOperation.array_of_accounts()
347 404 => Operation.response("Not Found", "application/json", ApiError)
352 def context_operation do
354 tags: ["Retrieve status information"],
355 summary: "Parent and child statuses",
356 description: "View statuses above and below this status in the thread",
357 operationId: "StatusController.context",
358 security: [%{"oAuth" => ["read:statuses"]}],
359 parameters: [id_param()],
361 200 => Operation.response("Context", "application/json", context())
366 def favourites_operation do
369 summary: "Favourited statuses",
371 "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.",
372 operationId: "StatusController.favourites",
373 parameters: pagination_params(),
374 security: [%{"oAuth" => ["read:favourites"]}],
376 200 => Operation.response("Array of Statuses", "application/json", array_of_statuses())
381 def bookmarks_operation do
384 summary: "Bookmarked statuses",
385 description: "Statuses the user has bookmarked",
386 operationId: "StatusController.bookmarks",
387 parameters: pagination_params(),
388 security: [%{"oAuth" => ["read:bookmarks"]}],
390 200 => Operation.response("Array of Statuses", "application/json", array_of_statuses())
395 def array_of_statuses do
396 %Schema{type: :array, items: Status, example: [Status.schema().example]}
399 defp create_request do
401 title: "StatusCreateRequest",
408 "Text content of the status. If `media_ids` is provided, this becomes optional. Attaching a `poll` is optional while `status` is provided."
413 items: %Schema{type: :string},
414 description: "Array of Attachment ids to be attached as media."
417 in_reply_to_id: %Schema{
420 description: "ID of the status being replied to, if status is a reply"
423 allOf: [BooleanLike],
425 description: "Mark status and attached media as sensitive?"
427 spoiler_text: %Schema{
431 "Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field."
433 scheduled_at: %Schema{
435 format: :"date-time",
438 "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."
443 description: "ISO 639 language code for this status."
445 # Pleroma-specific properties:
447 allOf: [BooleanLike],
450 "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"
452 content_type: %Schema{
456 "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."
461 items: %Schema{type: :string},
463 "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"
469 %Schema{type: :string, description: "`list:LIST_ID`", example: "LIST:123"}
472 "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`"
478 "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."
480 in_reply_to_conversation_id: %Schema{
484 "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`."
488 "status" => "What time is it?",
489 "sensitive" => "false",
491 "options" => ["Cofe", "Adventure"],
502 required: [:options, :expires_in],
506 items: %Schema{type: :string},
507 description: "Array of possible answers. Must be provided with `poll[expires_in]`."
513 "Duration the poll should be open, in seconds. Must be provided with `poll[options]`"
516 allOf: [BooleanLike],
518 description: "Allow multiple choices?"
520 hide_totals: %Schema{
521 allOf: [BooleanLike],
523 description: "Hide vote counts until the poll ends?"
530 Operation.parameter(:id, :path, FlakeID, "Status ID",
531 example: "9umDrYheeY451cQnEe",
536 defp status_response do
537 Operation.response("Status", "application/json", Status)
542 title: "StatusContext",
544 "Represents the tree around a given status. Used for reconstructing threads of statuses.",
546 required: [:ancestors, :descendants],
548 ancestors: array_of_statuses(),
549 descendants: array_of_statuses()
552 "ancestors" => [Status.schema().example],
553 "descendants" => [Status.schema().example]