1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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
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
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
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
75 summary: "Check relationships to other accounts",
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())
100 operationId: "AccountController.show",
101 description: "View information about a profile.",
102 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
104 200 => Operation.response("Account", "application/json", Account),
105 401 => Operation.response("Error", "application/json", ApiError),
106 404 => Operation.response("Error", "application/json", ApiError)
111 def statuses_operation do
115 operationId: "AccountController.statuses",
117 "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)",
120 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
121 Operation.parameter(:pinned, :query, BooleanLike, "Include only pinned statuses"),
122 Operation.parameter(:tagged, :query, :string, "With tag"),
127 "Include only statuses with media attached"
133 "Include statuses from muted acccounts."
135 Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"),
136 Operation.parameter(:exclude_replies, :query, BooleanLike, "Exclude replies"),
138 :exclude_visibilities,
140 %Schema{type: :array, items: VisibilityScope},
141 "Exclude visibilities"
143 ] ++ pagination_params(),
145 200 => Operation.response("Statuses", "application/json", array_of_statuses()),
146 401 => Operation.response("Error", "application/json", ApiError),
147 404 => Operation.response("Error", "application/json", ApiError)
152 def followers_operation do
155 summary: "Followers",
156 operationId: "AccountController.followers",
157 security: [%{"oAuth" => ["read:accounts"]}],
159 "Accounts which follow the given account, if network is not hidden by the account owner.",
161 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
162 with_relationships_param() | pagination_params()
165 200 => Operation.response("Accounts", "application/json", array_of_accounts())
170 def following_operation do
173 summary: "Following",
174 operationId: "AccountController.following",
175 security: [%{"oAuth" => ["read:accounts"]}],
177 "Accounts which the given account is following, if network is not hidden by the account owner.",
179 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
180 with_relationships_param() | pagination_params()
182 responses: %{200 => Operation.response("Accounts", "application/json", array_of_accounts())}
186 def lists_operation do
189 summary: "Lists containing this account",
190 operationId: "AccountController.lists",
191 security: [%{"oAuth" => ["read:lists"]}],
192 description: "User lists that you have added this account to.",
193 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
194 responses: %{200 => Operation.response("Lists", "application/json", array_of_lists())}
198 def follow_operation do
202 operationId: "AccountController.follow",
203 security: [%{"oAuth" => ["follow", "write:follows"]}],
204 description: "Follow the given account",
206 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
216 description: "Receive this account's reblogs in home timeline? Defaults to true.",
224 200 => Operation.response("Relationship", "application/json", AccountRelationship),
225 400 => Operation.response("Error", "application/json", ApiError),
226 404 => Operation.response("Error", "application/json", ApiError)
231 def unfollow_operation do
235 operationId: "AccountController.unfollow",
236 security: [%{"oAuth" => ["follow", "write:follows"]}],
237 description: "Unfollow the given account",
238 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
240 200 => Operation.response("Relationship", "application/json", AccountRelationship),
241 400 => Operation.response("Error", "application/json", ApiError),
242 404 => Operation.response("Error", "application/json", ApiError)
247 def mute_operation do
251 operationId: "AccountController.mute",
252 security: [%{"oAuth" => ["follow", "write:mutes"]}],
253 requestBody: request_body("Parameters", mute_request()),
255 "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).",
257 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
261 %Schema{allOf: [BooleanLike], default: true},
262 "Mute notifications in addition to statuses? Defaults to `true`."
266 200 => Operation.response("Relationship", "application/json", AccountRelationship)
271 def unmute_operation do
275 operationId: "AccountController.unmute",
276 security: [%{"oAuth" => ["follow", "write:mutes"]}],
277 description: "Unmute the given account.",
278 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
280 200 => Operation.response("Relationship", "application/json", AccountRelationship)
285 def block_operation do
289 operationId: "AccountController.block",
290 security: [%{"oAuth" => ["follow", "write:blocks"]}],
292 "Block the given account. Clients should filter statuses from this account if received (e.g. due to a boost in the Home timeline)",
293 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
295 200 => Operation.response("Relationship", "application/json", AccountRelationship)
300 def unblock_operation do
304 operationId: "AccountController.unblock",
305 security: [%{"oAuth" => ["follow", "write:blocks"]}],
306 description: "Unblock the given account.",
307 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
309 200 => Operation.response("Relationship", "application/json", AccountRelationship)
314 def follow_by_uri_operation do
317 summary: "Follow by URI",
318 operationId: "AccountController.follows",
319 security: [%{"oAuth" => ["follow", "write:follows"]}],
320 requestBody: request_body("Parameters", follow_by_uri_request(), required: true),
322 200 => Operation.response("Account", "application/json", AccountRelationship),
323 400 => Operation.response("Error", "application/json", ApiError),
324 404 => Operation.response("Error", "application/json", ApiError)
329 def mutes_operation do
332 summary: "Muted accounts",
333 operationId: "AccountController.mutes",
334 description: "Accounts the user has muted.",
335 security: [%{"oAuth" => ["follow", "read:mutes"]}],
337 200 => Operation.response("Accounts", "application/json", array_of_accounts())
342 def blocks_operation do
345 summary: "Blocked users",
346 operationId: "AccountController.blocks",
347 description: "View your blocks. See also accounts/:id/{block,unblock}",
348 security: [%{"oAuth" => ["read:blocks"]}],
350 200 => Operation.response("Accounts", "application/json", array_of_accounts())
355 def endorsements_operation do
358 summary: "Endorsements",
359 operationId: "AccountController.endorsements",
360 description: "Not implemented",
361 security: [%{"oAuth" => ["read:accounts"]}],
363 200 => empty_array_response()
368 def identity_proofs_operation do
371 summary: "Identity proofs",
372 operationId: "AccountController.identity_proofs",
373 description: "Not implemented",
375 200 => empty_array_response()
380 defp create_request do
382 title: "AccountCreateRequest",
383 description: "POST body for creating an account",
385 required: [:username, :password, :agreement],
391 "Text that will be reviewed by moderators if registrations require manual approval"
393 username: %Schema{type: :string, description: "The desired username for the account"},
398 "The email address to be used for login. Required when `account_activation_required` is enabled.",
403 description: "The password to be used for login",
407 allOf: [BooleanLike],
409 "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."
414 description: "The language of the confirmation email that will be sent"
416 # Pleroma-specific properties:
417 fullname: %Schema{type: :string, nullable: true, description: "Full name"},
418 bio: %Schema{type: :string, description: "Bio", nullable: true, default: ""},
419 captcha_solution: %Schema{
422 description: "Provider-specific captcha solution"
424 captcha_token: %Schema{
427 description: "Provider-specific captcha token"
429 captcha_answer_data: %Schema{
432 description: "Provider-specific captcha data"
437 description: "Invite token required when the registrations aren't public"
441 "username" => "cofe",
442 "email" => "cofe@example.com",
443 "password" => "secret",
444 "agreement" => "true",
450 # TODO: This is actually a token respone, but there's no oauth operation file yet.
451 defp create_response do
453 title: "AccountCreateResponse",
454 description: "Response schema for an account",
457 token_type: %Schema{type: :string},
458 access_token: %Schema{type: :string},
459 refresh_token: %Schema{type: :string},
460 scope: %Schema{type: :string},
461 created_at: %Schema{type: :integer, format: :"date-time"},
462 me: %Schema{type: :string},
463 expires_in: %Schema{type: :integer}
466 "token_type" => "Bearer",
467 "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk",
468 "refresh_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzz",
469 "created_at" => 1_585_918_714,
471 "scope" => "read write follow push",
472 "me" => "https://gensokyo.2hu/users/raymoo"
477 defp update_credentials_request do
479 title: "AccountUpdateCredentialsRequest",
480 description: "POST body for creating an account",
484 allOf: [BooleanLike],
486 description: "Whether the account has a bot flag."
488 display_name: %Schema{
491 description: "The display name to use for the profile."
493 note: %Schema{type: :string, description: "The account bio."},
497 description: "Avatar image encoded using multipart/form-data",
503 description: "Header image encoded using multipart/form-data",
507 allOf: [BooleanLike],
509 description: "Whether manual approval of follow requests is required."
511 accepts_chat_messages: %Schema{
512 allOf: [BooleanLike],
514 description: "Whether the user accepts receiving chat messages."
516 fields_attributes: %Schema{
519 %Schema{type: :array, items: attribute_field()},
520 %Schema{type: :object, additionalProperties: %Schema{type: attribute_field()}}
523 # NOTE: `source` field is not supported
528 # privacy: %Schema{type: :string},
529 # sensitive: %Schema{type: :boolean},
530 # language: %Schema{type: :string}
534 # Pleroma-specific fields
535 no_rich_text: %Schema{
536 allOf: [BooleanLike],
538 description: "html tags are stripped from all statuses requested from the API"
540 hide_followers: %Schema{
541 allOf: [BooleanLike],
543 description: "user's followers will be hidden"
545 hide_follows: %Schema{
546 allOf: [BooleanLike],
548 description: "user's follows will be hidden"
550 hide_followers_count: %Schema{
551 allOf: [BooleanLike],
553 description: "user's follower count will be hidden"
555 hide_follows_count: %Schema{
556 allOf: [BooleanLike],
558 description: "user's follow count will be hidden"
560 hide_favorites: %Schema{
561 allOf: [BooleanLike],
563 description: "user's favorites timeline will be hidden"
566 allOf: [BooleanLike],
568 description: "user's role (e.g admin, moderator) will be exposed to anyone in the
571 default_scope: VisibilityScope,
572 pleroma_settings_store: %Schema{
575 description: "Opaque user settings to be saved on the backend."
577 skip_thread_containment: %Schema{
578 allOf: [BooleanLike],
580 description: "Skip filtering out broken threads"
582 allow_following_move: %Schema{
583 allOf: [BooleanLike],
585 description: "Allows automatically follow moved following accounts"
587 pleroma_background_image: %Schema{
590 description: "Sets the background image of the user.",
593 discoverable: %Schema{
594 allOf: [BooleanLike],
597 "Discovery of this account in search results and other services is allowed."
599 actor_type: ActorType
603 display_name: "cofe",
605 fields_attributes: [%{name: "foo", value: "bar"}],
607 hide_followers: true,
609 hide_followers_count: false,
610 hide_follows_count: false,
611 hide_favorites: false,
613 default_scope: "private",
614 pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
615 skip_thread_containment: false,
616 allow_following_move: false,
623 def array_of_accounts do
625 title: "ArrayOfAccounts",
628 example: [Account.schema().example]
632 defp array_of_relationships do
634 title: "ArrayOfRelationships",
635 description: "Response schema for account relationships",
637 items: AccountRelationship,
642 "showing_reblogs" => true,
643 "followed_by" => true,
645 "blocked_by" => true,
647 "muting_notifications" => false,
648 "requested" => false,
649 "domain_blocking" => false,
650 "subscribing" => false,
656 "showing_reblogs" => true,
657 "followed_by" => true,
659 "blocked_by" => true,
661 "muting_notifications" => false,
663 "domain_blocking" => false,
664 "subscribing" => false,
670 "showing_reblogs" => true,
671 "followed_by" => true,
673 "blocked_by" => false,
675 "muting_notifications" => false,
676 "requested" => false,
677 "domain_blocking" => true,
678 "subscribing" => true,
685 defp follow_by_uri_request do
687 title: "AccountFollowsRequest",
688 description: "POST body for muting an account",
691 uri: %Schema{type: :string, nullable: true, format: :uri}
699 title: "AccountMuteRequest",
700 description: "POST body for muting an account",
703 notifications: %Schema{
704 allOf: [BooleanLike],
706 description: "Mute notifications in addition to statuses? Defaults to true.",
711 "notifications" => true
716 defp array_of_lists do
718 title: "ArrayOfLists",
719 description: "Response schema for lists",
723 %{"id" => "123", "title" => "my list"},
724 %{"id" => "1337", "title" => "anotehr list"}
729 defp array_of_statuses do
731 title: "ArrayOfStatuses",
737 defp attribute_field do
739 title: "AccountAttributeField",
740 description: "Request schema for account custom fields",
743 name: %Schema{type: :string},
744 value: %Schema{type: :string}
746 required: [:name, :value],
749 "value" => "https://pleroma.com"