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 card_operation do
328 tags: ["Retrieve status information"],
330 summary: "Preview card",
331 description: "Deprecated in favor of card property inlined on Status entity",
332 operationId: "StatusController.card",
333 parameters: [id_param()],
334 security: [%{"oAuth" => ["read:statuses"]}],
337 Operation.response("Card", "application/json", %Schema{
341 type: %Schema{type: :string, enum: ["link", "photo", "video", "rich"]},
342 provider_name: %Schema{type: :string, nullable: true},
343 provider_url: %Schema{type: :string, format: :uri},
344 url: %Schema{type: :string, format: :uri},
345 image: %Schema{type: :string, nullable: true, format: :uri},
346 title: %Schema{type: :string},
347 description: %Schema{type: :string}
354 def favourited_by_operation do
356 tags: ["Retrieve status information"],
357 summary: "Favourited by",
358 description: "View who favourited a given status",
359 operationId: "StatusController.favourited_by",
360 security: [%{"oAuth" => ["read:accounts"]}],
361 parameters: [id_param()],
367 AccountOperation.array_of_accounts()
369 404 => Operation.response("Not Found", "application/json", ApiError)
374 def reblogged_by_operation do
376 tags: ["Retrieve status information"],
377 summary: "Reblogged by",
378 description: "View who reblogged a given status",
379 operationId: "StatusController.reblogged_by",
380 security: [%{"oAuth" => ["read:accounts"]}],
381 parameters: [id_param()],
387 AccountOperation.array_of_accounts()
389 404 => Operation.response("Not Found", "application/json", ApiError)
394 def context_operation do
396 tags: ["Retrieve status information"],
397 summary: "Parent and child statuses",
398 description: "View statuses above and below this status in the thread",
399 operationId: "StatusController.context",
400 security: [%{"oAuth" => ["read:statuses"]}],
401 parameters: [id_param()],
403 200 => Operation.response("Context", "application/json", context())
408 def favourites_operation do
411 summary: "Favourited statuses",
413 "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.",
414 operationId: "StatusController.favourites",
415 parameters: pagination_params(),
416 security: [%{"oAuth" => ["read:favourites"]}],
418 200 => Operation.response("Array of Statuses", "application/json", array_of_statuses())
423 def bookmarks_operation do
426 summary: "Bookmarked statuses",
427 description: "Statuses the user has bookmarked",
428 operationId: "StatusController.bookmarks",
429 parameters: pagination_params(),
430 security: [%{"oAuth" => ["read:bookmarks"]}],
432 200 => Operation.response("Array of Statuses", "application/json", array_of_statuses())
437 def array_of_statuses do
438 %Schema{type: :array, items: Status, example: [Status.schema().example]}
441 defp create_request do
443 title: "StatusCreateRequest",
450 "Text content of the status. If `media_ids` is provided, this becomes optional. Attaching a `poll` is optional while `status` is provided."
455 items: %Schema{type: :string},
456 description: "Array of Attachment ids to be attached as media."
459 in_reply_to_id: %Schema{
462 description: "ID of the status being replied to, if status is a reply"
465 allOf: [BooleanLike],
467 description: "Mark status and attached media as sensitive?"
469 spoiler_text: %Schema{
473 "Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field."
475 scheduled_at: %Schema{
477 format: :"date-time",
480 "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."
485 description: "ISO 639 language code for this status."
487 # Pleroma-specific properties:
489 allOf: [BooleanLike],
492 "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"
494 content_type: %Schema{
498 "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."
503 items: %Schema{type: :string},
505 "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"
511 %Schema{type: :string, description: "`list:LIST_ID`", example: "LIST:123"}
514 "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`"
520 "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."
522 in_reply_to_conversation_id: %Schema{
526 "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`."
530 "status" => "What time is it?",
531 "sensitive" => "false",
533 "options" => ["Cofe", "Adventure"],
544 required: [:options, :expires_in],
548 items: %Schema{type: :string},
549 description: "Array of possible answers. Must be provided with `poll[expires_in]`."
555 "Duration the poll should be open, in seconds. Must be provided with `poll[options]`"
558 allOf: [BooleanLike],
560 description: "Allow multiple choices?"
562 hide_totals: %Schema{
563 allOf: [BooleanLike],
565 description: "Hide vote counts until the poll ends?"
572 Operation.parameter(:id, :path, FlakeID, "Status ID",
573 example: "9umDrYheeY451cQnEe",
578 defp status_response do
579 Operation.response("Status", "application/json", Status)
584 title: "StatusContext",
586 "Represents the tree around a given status. Used for reconstructing threads of statuses.",
588 required: [:ancestors, :descendants],
590 ancestors: array_of_statuses(),
591 descendants: array_of_statuses()
594 "ancestors" => [Status.schema().example],
595 "descendants" => [Status.schema().example]