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.",
235 200 => Operation.response("Relationship", "application/json", AccountRelationship),
236 400 => Operation.response("Error", "application/json", ApiError),
237 404 => Operation.response("Error", "application/json", ApiError)
242 def unfollow_operation do
244 tags: ["Account actions"],
246 operationId: "AccountController.unfollow",
247 security: [%{"oAuth" => ["follow", "write:follows"]}],
248 description: "Unfollow the given account",
249 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
251 200 => Operation.response("Relationship", "application/json", AccountRelationship),
252 400 => Operation.response("Error", "application/json", ApiError),
253 404 => Operation.response("Error", "application/json", ApiError)
258 def mute_operation do
260 tags: ["Account actions"],
262 operationId: "AccountController.mute",
263 security: [%{"oAuth" => ["follow", "write:mutes"]}],
264 requestBody: request_body("Parameters", mute_request()),
266 "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).",
268 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
272 %Schema{allOf: [BooleanLike], default: true},
273 "Mute notifications in addition to statuses? Defaults to `true`."
278 %Schema{type: :integer, default: 0},
279 "Expire the mute in `expires_in` seconds. Default 0 for infinity"
283 200 => Operation.response("Relationship", "application/json", AccountRelationship)
288 def unmute_operation do
290 tags: ["Account actions"],
292 operationId: "AccountController.unmute",
293 security: [%{"oAuth" => ["follow", "write:mutes"]}],
294 description: "Unmute the given account.",
295 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
297 200 => Operation.response("Relationship", "application/json", AccountRelationship)
302 def block_operation do
304 tags: ["Account actions"],
306 operationId: "AccountController.block",
307 security: [%{"oAuth" => ["follow", "write:blocks"]}],
309 "Block the given account. Clients should filter statuses from this account if received (e.g. due to a boost in the Home timeline)",
310 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
312 200 => Operation.response("Relationship", "application/json", AccountRelationship)
317 def unblock_operation do
319 tags: ["Account actions"],
321 operationId: "AccountController.unblock",
322 security: [%{"oAuth" => ["follow", "write:blocks"]}],
323 description: "Unblock the given account.",
324 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
326 200 => Operation.response("Relationship", "application/json", AccountRelationship)
331 def follow_by_uri_operation do
333 tags: ["Account actions"],
334 summary: "Follow by URI",
335 operationId: "AccountController.follows",
336 security: [%{"oAuth" => ["follow", "write:follows"]}],
337 requestBody: request_body("Parameters", follow_by_uri_request(), required: true),
339 200 => Operation.response("Account", "application/json", AccountRelationship),
340 400 => Operation.response("Error", "application/json", ApiError),
341 404 => Operation.response("Error", "application/json", ApiError)
346 def mutes_operation do
348 tags: ["Blocks and mutes"],
349 summary: "Retrieve list of mutes",
350 operationId: "AccountController.mutes",
351 description: "Accounts the user has muted.",
352 security: [%{"oAuth" => ["follow", "read:mutes"]}],
353 parameters: [with_relationships_param() | pagination_params()],
355 200 => Operation.response("Accounts", "application/json", array_of_accounts())
360 def blocks_operation do
362 tags: ["Blocks and mutes"],
363 summary: "Retrieve list of blocks",
364 operationId: "AccountController.blocks",
365 description: "View your blocks. See also accounts/:id/{block,unblock}",
366 security: [%{"oAuth" => ["read:blocks"]}],
367 parameters: pagination_params(),
369 200 => Operation.response("Accounts", "application/json", array_of_accounts())
374 def lookup_operation do
376 tags: ["Account lookup"],
377 summary: "Find a user by nickname",
378 operationId: "AccountController.lookup",
388 200 => Operation.response("Account", "application/json", Account),
389 404 => Operation.response("Error", "application/json", ApiError)
394 def endorsements_operation do
396 tags: ["Retrieve account information"],
397 summary: "Endorsements",
398 operationId: "AccountController.endorsements",
399 description: "Not implemented",
400 security: [%{"oAuth" => ["read:accounts"]}],
402 200 => empty_array_response()
407 def identity_proofs_operation do
409 tags: ["Retrieve account information"],
410 summary: "Identity proofs",
411 operationId: "AccountController.identity_proofs",
412 # Validators complains about unused path params otherwise
414 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
416 description: "Not implemented",
418 200 => empty_array_response()
423 defp create_request do
425 title: "AccountCreateRequest",
426 description: "POST body for creating an account",
428 required: [:username, :password, :agreement],
434 "Text that will be reviewed by moderators if registrations require manual approval"
436 username: %Schema{type: :string, description: "The desired username for the account"},
441 "The email address to be used for login. Required when `account_activation_required` is enabled.",
446 description: "The password to be used for login",
450 allOf: [BooleanLike],
452 "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."
457 description: "The language of the confirmation email that will be sent"
459 # Pleroma-specific properties:
460 fullname: %Schema{type: :string, nullable: true, description: "Full name"},
461 bio: %Schema{type: :string, description: "Bio", nullable: true, default: ""},
462 captcha_solution: %Schema{
465 description: "Provider-specific captcha solution"
467 captcha_token: %Schema{
470 description: "Provider-specific captcha token"
472 captcha_answer_data: %Schema{
475 description: "Provider-specific captcha data"
480 description: "Invite token required when the registrations aren't public"
484 "username" => "cofe",
485 "email" => "cofe@example.com",
486 "password" => "secret",
487 "agreement" => "true",
493 # Note: this is a token response (if login succeeds!), but there's no oauth operation file yet.
494 defp create_response do
496 title: "AccountCreateResponse",
497 description: "Response schema for an account",
500 # The response when auto-login on create succeeds (token is issued):
501 token_type: %Schema{type: :string},
502 access_token: %Schema{type: :string},
503 refresh_token: %Schema{type: :string},
504 scope: %Schema{type: :string},
505 created_at: %Schema{type: :integer, format: :"date-time"},
506 me: %Schema{type: :string},
507 expires_in: %Schema{type: :integer},
509 # The response when registration succeeds but auto-login fails (no token):
510 identifier: %Schema{type: :string},
511 message: %Schema{type: :string}
513 # Note: example of successful registration with failed login response:
515 # "identifier" => "missing_confirmed_email",
516 # "message" => "You have been registered. Please check your email for further instructions."
519 "token_type" => "Bearer",
520 "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk",
521 "refresh_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzz",
522 "created_at" => 1_585_918_714,
524 "scope" => "read write follow push",
525 "me" => "https://gensokyo.2hu/users/raymoo"
530 defp update_credentials_request do
532 title: "AccountUpdateCredentialsRequest",
533 description: "POST body for creating an account",
537 allOf: [BooleanLike],
539 description: "Whether the account has a bot flag."
541 display_name: %Schema{
544 description: "The display name to use for the profile."
546 note: %Schema{type: :string, description: "The account bio."},
550 description: "Avatar image encoded using multipart/form-data",
556 description: "Header image encoded using multipart/form-data",
560 allOf: [BooleanLike],
562 description: "Whether manual approval of follow requests is required."
564 accepts_chat_messages: %Schema{
565 allOf: [BooleanLike],
567 description: "Whether the user accepts receiving chat messages."
569 fields_attributes: %Schema{
572 %Schema{type: :array, items: attribute_field()},
573 %Schema{type: :object, additionalProperties: attribute_field()}
576 # NOTE: `source` field is not supported
581 # privacy: %Schema{type: :string},
582 # sensitive: %Schema{type: :boolean},
583 # language: %Schema{type: :string}
587 # Pleroma-specific fields
588 no_rich_text: %Schema{
589 allOf: [BooleanLike],
591 description: "html tags are stripped from all statuses requested from the API"
593 hide_followers: %Schema{
594 allOf: [BooleanLike],
596 description: "user's followers will be hidden"
598 hide_follows: %Schema{
599 allOf: [BooleanLike],
601 description: "user's follows will be hidden"
603 hide_followers_count: %Schema{
604 allOf: [BooleanLike],
606 description: "user's follower count will be hidden"
608 hide_follows_count: %Schema{
609 allOf: [BooleanLike],
611 description: "user's follow count will be hidden"
613 hide_favorites: %Schema{
614 allOf: [BooleanLike],
616 description: "user's favorites timeline will be hidden"
619 allOf: [BooleanLike],
621 description: "user's role (e.g admin, moderator) will be exposed to anyone in the
624 default_scope: VisibilityScope,
625 pleroma_settings_store: %Schema{
628 description: "Opaque user settings to be saved on the backend."
630 skip_thread_containment: %Schema{
631 allOf: [BooleanLike],
633 description: "Skip filtering out broken threads"
635 allow_following_move: %Schema{
636 allOf: [BooleanLike],
638 description: "Allows automatically follow moved following accounts"
640 also_known_as: %Schema{
642 items: %Schema{type: :string},
644 description: "List of alternate ActivityPub IDs"
646 pleroma_background_image: %Schema{
649 description: "Sets the background image of the user.",
652 discoverable: %Schema{
653 allOf: [BooleanLike],
656 "Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed."
658 actor_type: ActorType
662 display_name: "cofe",
664 fields_attributes: [%{name: "foo", value: "bar"}],
666 hide_followers: true,
668 hide_followers_count: false,
669 hide_follows_count: false,
670 hide_favorites: false,
672 default_scope: "private",
673 pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
674 skip_thread_containment: false,
675 allow_following_move: false,
676 also_known_as: ["https://foo.bar/users/foo"],
683 def array_of_accounts do
685 title: "ArrayOfAccounts",
688 example: [Account.schema().example]
692 defp array_of_relationships do
694 title: "ArrayOfRelationships",
695 description: "Response schema for account relationships",
697 items: AccountRelationship,
702 "showing_reblogs" => true,
703 "followed_by" => true,
705 "blocked_by" => true,
707 "muting_notifications" => false,
708 "requested" => false,
709 "domain_blocking" => false,
710 "subscribing" => false,
716 "showing_reblogs" => true,
717 "followed_by" => true,
719 "blocked_by" => true,
721 "muting_notifications" => false,
723 "domain_blocking" => false,
724 "subscribing" => false,
730 "showing_reblogs" => true,
731 "followed_by" => true,
733 "blocked_by" => false,
735 "muting_notifications" => false,
736 "requested" => false,
737 "domain_blocking" => true,
738 "subscribing" => true,
745 defp follow_by_uri_request do
747 title: "AccountFollowsRequest",
748 description: "POST body for muting an account",
751 uri: %Schema{type: :string, nullable: true, format: :uri}
759 title: "AccountMuteRequest",
760 description: "POST body for muting an account",
763 notifications: %Schema{
764 allOf: [BooleanLike],
766 description: "Mute notifications in addition to statuses? Defaults to true.",
772 description: "Expire the mute in `expires_in` seconds. Default 0 for infinity",
777 "notifications" => true,
778 "expires_in" => 86_400
783 defp array_of_lists do
785 title: "ArrayOfLists",
786 description: "Response schema for lists",
790 %{"id" => "123", "title" => "my list"},
791 %{"id" => "1337", "title" => "anotehr list"}
796 defp array_of_statuses do
798 title: "ArrayOfStatuses",
804 defp attribute_field do
806 title: "AccountAttributeField",
807 description: "Request schema for account custom fields",
810 name: %Schema{type: :string},
811 value: %Schema{type: :string}
813 required: [:name, :value],
816 "value" => "https://pleroma.com"