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.AccountOperation do
6 alias OpenApiSpex.Operation
7 alias OpenApiSpex.Reference
8 alias OpenApiSpex.Schema
9 alias Pleroma.Web.ApiSpec.Schemas.Account
10 alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
11 alias Pleroma.Web.ApiSpec.Schemas.ActorType
12 alias Pleroma.Web.ApiSpec.Schemas.ApiError
13 alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
14 alias Pleroma.Web.ApiSpec.Schemas.List
15 alias Pleroma.Web.ApiSpec.Schemas.Status
16 alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
18 import Pleroma.Web.ApiSpec.Helpers
20 @spec open_api_operation(atom) :: Operation.t()
21 def open_api_operation(action) do
22 operation = String.to_existing_atom("#{action}_operation")
23 apply(__MODULE__, operation, [])
26 @spec create_operation() :: Operation.t()
27 def create_operation do
29 tags: ["Account credentials"],
30 summary: "Register an account",
32 "Creates a user and account records. Returns an account access token for the app that initiated the request. The app should save this token for later, and should wait for the user to confirm their account by clicking a link in their email inbox.",
33 operationId: "AccountController.create",
34 requestBody: request_body("Parameters", create_request(), required: true),
36 200 => Operation.response("Account", "application/json", create_response()),
37 400 => Operation.response("Error", "application/json", ApiError),
38 403 => Operation.response("Error", "application/json", ApiError),
39 429 => Operation.response("Error", "application/json", ApiError)
44 def verify_credentials_operation do
46 tags: ["Account credentials"],
47 description: "Test to make sure that the user token works.",
48 summary: "Verify account credentials",
49 operationId: "AccountController.verify_credentials",
50 security: [%{"oAuth" => ["read:accounts"]}],
52 200 => Operation.response("Account", "application/json", Account)
57 def update_credentials_operation do
59 tags: ["Account credentials"],
60 summary: "Update account credentials",
61 description: "Update the user's display and preferences.",
62 operationId: "AccountController.update_credentials",
63 security: [%{"oAuth" => ["write:accounts"]}],
64 requestBody: request_body("Parameters", update_credentials_request(), required: true),
66 200 => Operation.response("Account", "application/json", Account),
67 403 => Operation.response("Error", "application/json", ApiError)
72 def relationships_operation do
74 tags: ["Retrieve account information"],
75 summary: "Relationship with current account",
76 operationId: "AccountController.relationships",
77 description: "Find out whether a given account is followed, blocked, muted, etc.",
78 security: [%{"oAuth" => ["read:follows"]}],
84 oneOf: [%Schema{type: :array, items: %Schema{type: :string}}, %Schema{type: :string}]
91 200 => Operation.response("Account", "application/json", array_of_relationships())
98 tags: ["Retrieve account information"],
100 operationId: "AccountController.show",
101 description: "View information about a profile.",
103 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
104 with_relationships_param()
107 200 => Operation.response("Account", "application/json", Account),
108 401 => Operation.response("Error", "application/json", ApiError),
109 404 => Operation.response("Error", "application/json", ApiError)
114 def statuses_operation do
117 tags: ["Retrieve account information"],
118 operationId: "AccountController.statuses",
120 "Statuses posted to the given account. Public (for public statuses only), or user token + `read:statuses` (for private statuses the user is authorized to see)",
123 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
124 Operation.parameter(:pinned, :query, BooleanLike, "Include only pinned statuses"),
125 Operation.parameter(:tagged, :query, :string, "With tag"),
130 "Include only statuses with media attached"
136 "Include statuses from muted accounts."
138 Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"),
139 Operation.parameter(:exclude_replies, :query, BooleanLike, "Exclude replies"),
141 :exclude_visibilities,
143 %Schema{type: :array, items: VisibilityScope},
144 "Exclude visibilities"
150 "Include reactions from muted accounts."
152 ] ++ pagination_params(),
154 200 => Operation.response("Statuses", "application/json", array_of_statuses()),
155 401 => Operation.response("Error", "application/json", ApiError),
156 404 => Operation.response("Error", "application/json", ApiError)
161 def followers_operation do
163 tags: ["Retrieve account information"],
164 summary: "Followers",
165 operationId: "AccountController.followers",
166 security: [%{"oAuth" => ["read:accounts"]}],
168 "Accounts which follow the given account, if network is not hidden by the account owner.",
170 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
171 Operation.parameter(:id, :query, :string, "ID of the resource owner"),
172 with_relationships_param() | pagination_params()
175 200 => Operation.response("Accounts", "application/json", array_of_accounts())
180 def following_operation do
182 tags: ["Retrieve account information"],
183 summary: "Following",
184 operationId: "AccountController.following",
185 security: [%{"oAuth" => ["read:accounts"]}],
187 "Accounts which the given account is following, if network is not hidden by the account owner.",
189 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
190 Operation.parameter(:id, :query, :string, "ID of the resource owner"),
191 with_relationships_param() | pagination_params()
193 responses: %{200 => Operation.response("Accounts", "application/json", array_of_accounts())}
197 def lists_operation do
199 tags: ["Retrieve account information"],
200 summary: "Lists containing this account",
201 operationId: "AccountController.lists",
202 security: [%{"oAuth" => ["read:lists"]}],
203 description: "User lists that you have added this account to.",
204 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
205 responses: %{200 => Operation.response("Lists", "application/json", array_of_lists())}
209 def follow_operation do
211 tags: ["Account actions"],
213 operationId: "AccountController.follow",
214 security: [%{"oAuth" => ["follow", "write:follows"]}],
215 description: "Follow the given account",
217 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
227 description: "Receive this account's reblogs in home timeline? Defaults to true.",
233 "Receive notifications for all statuses posted by the account? Defaults to false.",
241 200 => Operation.response("Relationship", "application/json", AccountRelationship),
242 400 => Operation.response("Error", "application/json", ApiError),
243 404 => Operation.response("Error", "application/json", ApiError)
248 def unfollow_operation do
250 tags: ["Account actions"],
252 operationId: "AccountController.unfollow",
253 security: [%{"oAuth" => ["follow", "write:follows"]}],
254 description: "Unfollow the given account",
255 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
257 200 => Operation.response("Relationship", "application/json", AccountRelationship),
258 400 => Operation.response("Error", "application/json", ApiError),
259 404 => Operation.response("Error", "application/json", ApiError)
264 def mute_operation do
266 tags: ["Account actions"],
268 operationId: "AccountController.mute",
269 security: [%{"oAuth" => ["follow", "write:mutes"]}],
270 requestBody: request_body("Parameters", mute_request()),
272 "Mute the given account. Clients should filter statuses and notifications from this account, if received (e.g. due to a boost in the Home timeline).",
274 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
278 %Schema{allOf: [BooleanLike], default: true},
279 "Mute notifications in addition to statuses? Defaults to `true`."
284 %Schema{type: :integer, default: 0},
285 "Expire the mute in `expires_in` seconds. Default 0 for infinity"
289 200 => Operation.response("Relationship", "application/json", AccountRelationship)
294 def unmute_operation do
296 tags: ["Account actions"],
298 operationId: "AccountController.unmute",
299 security: [%{"oAuth" => ["follow", "write:mutes"]}],
300 description: "Unmute the given account.",
301 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
303 200 => Operation.response("Relationship", "application/json", AccountRelationship)
308 def block_operation do
310 tags: ["Account actions"],
312 operationId: "AccountController.block",
313 security: [%{"oAuth" => ["follow", "write:blocks"]}],
315 "Block the given account. Clients should filter statuses from this account if received (e.g. due to a boost in the Home timeline)",
316 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
318 200 => Operation.response("Relationship", "application/json", AccountRelationship)
323 def unblock_operation do
325 tags: ["Account actions"],
327 operationId: "AccountController.unblock",
328 security: [%{"oAuth" => ["follow", "write:blocks"]}],
329 description: "Unblock the given account.",
330 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
332 200 => Operation.response("Relationship", "application/json", AccountRelationship)
337 def note_operation do
339 tags: ["Account actions"],
340 summary: "Set a private note about a user.",
341 operationId: "AccountController.note",
342 security: [%{"oAuth" => ["follow", "write:accounts"]}],
343 requestBody: request_body("Parameters", note_request()),
344 description: "Create a note for the given account.",
346 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
350 %Schema{type: :string},
355 200 => Operation.response("Relationship", "application/json", AccountRelationship)
360 def follow_by_uri_operation do
362 tags: ["Account actions"],
363 summary: "Follow by URI",
364 operationId: "AccountController.follows",
365 security: [%{"oAuth" => ["follow", "write:follows"]}],
366 requestBody: request_body("Parameters", follow_by_uri_request(), required: true),
368 200 => Operation.response("Account", "application/json", AccountRelationship),
369 400 => Operation.response("Error", "application/json", ApiError),
370 404 => Operation.response("Error", "application/json", ApiError)
375 def mutes_operation do
377 tags: ["Blocks and mutes"],
378 summary: "Retrieve list of mutes",
379 operationId: "AccountController.mutes",
380 description: "Accounts the user has muted.",
381 security: [%{"oAuth" => ["follow", "read:mutes"]}],
382 parameters: [with_relationships_param() | pagination_params()],
384 200 => Operation.response("Accounts", "application/json", array_of_accounts())
389 def blocks_operation do
391 tags: ["Blocks and mutes"],
392 summary: "Retrieve list of blocks",
393 operationId: "AccountController.blocks",
394 description: "View your blocks. See also accounts/:id/{block,unblock}",
395 security: [%{"oAuth" => ["read:blocks"]}],
396 parameters: pagination_params(),
398 200 => Operation.response("Accounts", "application/json", array_of_accounts())
403 def endorsements_operation do
405 tags: ["Retrieve account information"],
406 summary: "Endorsements",
407 operationId: "AccountController.endorsements",
408 description: "Not implemented",
409 security: [%{"oAuth" => ["read:accounts"]}],
411 200 => empty_array_response()
416 def identity_proofs_operation do
418 tags: ["Retrieve account information"],
419 summary: "Identity proofs",
420 operationId: "AccountController.identity_proofs",
421 # Validators complains about unused path params otherwise
423 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
425 description: "Not implemented",
427 200 => empty_array_response()
432 defp create_request do
434 title: "AccountCreateRequest",
435 description: "POST body for creating an account",
437 required: [:username, :password, :agreement],
443 "Text that will be reviewed by moderators if registrations require manual approval"
445 username: %Schema{type: :string, description: "The desired username for the account"},
450 "The email address to be used for login. Required when `account_activation_required` is enabled.",
455 description: "The password to be used for login",
459 allOf: [BooleanLike],
461 "Whether the user agrees to the local rules, terms, and policies. These should be presented to the user in order to allow them to consent before setting this parameter to TRUE."
466 description: "The language of the confirmation email that will be sent"
468 # Pleroma-specific properties:
469 fullname: %Schema{type: :string, nullable: true, description: "Full name"},
470 bio: %Schema{type: :string, description: "Bio", nullable: true, default: ""},
471 captcha_solution: %Schema{
474 description: "Provider-specific captcha solution"
476 captcha_token: %Schema{
479 description: "Provider-specific captcha token"
481 captcha_answer_data: %Schema{
484 description: "Provider-specific captcha data"
489 description: "Invite token required when the registrations aren't public"
493 "username" => "cofe",
494 "email" => "cofe@example.com",
495 "password" => "secret",
496 "agreement" => "true",
502 # Note: this is a token response (if login succeeds!), but there's no oauth operation file yet.
503 defp create_response do
505 title: "AccountCreateResponse",
506 description: "Response schema for an account",
509 # The response when auto-login on create succeeds (token is issued):
510 token_type: %Schema{type: :string},
511 access_token: %Schema{type: :string},
512 refresh_token: %Schema{type: :string},
513 scope: %Schema{type: :string},
514 created_at: %Schema{type: :integer, format: :"date-time"},
515 me: %Schema{type: :string},
516 expires_in: %Schema{type: :integer},
518 # The response when registration succeeds but auto-login fails (no token):
519 identifier: %Schema{type: :string},
520 message: %Schema{type: :string}
522 # Note: example of successful registration with failed login response:
524 # "identifier" => "missing_confirmed_email",
525 # "message" => "You have been registered. Please check your email for further instructions."
528 "token_type" => "Bearer",
529 "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk",
530 "refresh_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzz",
531 "created_at" => 1_585_918_714,
533 "scope" => "read write follow push",
534 "me" => "https://gensokyo.2hu/users/raymoo"
539 defp update_credentials_request do
541 title: "AccountUpdateCredentialsRequest",
542 description: "POST body for creating an account",
546 allOf: [BooleanLike],
548 description: "Whether the account has a bot flag."
550 display_name: %Schema{
553 description: "The display name to use for the profile."
555 note: %Schema{type: :string, description: "The account bio."},
559 description: "Avatar image encoded using multipart/form-data",
565 description: "Header image encoded using multipart/form-data",
569 allOf: [BooleanLike],
571 description: "Whether manual approval of follow requests is required."
573 accepts_chat_messages: %Schema{
574 allOf: [BooleanLike],
576 description: "Whether the user accepts receiving chat messages."
578 fields_attributes: %Schema{
581 %Schema{type: :array, items: attribute_field()},
582 %Schema{type: :object, additionalProperties: attribute_field()}
585 # NOTE: `source` field is not supported
590 # privacy: %Schema{type: :string},
591 # sensitive: %Schema{type: :boolean},
592 # language: %Schema{type: :string}
596 # Pleroma-specific fields
597 no_rich_text: %Schema{
598 allOf: [BooleanLike],
600 description: "html tags are stripped from all statuses requested from the API"
602 hide_followers: %Schema{
603 allOf: [BooleanLike],
605 description: "user's followers will be hidden"
607 hide_follows: %Schema{
608 allOf: [BooleanLike],
610 description: "user's follows will be hidden"
612 hide_followers_count: %Schema{
613 allOf: [BooleanLike],
615 description: "user's follower count will be hidden"
617 hide_follows_count: %Schema{
618 allOf: [BooleanLike],
620 description: "user's follow count will be hidden"
622 hide_favorites: %Schema{
623 allOf: [BooleanLike],
625 description: "user's favorites timeline will be hidden"
628 allOf: [BooleanLike],
630 description: "user's role (e.g admin, moderator) will be exposed to anyone in the
633 default_scope: VisibilityScope,
634 pleroma_settings_store: %Schema{
637 description: "Opaque user settings to be saved on the backend."
639 skip_thread_containment: %Schema{
640 allOf: [BooleanLike],
642 description: "Skip filtering out broken threads"
644 allow_following_move: %Schema{
645 allOf: [BooleanLike],
647 description: "Allows automatically follow moved following accounts"
649 also_known_as: %Schema{
651 items: %Schema{type: :string},
653 description: "List of alternate ActivityPub IDs"
655 pleroma_background_image: %Schema{
658 description: "Sets the background image of the user.",
661 discoverable: %Schema{
662 allOf: [BooleanLike],
665 "Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed."
667 actor_type: ActorType
671 display_name: "cofe",
673 fields_attributes: [%{name: "foo", value: "bar"}],
675 hide_followers: true,
677 hide_followers_count: false,
678 hide_follows_count: false,
679 hide_favorites: false,
681 default_scope: "private",
682 pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
683 skip_thread_containment: false,
684 allow_following_move: false,
685 also_known_as: ["https://foo.bar/users/foo"],
692 def array_of_accounts do
694 title: "ArrayOfAccounts",
697 example: [Account.schema().example]
701 defp array_of_relationships do
703 title: "ArrayOfRelationships",
704 description: "Response schema for account relationships",
706 items: AccountRelationship,
711 "showing_reblogs" => true,
712 "followed_by" => true,
714 "blocked_by" => true,
716 "muting_notifications" => false,
718 "requested" => false,
719 "domain_blocking" => false,
720 "subscribing" => false,
721 "notifying" => false,
727 "showing_reblogs" => true,
728 "followed_by" => true,
730 "blocked_by" => true,
732 "muting_notifications" => false,
735 "domain_blocking" => false,
736 "subscribing" => false,
737 "notifying" => false,
743 "showing_reblogs" => true,
744 "followed_by" => true,
746 "blocked_by" => false,
748 "muting_notifications" => false,
750 "requested" => false,
751 "domain_blocking" => true,
752 "subscribing" => true,
760 defp follow_by_uri_request do
762 title: "AccountFollowsRequest",
763 description: "POST body for muting an account",
766 uri: %Schema{type: :string, nullable: true, format: :uri}
774 title: "AccountMuteRequest",
775 description: "POST body for muting an account",
778 notifications: %Schema{
779 allOf: [BooleanLike],
781 description: "Mute notifications in addition to statuses? Defaults to true.",
787 description: "Expire the mute in `expires_in` seconds. Default 0 for infinity",
792 "notifications" => true,
793 "expires_in" => 86_400
800 title: "AccountNoteRequest",
801 description: "POST body for adding a note for an account",
806 description: "Account note body"
810 "comment" => "Example note"
815 defp array_of_lists do
817 title: "ArrayOfLists",
818 description: "Response schema for lists",
822 %{"id" => "123", "title" => "my list"},
823 %{"id" => "1337", "title" => "anotehr list"}
828 defp array_of_statuses do
830 title: "ArrayOfStatuses",
836 defp attribute_field do
838 title: "AccountAttributeField",
839 description: "Request schema for account custom fields",
842 name: %Schema{type: :string},
843 value: %Schema{type: :string}
845 required: [:name, :value],
848 "value" => "https://pleroma.com"