1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 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),
68 413 => Operation.response("Error", "application/json", ApiError)
73 def relationships_operation do
75 tags: ["Retrieve account information"],
76 summary: "Relationship with current account",
77 operationId: "AccountController.relationships",
78 description: "Find out whether a given account is followed, blocked, muted, etc.",
79 security: [%{"oAuth" => ["read:follows"]}],
85 oneOf: [%Schema{type: :array, items: %Schema{type: :string}}, %Schema{type: :string}]
92 200 => Operation.response("Account", "application/json", array_of_relationships())
99 tags: ["Retrieve account information"],
101 operationId: "AccountController.show",
102 description: "View information about a profile.",
104 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
105 with_relationships_param()
108 200 => Operation.response("Account", "application/json", Account),
109 401 => Operation.response("Error", "application/json", ApiError),
110 404 => Operation.response("Error", "application/json", ApiError)
115 def statuses_operation do
118 tags: ["Retrieve account information"],
119 operationId: "AccountController.statuses",
121 "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)",
124 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
125 Operation.parameter(:pinned, :query, BooleanLike, "Include only pinned statuses"),
126 Operation.parameter(:tagged, :query, :string, "With tag"),
131 "Include only statuses with media attached"
137 "Include statuses from muted accounts."
139 Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"),
140 Operation.parameter(:exclude_replies, :query, BooleanLike, "Exclude replies"),
142 :exclude_visibilities,
144 %Schema{type: :array, items: VisibilityScope},
145 "Exclude visibilities"
151 "Include reactions from muted accounts."
153 ] ++ pagination_params(),
155 200 => Operation.response("Statuses", "application/json", array_of_statuses()),
156 401 => Operation.response("Error", "application/json", ApiError),
157 404 => Operation.response("Error", "application/json", ApiError)
162 def followers_operation do
164 tags: ["Retrieve account information"],
165 summary: "Followers",
166 operationId: "AccountController.followers",
167 security: [%{"oAuth" => ["read:accounts"]}],
169 "Accounts which follow the given account, if network is not hidden by the account owner.",
171 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
172 Operation.parameter(:id, :query, :string, "ID of the resource owner"),
173 with_relationships_param() | pagination_params()
176 200 => Operation.response("Accounts", "application/json", array_of_accounts())
181 def following_operation do
183 tags: ["Retrieve account information"],
184 summary: "Following",
185 operationId: "AccountController.following",
186 security: [%{"oAuth" => ["read:accounts"]}],
188 "Accounts which the given account is following, if network is not hidden by the account owner.",
190 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
191 Operation.parameter(:id, :query, :string, "ID of the resource owner"),
192 with_relationships_param() | pagination_params()
194 responses: %{200 => Operation.response("Accounts", "application/json", array_of_accounts())}
198 def lists_operation do
200 tags: ["Retrieve account information"],
201 summary: "Lists containing this account",
202 operationId: "AccountController.lists",
203 security: [%{"oAuth" => ["read:lists"]}],
204 description: "User lists that you have added this account to.",
205 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
206 responses: %{200 => Operation.response("Lists", "application/json", array_of_lists())}
210 def follow_operation do
212 tags: ["Account actions"],
214 operationId: "AccountController.follow",
215 security: [%{"oAuth" => ["follow", "write:follows"]}],
216 description: "Follow the given account",
218 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
227 allOf: [BooleanLike],
228 description: "Receive this account's reblogs in home timeline? Defaults to true.",
232 allOf: [BooleanLike],
234 "Receive notifications for all statuses posted by the account? Defaults to false.",
242 200 => Operation.response("Relationship", "application/json", AccountRelationship),
243 400 => Operation.response("Error", "application/json", ApiError),
244 404 => Operation.response("Error", "application/json", ApiError)
249 def unfollow_operation do
251 tags: ["Account actions"],
253 operationId: "AccountController.unfollow",
254 security: [%{"oAuth" => ["follow", "write:follows"]}],
255 description: "Unfollow the given account",
256 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
258 200 => Operation.response("Relationship", "application/json", AccountRelationship),
259 400 => Operation.response("Error", "application/json", ApiError),
260 404 => Operation.response("Error", "application/json", ApiError)
265 def mute_operation do
267 tags: ["Account actions"],
269 operationId: "AccountController.mute",
270 security: [%{"oAuth" => ["follow", "write:mutes"]}],
271 requestBody: request_body("Parameters", mute_request()),
273 "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).",
275 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
279 %Schema{allOf: [BooleanLike], default: true},
280 "Mute notifications in addition to statuses? Defaults to `true`."
285 %Schema{type: :integer},
286 "Expire the mute in `duration` seconds. Default 0 for infinity"
291 %Schema{type: :integer, default: 0},
292 "Deprecated, use `duration` instead"
296 200 => Operation.response("Relationship", "application/json", AccountRelationship)
301 def unmute_operation do
303 tags: ["Account actions"],
305 operationId: "AccountController.unmute",
306 security: [%{"oAuth" => ["follow", "write:mutes"]}],
307 description: "Unmute the given account.",
308 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
310 200 => Operation.response("Relationship", "application/json", AccountRelationship)
315 def block_operation do
317 tags: ["Account actions"],
319 operationId: "AccountController.block",
320 security: [%{"oAuth" => ["follow", "write:blocks"]}],
322 "Block the given account. Clients should filter statuses from this account if received (e.g. due to a boost in the Home timeline)",
323 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
325 200 => Operation.response("Relationship", "application/json", AccountRelationship)
330 def unblock_operation do
332 tags: ["Account actions"],
334 operationId: "AccountController.unblock",
335 security: [%{"oAuth" => ["follow", "write:blocks"]}],
336 description: "Unblock the given account.",
337 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
339 200 => Operation.response("Relationship", "application/json", AccountRelationship)
344 def endorse_operation do
346 tags: ["Account actions"],
348 operationId: "AccountController.endorse",
349 security: [%{"oAuth" => ["follow", "write:accounts"]}],
350 description: "Addds the given account to endorsed accounts list.",
351 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
353 200 => Operation.response("Relationship", "application/json", AccountRelationship),
355 Operation.response("Bad Request", "application/json", %Schema{
357 title: "Unprocessable Entity",
359 "error" => "You have already pinned the maximum number of users"
366 def unendorse_operation do
368 tags: ["Account actions"],
369 summary: "Unendorse",
370 operationId: "AccountController.unendorse",
371 security: [%{"oAuth" => ["follow", "write:accounts"]}],
372 description: "Removes the given account from endorsed accounts list.",
373 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
375 200 => Operation.response("Relationship", "application/json", AccountRelationship)
380 def remove_from_followers_operation do
382 tags: ["Account actions"],
383 summary: "Remove from followers",
384 operationId: "AccountController.remove_from_followers",
385 security: [%{"oAuth" => ["follow", "write:follows"]}],
386 description: "Remove the given account from followers",
387 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
389 200 => Operation.response("Relationship", "application/json", AccountRelationship),
390 400 => Operation.response("Error", "application/json", ApiError),
391 404 => Operation.response("Error", "application/json", ApiError)
396 def note_operation do
398 tags: ["Account actions"],
399 summary: "Set a private note about a user.",
400 operationId: "AccountController.note",
401 security: [%{"oAuth" => ["follow", "write:accounts"]}],
402 requestBody: request_body("Parameters", note_request()),
403 description: "Create a note for the given account.",
405 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
409 %Schema{type: :string},
414 200 => Operation.response("Relationship", "application/json", AccountRelationship)
419 def follow_by_uri_operation do
421 tags: ["Account actions"],
422 summary: "Follow by URI",
423 operationId: "AccountController.follows",
424 security: [%{"oAuth" => ["follow", "write:follows"]}],
425 requestBody: request_body("Parameters", follow_by_uri_request(), required: true),
427 200 => Operation.response("Account", "application/json", AccountRelationship),
428 400 => Operation.response("Error", "application/json", ApiError),
429 404 => Operation.response("Error", "application/json", ApiError)
434 def mutes_operation do
436 tags: ["Blocks and mutes"],
437 summary: "Retrieve list of mutes",
438 operationId: "AccountController.mutes",
439 description: "Accounts the user has muted.",
440 security: [%{"oAuth" => ["follow", "read:mutes"]}],
441 parameters: [with_relationships_param() | pagination_params()],
443 200 => Operation.response("Accounts", "application/json", array_of_accounts())
448 def blocks_operation do
450 tags: ["Blocks and mutes"],
451 summary: "Retrieve list of blocks",
452 operationId: "AccountController.blocks",
453 description: "View your blocks. See also accounts/:id/{block,unblock}",
454 security: [%{"oAuth" => ["read:blocks"]}],
455 parameters: pagination_params(),
457 200 => Operation.response("Accounts", "application/json", array_of_accounts())
462 def lookup_operation do
464 tags: ["Account lookup"],
465 summary: "Find a user by nickname",
466 operationId: "AccountController.lookup",
476 200 => Operation.response("Account", "application/json", Account),
477 404 => Operation.response("Error", "application/json", ApiError)
482 def endorsements_operation do
484 tags: ["Retrieve account information"],
485 summary: "Endorsements",
486 operationId: "AccountController.endorsements",
487 description: "Returns endorsed accounts",
488 security: [%{"oAuth" => ["read:accounts"]}],
490 200 => Operation.response("Array of Accounts", "application/json", array_of_accounts())
495 def identity_proofs_operation do
497 tags: ["Retrieve account information"],
498 summary: "Identity proofs",
499 operationId: "AccountController.identity_proofs",
500 # Validators complains about unused path params otherwise
502 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
504 description: "Not implemented",
506 200 => empty_array_response()
511 defp create_request do
513 title: "AccountCreateRequest",
514 description: "POST body for creating an account",
516 required: [:username, :password, :agreement],
522 "Text that will be reviewed by moderators if registrations require manual approval"
524 username: %Schema{type: :string, description: "The desired username for the account"},
529 "The email address to be used for login. Required when `account_activation_required` is enabled.",
534 description: "The password to be used for login",
538 allOf: [BooleanLike],
540 "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."
545 description: "The language of the confirmation email that will be sent"
547 # Pleroma-specific properties:
548 fullname: %Schema{type: :string, nullable: true, description: "Full name"},
549 bio: %Schema{type: :string, description: "Bio", nullable: true, default: ""},
550 captcha_solution: %Schema{
553 description: "Provider-specific captcha solution"
555 captcha_token: %Schema{
558 description: "Provider-specific captcha token"
560 captcha_answer_data: %Schema{
563 description: "Provider-specific captcha data"
568 description: "Invite token required when the registrations aren't public"
572 description: "User's birthday",
587 description: "User's preferred language for emails"
591 "username" => "cofe",
592 "email" => "cofe@example.com",
593 "password" => "secret",
594 "agreement" => "true",
600 # Note: this is a token response (if login succeeds!), but there's no oauth operation file yet.
601 defp create_response do
603 title: "AccountCreateResponse",
604 description: "Response schema for an account",
607 # The response when auto-login on create succeeds (token is issued):
608 token_type: %Schema{type: :string},
609 access_token: %Schema{type: :string},
610 refresh_token: %Schema{type: :string},
611 scope: %Schema{type: :string},
612 created_at: %Schema{type: :integer, format: :"date-time"},
613 me: %Schema{type: :string},
614 expires_in: %Schema{type: :integer},
616 # The response when registration succeeds but auto-login fails (no token):
617 identifier: %Schema{type: :string},
618 message: %Schema{type: :string}
620 # Note: example of successful registration with failed login response:
622 # "identifier" => "missing_confirmed_email",
623 # "message" => "You have been registered. Please check your email for further instructions."
626 "token_type" => "Bearer",
627 "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk",
628 "refresh_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzz",
629 "created_at" => 1_585_918_714,
631 "scope" => "read write follow push",
632 "me" => "https://gensokyo.2hu/users/raymoo"
637 defp update_credentials_request do
639 title: "AccountUpdateCredentialsRequest",
640 description: "POST body for creating an account",
644 allOf: [BooleanLike],
646 description: "Whether the account has a bot flag."
648 display_name: %Schema{
651 description: "The display name to use for the profile."
653 note: %Schema{type: :string, description: "The account bio."},
657 description: "Avatar image encoded using multipart/form-data",
663 description: "Header image encoded using multipart/form-data",
667 allOf: [BooleanLike],
669 description: "Whether manual approval of follow requests is required."
671 accepts_chat_messages: %Schema{
672 allOf: [BooleanLike],
674 description: "Whether the user accepts receiving chat messages."
676 fields_attributes: %Schema{
679 %Schema{type: :array, items: attribute_field()},
680 %Schema{type: :object, additionalProperties: attribute_field()}
683 # NOTE: `source` field is not supported
688 # privacy: %Schema{type: :string},
689 # sensitive: %Schema{type: :boolean},
690 # language: %Schema{type: :string}
694 # Pleroma-specific fields
695 no_rich_text: %Schema{
696 allOf: [BooleanLike],
698 description: "html tags are stripped from all statuses requested from the API"
700 hide_followers: %Schema{
701 allOf: [BooleanLike],
703 description: "user's followers will be hidden"
705 hide_follows: %Schema{
706 allOf: [BooleanLike],
708 description: "user's follows will be hidden"
710 hide_followers_count: %Schema{
711 allOf: [BooleanLike],
713 description: "user's follower count will be hidden"
715 hide_follows_count: %Schema{
716 allOf: [BooleanLike],
718 description: "user's follow count will be hidden"
720 hide_favorites: %Schema{
721 allOf: [BooleanLike],
723 description: "user's favorites timeline will be hidden"
726 allOf: [BooleanLike],
728 description: "user's role (e.g admin, moderator) will be exposed to anyone in the
731 default_scope: VisibilityScope,
732 pleroma_settings_store: %Schema{
735 description: "Opaque user settings to be saved on the backend."
737 skip_thread_containment: %Schema{
738 allOf: [BooleanLike],
740 description: "Skip filtering out broken threads"
742 allow_following_move: %Schema{
743 allOf: [BooleanLike],
745 description: "Allows automatically follow moved following accounts"
747 also_known_as: %Schema{
749 items: %Schema{type: :string},
751 description: "List of alternate ActivityPub IDs"
753 pleroma_background_image: %Schema{
756 description: "Sets the background image of the user.",
759 discoverable: %Schema{
760 allOf: [BooleanLike],
763 "Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed."
765 actor_type: ActorType,
768 description: "User's birthday",
780 show_birthday: %Schema{
781 allOf: [BooleanLike],
783 description: "User's birthday will be visible"
788 display_name: "cofe",
790 fields_attributes: [%{name: "foo", value: "bar"}],
792 hide_followers: true,
794 hide_followers_count: false,
795 hide_follows_count: false,
796 hide_favorites: false,
798 default_scope: "private",
799 pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
800 skip_thread_containment: false,
801 allow_following_move: false,
802 also_known_as: ["https://foo.bar/users/foo"],
804 actor_type: "Person",
805 show_birthday: false,
806 birthday: "2001-02-12"
811 def array_of_accounts do
813 title: "ArrayOfAccounts",
816 example: [Account.schema().example]
820 defp array_of_relationships do
822 title: "ArrayOfRelationships",
823 description: "Response schema for account relationships",
825 items: AccountRelationship,
830 "showing_reblogs" => true,
831 "followed_by" => true,
833 "blocked_by" => true,
835 "muting_notifications" => false,
837 "requested" => false,
838 "domain_blocking" => false,
839 "subscribing" => false,
840 "notifying" => false,
846 "showing_reblogs" => true,
847 "followed_by" => true,
849 "blocked_by" => true,
851 "muting_notifications" => false,
854 "domain_blocking" => false,
855 "subscribing" => false,
856 "notifying" => false,
862 "showing_reblogs" => true,
863 "followed_by" => true,
865 "blocked_by" => false,
867 "muting_notifications" => false,
869 "requested" => false,
870 "domain_blocking" => true,
871 "subscribing" => true,
879 defp follow_by_uri_request do
881 title: "AccountFollowsRequest",
882 description: "POST body for muting an account",
885 uri: %Schema{type: :string, nullable: true, format: :uri}
893 title: "AccountMuteRequest",
894 description: "POST body for muting an account",
897 notifications: %Schema{
898 allOf: [BooleanLike],
900 description: "Mute notifications in addition to statuses? Defaults to true.",
906 description: "Expire the mute in `expires_in` seconds. Default 0 for infinity"
911 description: "Deprecated, use `duration` instead",
916 "notifications" => true,
917 "expires_in" => 86_400
924 title: "AccountNoteRequest",
925 description: "POST body for adding a note for an account",
930 description: "Account note body"
934 "comment" => "Example note"
939 defp array_of_lists do
941 title: "ArrayOfLists",
942 description: "Response schema for lists",
946 %{"id" => "123", "title" => "my list"},
947 %{"id" => "1337", "title" => "anotehr list"}
952 defp array_of_statuses do
954 title: "ArrayOfStatuses",
960 defp attribute_field do
962 title: "AccountAttributeField",
963 description: "Request schema for account custom fields",
966 name: %Schema{type: :string},
967 value: %Schema{type: :string}
969 required: [:name, :value],
972 "value" => "https://pleroma.com"