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),
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, default: 0},
286 "Expire the mute in `expires_in` seconds. Default 0 for infinity"
290 200 => Operation.response("Relationship", "application/json", AccountRelationship)
295 def unmute_operation do
297 tags: ["Account actions"],
299 operationId: "AccountController.unmute",
300 security: [%{"oAuth" => ["follow", "write:mutes"]}],
301 description: "Unmute the given account.",
302 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
304 200 => Operation.response("Relationship", "application/json", AccountRelationship)
309 def block_operation do
311 tags: ["Account actions"],
313 operationId: "AccountController.block",
314 security: [%{"oAuth" => ["follow", "write:blocks"]}],
316 "Block the given account. Clients should filter statuses from this account if received (e.g. due to a boost in the Home timeline)",
317 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
319 200 => Operation.response("Relationship", "application/json", AccountRelationship)
324 def unblock_operation do
326 tags: ["Account actions"],
328 operationId: "AccountController.unblock",
329 security: [%{"oAuth" => ["follow", "write:blocks"]}],
330 description: "Unblock the given account.",
331 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
333 200 => Operation.response("Relationship", "application/json", AccountRelationship)
338 def remove_from_followers_operation do
340 tags: ["Account actions"],
341 summary: "Remove from followers",
342 operationId: "AccountController.remove_from_followers",
343 security: [%{"oAuth" => ["follow", "write:follows"]}],
344 description: "Remove the given account from followers",
345 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
347 200 => Operation.response("Relationship", "application/json", AccountRelationship),
348 400 => Operation.response("Error", "application/json", ApiError),
349 404 => Operation.response("Error", "application/json", ApiError)
354 def note_operation do
356 tags: ["Account actions"],
357 summary: "Set a private note about a user.",
358 operationId: "AccountController.note",
359 security: [%{"oAuth" => ["follow", "write:accounts"]}],
360 requestBody: request_body("Parameters", note_request()),
361 description: "Create a note for the given account.",
363 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
367 %Schema{type: :string},
372 200 => Operation.response("Relationship", "application/json", AccountRelationship)
377 def follow_by_uri_operation do
379 tags: ["Account actions"],
380 summary: "Follow by URI",
381 operationId: "AccountController.follows",
382 security: [%{"oAuth" => ["follow", "write:follows"]}],
383 requestBody: request_body("Parameters", follow_by_uri_request(), required: true),
385 200 => Operation.response("Account", "application/json", AccountRelationship),
386 400 => Operation.response("Error", "application/json", ApiError),
387 404 => Operation.response("Error", "application/json", ApiError)
392 def mutes_operation do
394 tags: ["Blocks and mutes"],
395 summary: "Retrieve list of mutes",
396 operationId: "AccountController.mutes",
397 description: "Accounts the user has muted.",
398 security: [%{"oAuth" => ["follow", "read:mutes"]}],
399 parameters: [with_relationships_param() | pagination_params()],
401 200 => Operation.response("Accounts", "application/json", array_of_accounts())
406 def blocks_operation do
408 tags: ["Blocks and mutes"],
409 summary: "Retrieve list of blocks",
410 operationId: "AccountController.blocks",
411 description: "View your blocks. See also accounts/:id/{block,unblock}",
412 security: [%{"oAuth" => ["read:blocks"]}],
413 parameters: pagination_params(),
415 200 => Operation.response("Accounts", "application/json", array_of_accounts())
420 def lookup_operation do
422 tags: ["Account lookup"],
423 summary: "Find a user by nickname",
424 operationId: "AccountController.lookup",
434 200 => Operation.response("Account", "application/json", Account),
435 404 => Operation.response("Error", "application/json", ApiError)
440 def endorsements_operation do
442 tags: ["Retrieve account information"],
443 summary: "Endorsements",
444 operationId: "AccountController.endorsements",
445 description: "Not implemented",
446 security: [%{"oAuth" => ["read:accounts"]}],
448 200 => empty_array_response()
453 def identity_proofs_operation do
455 tags: ["Retrieve account information"],
456 summary: "Identity proofs",
457 operationId: "AccountController.identity_proofs",
458 # Validators complains about unused path params otherwise
460 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
462 description: "Not implemented",
464 200 => empty_array_response()
469 defp create_request do
471 title: "AccountCreateRequest",
472 description: "POST body for creating an account",
474 required: [:username, :password, :agreement],
480 "Text that will be reviewed by moderators if registrations require manual approval"
482 username: %Schema{type: :string, description: "The desired username for the account"},
487 "The email address to be used for login. Required when `account_activation_required` is enabled.",
492 description: "The password to be used for login",
496 allOf: [BooleanLike],
498 "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."
503 description: "The language of the confirmation email that will be sent"
505 # Pleroma-specific properties:
506 fullname: %Schema{type: :string, nullable: true, description: "Full name"},
507 bio: %Schema{type: :string, description: "Bio", nullable: true, default: ""},
508 captcha_solution: %Schema{
511 description: "Provider-specific captcha solution"
513 captcha_token: %Schema{
516 description: "Provider-specific captcha token"
518 captcha_answer_data: %Schema{
521 description: "Provider-specific captcha data"
526 description: "Invite token required when the registrations aren't public"
531 description: "User's preferred language for emails"
535 "username" => "cofe",
536 "email" => "cofe@example.com",
537 "password" => "secret",
538 "agreement" => "true",
544 # Note: this is a token response (if login succeeds!), but there's no oauth operation file yet.
545 defp create_response do
547 title: "AccountCreateResponse",
548 description: "Response schema for an account",
551 # The response when auto-login on create succeeds (token is issued):
552 token_type: %Schema{type: :string},
553 access_token: %Schema{type: :string},
554 refresh_token: %Schema{type: :string},
555 scope: %Schema{type: :string},
556 created_at: %Schema{type: :integer, format: :"date-time"},
557 me: %Schema{type: :string},
558 expires_in: %Schema{type: :integer},
560 # The response when registration succeeds but auto-login fails (no token):
561 identifier: %Schema{type: :string},
562 message: %Schema{type: :string}
564 # Note: example of successful registration with failed login response:
566 # "identifier" => "missing_confirmed_email",
567 # "message" => "You have been registered. Please check your email for further instructions."
570 "token_type" => "Bearer",
571 "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk",
572 "refresh_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzz",
573 "created_at" => 1_585_918_714,
575 "scope" => "read write follow push",
576 "me" => "https://gensokyo.2hu/users/raymoo"
581 defp update_credentials_request do
583 title: "AccountUpdateCredentialsRequest",
584 description: "POST body for creating an account",
588 allOf: [BooleanLike],
590 description: "Whether the account has a bot flag."
592 display_name: %Schema{
595 description: "The display name to use for the profile."
597 note: %Schema{type: :string, description: "The account bio."},
601 description: "Avatar image encoded using multipart/form-data",
607 description: "Header image encoded using multipart/form-data",
611 allOf: [BooleanLike],
613 description: "Whether manual approval of follow requests is required."
615 fields_attributes: %Schema{
618 %Schema{type: :array, items: attribute_field()},
619 %Schema{type: :object, additionalProperties: attribute_field()}
622 # NOTE: `source` field is not supported
627 # privacy: %Schema{type: :string},
628 # sensitive: %Schema{type: :boolean},
629 # language: %Schema{type: :string}
633 # Pleroma-specific fields
634 no_rich_text: %Schema{
635 allOf: [BooleanLike],
637 description: "html tags are stripped from all statuses requested from the API"
639 hide_followers: %Schema{
640 allOf: [BooleanLike],
642 description: "user's followers will be hidden"
644 hide_follows: %Schema{
645 allOf: [BooleanLike],
647 description: "user's follows will be hidden"
649 hide_followers_count: %Schema{
650 allOf: [BooleanLike],
652 description: "user's follower count will be hidden"
654 hide_follows_count: %Schema{
655 allOf: [BooleanLike],
657 description: "user's follow count will be hidden"
659 hide_favorites: %Schema{
660 allOf: [BooleanLike],
662 description: "user's favorites timeline will be hidden"
665 allOf: [BooleanLike],
667 description: "user's role (e.g admin, moderator) will be exposed to anyone in the
670 default_scope: VisibilityScope,
671 pleroma_settings_store: %Schema{
674 description: "Opaque user settings to be saved on the backend."
676 skip_thread_containment: %Schema{
677 allOf: [BooleanLike],
679 description: "Skip filtering out broken threads"
681 allow_following_move: %Schema{
682 allOf: [BooleanLike],
684 description: "Allows automatically follow moved following accounts"
686 also_known_as: %Schema{
688 items: %Schema{type: :string},
690 description: "List of alternate ActivityPub IDs"
692 pleroma_background_image: %Schema{
695 description: "Sets the background image of the user.",
698 discoverable: %Schema{
699 allOf: [BooleanLike],
702 "Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed."
704 actor_type: ActorType,
705 status_ttl_days: %Schema{
709 "Number of days after which statuses will be deleted. Set to -1 to disable."
714 display_name: "cofe",
716 fields_attributes: [%{name: "foo", value: "bar"}],
718 hide_followers: true,
720 hide_followers_count: false,
721 hide_follows_count: false,
722 hide_favorites: false,
724 default_scope: "private",
725 pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
726 skip_thread_containment: false,
727 allow_following_move: false,
728 also_known_as: ["https://foo.bar/users/foo"],
730 actor_type: "Person",
736 def array_of_accounts do
738 title: "ArrayOfAccounts",
741 example: [Account.schema().example]
745 defp array_of_relationships do
747 title: "ArrayOfRelationships",
748 description: "Response schema for account relationships",
750 items: AccountRelationship,
755 "showing_reblogs" => true,
756 "followed_by" => true,
758 "blocked_by" => true,
760 "muting_notifications" => false,
762 "requested" => false,
763 "domain_blocking" => false,
764 "subscribing" => false,
765 "notifying" => false,
771 "showing_reblogs" => true,
772 "followed_by" => true,
774 "blocked_by" => true,
776 "muting_notifications" => false,
779 "domain_blocking" => false,
780 "subscribing" => false,
781 "notifying" => false,
787 "showing_reblogs" => true,
788 "followed_by" => true,
790 "blocked_by" => false,
792 "muting_notifications" => false,
794 "requested" => false,
795 "domain_blocking" => true,
796 "subscribing" => true,
804 defp follow_by_uri_request do
806 title: "AccountFollowsRequest",
807 description: "POST body for muting an account",
810 uri: %Schema{type: :string, nullable: true, format: :uri}
818 title: "AccountMuteRequest",
819 description: "POST body for muting an account",
822 notifications: %Schema{
823 allOf: [BooleanLike],
825 description: "Mute notifications in addition to statuses? Defaults to true.",
831 description: "Expire the mute in `expires_in` seconds. Default 0 for infinity",
836 "notifications" => true,
837 "expires_in" => 86_400
844 title: "AccountNoteRequest",
845 description: "POST body for adding a note for an account",
850 description: "Account note body"
854 "comment" => "Example note"
859 defp array_of_lists do
861 title: "ArrayOfLists",
862 description: "Response schema for lists",
866 %{"id" => "123", "title" => "my list"},
867 %{"id" => "1337", "title" => "anotehr list"}
872 defp array_of_statuses do
874 title: "ArrayOfStatuses",
880 defp attribute_field do
882 title: "AccountAttributeField",
883 description: "Request schema for account custom fields",
886 name: %Schema{type: :string},
887 value: %Schema{type: :string}
889 required: [:name, :value],
892 "value" => "https://pleroma.com"