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 401 => Operation.response("Error", "application/json", ApiError),
436 404 => Operation.response("Error", "application/json", ApiError)
441 def endorsements_operation do
443 tags: ["Retrieve account information"],
444 summary: "Endorsements",
445 operationId: "AccountController.endorsements",
446 description: "Not implemented",
447 security: [%{"oAuth" => ["read:accounts"]}],
449 200 => empty_array_response()
454 def identity_proofs_operation do
456 tags: ["Retrieve account information"],
457 summary: "Identity proofs",
458 operationId: "AccountController.identity_proofs",
459 # Validators complains about unused path params otherwise
461 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
463 description: "Not implemented",
465 200 => empty_array_response()
470 defp create_request do
472 title: "AccountCreateRequest",
473 description: "POST body for creating an account",
475 required: [:username, :password, :agreement],
481 "Text that will be reviewed by moderators if registrations require manual approval"
483 username: %Schema{type: :string, description: "The desired username for the account"},
488 "The email address to be used for login. Required when `account_activation_required` is enabled.",
493 description: "The password to be used for login",
497 allOf: [BooleanLike],
499 "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."
504 description: "The language of the confirmation email that will be sent"
506 # Pleroma-specific properties:
507 fullname: %Schema{type: :string, nullable: true, description: "Full name"},
508 bio: %Schema{type: :string, description: "Bio", nullable: true, default: ""},
509 captcha_solution: %Schema{
512 description: "Provider-specific captcha solution"
514 captcha_token: %Schema{
517 description: "Provider-specific captcha token"
519 captcha_answer_data: %Schema{
522 description: "Provider-specific captcha data"
527 description: "Invite token required when the registrations aren't public"
532 description: "User's preferred language for emails"
536 "username" => "cofe",
537 "email" => "cofe@example.com",
538 "password" => "secret",
539 "agreement" => "true",
545 # Note: this is a token response (if login succeeds!), but there's no oauth operation file yet.
546 defp create_response do
548 title: "AccountCreateResponse",
549 description: "Response schema for an account",
552 # The response when auto-login on create succeeds (token is issued):
553 token_type: %Schema{type: :string},
554 access_token: %Schema{type: :string},
555 refresh_token: %Schema{type: :string},
556 scope: %Schema{type: :string},
557 created_at: %Schema{type: :integer, format: :"date-time"},
558 me: %Schema{type: :string},
559 expires_in: %Schema{type: :integer},
561 # The response when registration succeeds but auto-login fails (no token):
562 identifier: %Schema{type: :string},
563 message: %Schema{type: :string}
565 # Note: example of successful registration with failed login response:
567 # "identifier" => "missing_confirmed_email",
568 # "message" => "You have been registered. Please check your email for further instructions."
571 "token_type" => "Bearer",
572 "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk",
573 "refresh_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzz",
574 "created_at" => 1_585_918_714,
576 "scope" => "read write follow push",
577 "me" => "https://gensokyo.2hu/users/raymoo"
582 defp update_credentials_request do
584 title: "AccountUpdateCredentialsRequest",
585 description: "POST body for creating an account",
589 allOf: [BooleanLike],
591 description: "Whether the account has a bot flag."
593 display_name: %Schema{
596 description: "The display name to use for the profile."
598 note: %Schema{type: :string, description: "The account bio."},
602 description: "Avatar image encoded using multipart/form-data",
608 description: "Header image encoded using multipart/form-data",
612 allOf: [BooleanLike],
614 description: "Whether manual approval of follow requests is required."
616 fields_attributes: %Schema{
619 %Schema{type: :array, items: attribute_field()},
620 %Schema{type: :object, additionalProperties: attribute_field()}
623 # NOTE: `source` field is not supported
628 # privacy: %Schema{type: :string},
629 # sensitive: %Schema{type: :boolean},
630 # language: %Schema{type: :string}
634 # Pleroma-specific fields
635 no_rich_text: %Schema{
636 allOf: [BooleanLike],
638 description: "html tags are stripped from all statuses requested from the API"
640 hide_followers: %Schema{
641 allOf: [BooleanLike],
643 description: "user's followers will be hidden"
645 hide_follows: %Schema{
646 allOf: [BooleanLike],
648 description: "user's follows will be hidden"
650 hide_followers_count: %Schema{
651 allOf: [BooleanLike],
653 description: "user's follower count will be hidden"
655 hide_follows_count: %Schema{
656 allOf: [BooleanLike],
658 description: "user's follow count will be hidden"
660 hide_favorites: %Schema{
661 allOf: [BooleanLike],
663 description: "user's favorites timeline will be hidden"
666 allOf: [BooleanLike],
668 description: "user's role (e.g admin, moderator) will be exposed to anyone in the
671 default_scope: VisibilityScope,
672 pleroma_settings_store: %Schema{
675 description: "Opaque user settings to be saved on the backend."
677 skip_thread_containment: %Schema{
678 allOf: [BooleanLike],
680 description: "Skip filtering out broken threads"
682 allow_following_move: %Schema{
683 allOf: [BooleanLike],
685 description: "Allows automatically follow moved following accounts"
687 also_known_as: %Schema{
689 items: %Schema{type: :string},
691 description: "List of alternate ActivityPub IDs"
693 pleroma_background_image: %Schema{
696 description: "Sets the background image of the user.",
699 discoverable: %Schema{
700 allOf: [BooleanLike],
703 "Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed."
705 actor_type: ActorType,
706 status_ttl_days: %Schema{
710 "Number of days after which statuses will be deleted. Set to -1 to disable."
715 display_name: "cofe",
717 fields_attributes: [%{name: "foo", value: "bar"}],
719 hide_followers: true,
721 hide_followers_count: false,
722 hide_follows_count: false,
723 hide_favorites: false,
725 default_scope: "private",
726 pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
727 skip_thread_containment: false,
728 allow_following_move: false,
729 also_known_as: ["https://foo.bar/users/foo"],
731 actor_type: "Person",
737 def array_of_accounts do
739 title: "ArrayOfAccounts",
742 example: [Account.schema().example]
746 defp array_of_relationships do
748 title: "ArrayOfRelationships",
749 description: "Response schema for account relationships",
751 items: AccountRelationship,
756 "showing_reblogs" => true,
757 "followed_by" => true,
759 "blocked_by" => true,
761 "muting_notifications" => false,
763 "requested" => false,
764 "domain_blocking" => false,
765 "subscribing" => false,
766 "notifying" => false,
772 "showing_reblogs" => true,
773 "followed_by" => true,
775 "blocked_by" => true,
777 "muting_notifications" => false,
780 "domain_blocking" => false,
781 "subscribing" => false,
782 "notifying" => false,
788 "showing_reblogs" => true,
789 "followed_by" => true,
791 "blocked_by" => false,
793 "muting_notifications" => false,
795 "requested" => false,
796 "domain_blocking" => true,
797 "subscribing" => true,
805 defp follow_by_uri_request do
807 title: "AccountFollowsRequest",
808 description: "POST body for muting an account",
811 uri: %Schema{type: :string, nullable: true, format: :uri}
819 title: "AccountMuteRequest",
820 description: "POST body for muting an account",
823 notifications: %Schema{
824 allOf: [BooleanLike],
826 description: "Mute notifications in addition to statuses? Defaults to true.",
832 description: "Expire the mute in `expires_in` seconds. Default 0 for infinity",
837 "notifications" => true,
838 "expires_in" => 86_400
845 title: "AccountNoteRequest",
846 description: "POST body for adding a note for an account",
851 description: "Account note body"
855 "comment" => "Example note"
860 defp array_of_lists do
862 title: "ArrayOfLists",
863 description: "Response schema for lists",
867 %{"id" => "123", "title" => "my list"},
868 %{"id" => "1337", "title" => "anotehr list"}
873 defp array_of_statuses do
875 title: "ArrayOfStatuses",
881 defp attribute_field do
883 title: "AccountAttributeField",
884 description: "Request schema for account custom fields",
887 name: %Schema{type: :string},
888 value: %Schema{type: :string}
890 required: [:name, :value],
893 "value" => "https://pleroma.com"