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 lookup_operation do
405 tags: ["Account lookup"],
406 summary: "Find a user by nickname",
407 operationId: "AccountController.lookup",
417 200 => Operation.response("Account", "application/json", Account),
418 404 => Operation.response("Error", "application/json", ApiError)
423 def endorsements_operation do
425 tags: ["Retrieve account information"],
426 summary: "Endorsements",
427 operationId: "AccountController.endorsements",
428 description: "Not implemented",
429 security: [%{"oAuth" => ["read:accounts"]}],
431 200 => empty_array_response()
436 def identity_proofs_operation do
438 tags: ["Retrieve account information"],
439 summary: "Identity proofs",
440 operationId: "AccountController.identity_proofs",
441 # Validators complains about unused path params otherwise
443 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
445 description: "Not implemented",
447 200 => empty_array_response()
452 defp create_request do
454 title: "AccountCreateRequest",
455 description: "POST body for creating an account",
457 required: [:username, :password, :agreement],
463 "Text that will be reviewed by moderators if registrations require manual approval"
465 username: %Schema{type: :string, description: "The desired username for the account"},
470 "The email address to be used for login. Required when `account_activation_required` is enabled.",
475 description: "The password to be used for login",
479 allOf: [BooleanLike],
481 "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."
486 description: "The language of the confirmation email that will be sent"
488 # Pleroma-specific properties:
489 fullname: %Schema{type: :string, nullable: true, description: "Full name"},
490 bio: %Schema{type: :string, description: "Bio", nullable: true, default: ""},
491 captcha_solution: %Schema{
494 description: "Provider-specific captcha solution"
496 captcha_token: %Schema{
499 description: "Provider-specific captcha token"
501 captcha_answer_data: %Schema{
504 description: "Provider-specific captcha data"
509 description: "Invite token required when the registrations aren't public"
514 description: "User's preferred language for emails"
518 "username" => "cofe",
519 "email" => "cofe@example.com",
520 "password" => "secret",
521 "agreement" => "true",
527 # Note: this is a token response (if login succeeds!), but there's no oauth operation file yet.
528 defp create_response do
530 title: "AccountCreateResponse",
531 description: "Response schema for an account",
534 # The response when auto-login on create succeeds (token is issued):
535 token_type: %Schema{type: :string},
536 access_token: %Schema{type: :string},
537 refresh_token: %Schema{type: :string},
538 scope: %Schema{type: :string},
539 created_at: %Schema{type: :integer, format: :"date-time"},
540 me: %Schema{type: :string},
541 expires_in: %Schema{type: :integer},
543 # The response when registration succeeds but auto-login fails (no token):
544 identifier: %Schema{type: :string},
545 message: %Schema{type: :string}
547 # Note: example of successful registration with failed login response:
549 # "identifier" => "missing_confirmed_email",
550 # "message" => "You have been registered. Please check your email for further instructions."
553 "token_type" => "Bearer",
554 "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk",
555 "refresh_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzz",
556 "created_at" => 1_585_918_714,
558 "scope" => "read write follow push",
559 "me" => "https://gensokyo.2hu/users/raymoo"
564 defp update_credentials_request do
566 title: "AccountUpdateCredentialsRequest",
567 description: "POST body for creating an account",
571 allOf: [BooleanLike],
573 description: "Whether the account has a bot flag."
575 display_name: %Schema{
578 description: "The display name to use for the profile."
580 note: %Schema{type: :string, description: "The account bio."},
584 description: "Avatar image encoded using multipart/form-data",
590 description: "Header image encoded using multipart/form-data",
594 allOf: [BooleanLike],
596 description: "Whether manual approval of follow requests is required."
598 accepts_chat_messages: %Schema{
599 allOf: [BooleanLike],
601 description: "Whether the user accepts receiving chat messages."
603 fields_attributes: %Schema{
606 %Schema{type: :array, items: attribute_field()},
607 %Schema{type: :object, additionalProperties: attribute_field()}
610 # NOTE: `source` field is not supported
615 # privacy: %Schema{type: :string},
616 # sensitive: %Schema{type: :boolean},
617 # language: %Schema{type: :string}
621 # Pleroma-specific fields
622 no_rich_text: %Schema{
623 allOf: [BooleanLike],
625 description: "html tags are stripped from all statuses requested from the API"
627 hide_followers: %Schema{
628 allOf: [BooleanLike],
630 description: "user's followers will be hidden"
632 hide_follows: %Schema{
633 allOf: [BooleanLike],
635 description: "user's follows will be hidden"
637 hide_followers_count: %Schema{
638 allOf: [BooleanLike],
640 description: "user's follower count will be hidden"
642 hide_follows_count: %Schema{
643 allOf: [BooleanLike],
645 description: "user's follow count will be hidden"
647 hide_favorites: %Schema{
648 allOf: [BooleanLike],
650 description: "user's favorites timeline will be hidden"
653 allOf: [BooleanLike],
655 description: "user's role (e.g admin, moderator) will be exposed to anyone in the
658 default_scope: VisibilityScope,
659 pleroma_settings_store: %Schema{
662 description: "Opaque user settings to be saved on the backend."
664 skip_thread_containment: %Schema{
665 allOf: [BooleanLike],
667 description: "Skip filtering out broken threads"
669 allow_following_move: %Schema{
670 allOf: [BooleanLike],
672 description: "Allows automatically follow moved following accounts"
674 also_known_as: %Schema{
676 items: %Schema{type: :string},
678 description: "List of alternate ActivityPub IDs"
680 pleroma_background_image: %Schema{
683 description: "Sets the background image of the user.",
686 discoverable: %Schema{
687 allOf: [BooleanLike],
690 "Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed."
692 actor_type: ActorType
696 display_name: "cofe",
698 fields_attributes: [%{name: "foo", value: "bar"}],
700 hide_followers: true,
702 hide_followers_count: false,
703 hide_follows_count: false,
704 hide_favorites: false,
706 default_scope: "private",
707 pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
708 skip_thread_containment: false,
709 allow_following_move: false,
710 also_known_as: ["https://foo.bar/users/foo"],
717 def array_of_accounts do
719 title: "ArrayOfAccounts",
722 example: [Account.schema().example]
726 defp array_of_relationships do
728 title: "ArrayOfRelationships",
729 description: "Response schema for account relationships",
731 items: AccountRelationship,
736 "showing_reblogs" => true,
737 "followed_by" => true,
739 "blocked_by" => true,
741 "muting_notifications" => false,
743 "requested" => false,
744 "domain_blocking" => false,
745 "subscribing" => false,
746 "notifying" => false,
752 "showing_reblogs" => true,
753 "followed_by" => true,
755 "blocked_by" => true,
757 "muting_notifications" => false,
760 "domain_blocking" => false,
761 "subscribing" => false,
762 "notifying" => false,
768 "showing_reblogs" => true,
769 "followed_by" => true,
771 "blocked_by" => false,
773 "muting_notifications" => false,
775 "requested" => false,
776 "domain_blocking" => true,
777 "subscribing" => true,
785 defp follow_by_uri_request do
787 title: "AccountFollowsRequest",
788 description: "POST body for muting an account",
791 uri: %Schema{type: :string, nullable: true, format: :uri}
799 title: "AccountMuteRequest",
800 description: "POST body for muting an account",
803 notifications: %Schema{
804 allOf: [BooleanLike],
806 description: "Mute notifications in addition to statuses? Defaults to true.",
812 description: "Expire the mute in `expires_in` seconds. Default 0 for infinity",
817 "notifications" => true,
818 "expires_in" => 86_400
825 title: "AccountNoteRequest",
826 description: "POST body for adding a note for an account",
831 description: "Account note body"
835 "comment" => "Example note"
840 defp array_of_lists do
842 title: "ArrayOfLists",
843 description: "Response schema for lists",
847 %{"id" => "123", "title" => "my list"},
848 %{"id" => "1337", "title" => "anotehr list"}
853 defp array_of_statuses do
855 title: "ArrayOfStatuses",
861 defp attribute_field do
863 title: "AccountAttributeField",
864 description: "Request schema for account custom fields",
867 name: %Schema{type: :string},
868 value: %Schema{type: :string}
870 required: [:name, :value],
873 "value" => "https://pleroma.com"