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_creadentials_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),
107 410 => Operation.response("Error", "application/json", ApiError)
112 def statuses_operation do
116 operationId: "AccountController.statuses",
118 "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)",
121 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
122 Operation.parameter(:pinned, :query, BooleanLike, "Include only pinned statuses"),
123 Operation.parameter(:tagged, :query, :string, "With tag"),
128 "Include only statuses with media attached"
134 "Include statuses from muted acccounts."
136 Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"),
137 Operation.parameter(:exclude_replies, :query, BooleanLike, "Exclude replies"),
139 :exclude_visibilities,
141 %Schema{type: :array, items: VisibilityScope},
142 "Exclude visibilities"
144 ] ++ pagination_params(),
146 200 => Operation.response("Statuses", "application/json", array_of_statuses()),
147 401 => Operation.response("Error", "application/json", ApiError),
148 404 => Operation.response("Error", "application/json", ApiError),
149 410 => Operation.response("Error", "application/json", ApiError)
154 def followers_operation do
157 summary: "Followers",
158 operationId: "AccountController.followers",
159 security: [%{"oAuth" => ["read:accounts"]}],
161 "Accounts which follow the given account, if network is not hidden by the account owner.",
163 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
164 with_relationships_param() | pagination_params()
167 200 => Operation.response("Accounts", "application/json", array_of_accounts())
172 def following_operation do
175 summary: "Following",
176 operationId: "AccountController.following",
177 security: [%{"oAuth" => ["read:accounts"]}],
179 "Accounts which the given account is following, if network is not hidden by the account owner.",
181 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
182 with_relationships_param() | pagination_params()
184 responses: %{200 => Operation.response("Accounts", "application/json", array_of_accounts())}
188 def lists_operation do
191 summary: "Lists containing this account",
192 operationId: "AccountController.lists",
193 security: [%{"oAuth" => ["read:lists"]}],
194 description: "User lists that you have added this account to.",
195 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
196 responses: %{200 => Operation.response("Lists", "application/json", array_of_lists())}
200 def follow_operation do
204 operationId: "AccountController.follow",
205 security: [%{"oAuth" => ["follow", "write:follows"]}],
206 description: "Follow the given account",
208 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
213 "Receive this account's reblogs in home timeline? Defaults to true."
217 200 => Operation.response("Relationship", "application/json", AccountRelationship),
218 400 => Operation.response("Error", "application/json", ApiError),
219 404 => Operation.response("Error", "application/json", ApiError)
224 def unfollow_operation do
228 operationId: "AccountController.unfollow",
229 security: [%{"oAuth" => ["follow", "write:follows"]}],
230 description: "Unfollow the given account",
231 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
233 200 => Operation.response("Relationship", "application/json", AccountRelationship),
234 400 => Operation.response("Error", "application/json", ApiError),
235 404 => Operation.response("Error", "application/json", ApiError)
240 def mute_operation do
244 operationId: "AccountController.mute",
245 security: [%{"oAuth" => ["follow", "write:mutes"]}],
246 requestBody: request_body("Parameters", mute_request()),
248 "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).",
250 %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
254 %Schema{allOf: [BooleanLike], default: true},
255 "Mute notifications in addition to statuses? Defaults to `true`."
259 200 => Operation.response("Relationship", "application/json", AccountRelationship)
264 def unmute_operation do
268 operationId: "AccountController.unmute",
269 security: [%{"oAuth" => ["follow", "write:mutes"]}],
270 description: "Unmute the given account.",
271 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
273 200 => Operation.response("Relationship", "application/json", AccountRelationship)
278 def block_operation do
282 operationId: "AccountController.block",
283 security: [%{"oAuth" => ["follow", "write:blocks"]}],
285 "Block the given account. Clients should filter statuses from this account if received (e.g. due to a boost in the Home timeline)",
286 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
288 200 => Operation.response("Relationship", "application/json", AccountRelationship)
293 def unblock_operation do
297 operationId: "AccountController.unblock",
298 security: [%{"oAuth" => ["follow", "write:blocks"]}],
299 description: "Unblock the given account.",
300 parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
302 200 => Operation.response("Relationship", "application/json", AccountRelationship)
307 def follow_by_uri_operation do
310 summary: "Follow by URI",
311 operationId: "AccountController.follows",
312 security: [%{"oAuth" => ["follow", "write:follows"]}],
313 requestBody: request_body("Parameters", follow_by_uri_request(), required: true),
315 200 => Operation.response("Account", "application/json", AccountRelationship),
316 400 => Operation.response("Error", "application/json", ApiError),
317 404 => Operation.response("Error", "application/json", ApiError)
322 def mutes_operation do
325 summary: "Muted accounts",
326 operationId: "AccountController.mutes",
327 description: "Accounts the user has muted.",
328 security: [%{"oAuth" => ["follow", "read:mutes"]}],
330 200 => Operation.response("Accounts", "application/json", array_of_accounts())
335 def blocks_operation do
338 summary: "Blocked users",
339 operationId: "AccountController.blocks",
340 description: "View your blocks. See also accounts/:id/{block,unblock}",
341 security: [%{"oAuth" => ["read:blocks"]}],
343 200 => Operation.response("Accounts", "application/json", array_of_accounts())
348 def endorsements_operation do
351 summary: "Endorsements",
352 operationId: "AccountController.endorsements",
353 description: "Not implemented",
354 security: [%{"oAuth" => ["read:accounts"]}],
356 200 => empty_array_response()
361 def identity_proofs_operation do
364 summary: "Identity proofs",
365 operationId: "AccountController.identity_proofs",
366 description: "Not implemented",
368 200 => empty_array_response()
373 defp create_request do
375 title: "AccountCreateRequest",
376 description: "POST body for creating an account",
378 required: [:username, :password, :agreement],
384 "Text that will be reviewed by moderators if registrations require manual approval"
386 username: %Schema{type: :string, description: "The desired username for the account"},
391 "The email address to be used for login. Required when `account_activation_required` is enabled.",
396 description: "The password to be used for login",
402 "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."
407 description: "The language of the confirmation email that will be sent"
409 # Pleroma-specific properties:
410 fullname: %Schema{type: :string, nullable: true, description: "Full name"},
411 bio: %Schema{type: :string, description: "Bio", nullable: true, default: ""},
412 captcha_solution: %Schema{
415 description: "Provider-specific captcha solution"
417 captcha_token: %Schema{
420 description: "Provider-specific captcha token"
422 captcha_answer_data: %Schema{
425 description: "Provider-specific captcha data"
430 description: "Invite token required when the registrations aren't public"
434 "username" => "cofe",
435 "email" => "cofe@example.com",
436 "password" => "secret",
437 "agreement" => "true",
443 defp create_response do
445 title: "AccountCreateResponse",
446 description: "Response schema for an account",
449 token_type: %Schema{type: :string},
450 access_token: %Schema{type: :string},
451 scope: %Schema{type: :array, items: %Schema{type: :string}},
452 created_at: %Schema{type: :integer, format: :"date-time"}
455 "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk",
456 "created_at" => 1_585_918_714,
457 "scope" => ["read", "write", "follow", "push"],
458 "token_type" => "Bearer"
463 defp update_creadentials_request do
465 title: "AccountUpdateCredentialsRequest",
466 description: "POST body for creating an account",
472 description: "Whether the account has a bot flag."
474 display_name: %Schema{
477 description: "The display name to use for the profile."
479 note: %Schema{type: :string, description: "The account bio."},
483 description: "Avatar image encoded using multipart/form-data",
489 description: "Header image encoded using multipart/form-data",
495 description: "Whether manual approval of follow requests is required."
497 fields_attributes: %Schema{
500 %Schema{type: :array, items: attribute_field()},
501 %Schema{type: :object, additionalProperties: %Schema{type: attribute_field()}}
504 # NOTE: `source` field is not supported
509 # privacy: %Schema{type: :string},
510 # sensitive: %Schema{type: :boolean},
511 # language: %Schema{type: :string}
515 # Pleroma-specific fields
516 no_rich_text: %Schema{
519 description: "html tags are stripped from all statuses requested from the API"
521 hide_followers: %Schema{
524 description: "user's followers will be hidden"
526 hide_follows: %Schema{
529 description: "user's follows will be hidden"
531 hide_followers_count: %Schema{
534 description: "user's follower count will be hidden"
536 hide_follows_count: %Schema{
539 description: "user's follow count will be hidden"
541 hide_favorites: %Schema{
544 description: "user's favorites timeline will be hidden"
549 description: "user's role (e.g admin, moderator) will be exposed to anyone in the
552 default_scope: VisibilityScope,
553 pleroma_settings_store: %Schema{
556 description: "Opaque user settings to be saved on the backend."
558 skip_thread_containment: %Schema{
561 description: "Skip filtering out broken threads"
563 allow_following_move: %Schema{
566 description: "Allows automatically follow moved following accounts"
568 pleroma_background_image: %Schema{
571 description: "Sets the background image of the user.",
574 discoverable: %Schema{
578 "Discovery of this account in search results and other services is allowed."
580 actor_type: ActorType
584 display_name: "cofe",
586 fields_attributes: [%{name: "foo", value: "bar"}],
588 hide_followers: true,
590 hide_followers_count: false,
591 hide_follows_count: false,
592 hide_favorites: false,
594 default_scope: "private",
595 pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
596 skip_thread_containment: false,
597 allow_following_move: false,
604 def array_of_accounts do
606 title: "ArrayOfAccounts",
609 example: [Account.schema().example]
613 defp array_of_relationships do
615 title: "ArrayOfRelationships",
616 description: "Response schema for account relationships",
618 items: AccountRelationship,
623 "showing_reblogs" => true,
624 "followed_by" => true,
626 "blocked_by" => true,
628 "muting_notifications" => false,
629 "requested" => false,
630 "domain_blocking" => false,
631 "subscribing" => false,
637 "showing_reblogs" => true,
638 "followed_by" => true,
640 "blocked_by" => true,
642 "muting_notifications" => false,
644 "domain_blocking" => false,
645 "subscribing" => false,
651 "showing_reblogs" => true,
652 "followed_by" => true,
654 "blocked_by" => false,
656 "muting_notifications" => false,
657 "requested" => false,
658 "domain_blocking" => true,
659 "subscribing" => true,
666 defp follow_by_uri_request do
668 title: "AccountFollowsRequest",
669 description: "POST body for muting an account",
672 uri: %Schema{type: :string, nullable: true, format: :uri}
680 title: "AccountMuteRequest",
681 description: "POST body for muting an account",
684 notifications: %Schema{
687 description: "Mute notifications in addition to statuses? Defaults to true.",
692 "notifications" => true
697 defp array_of_lists do
699 title: "ArrayOfLists",
700 description: "Response schema for lists",
704 %{"id" => "123", "title" => "my list"},
705 %{"id" => "1337", "title" => "anotehr list"}
710 defp array_of_statuses do
712 title: "ArrayOfStatuses",
718 defp attribute_field do
720 title: "AccountAttributeField",
721 description: "Request schema for account custom fields",
724 name: %Schema{type: :string},
725 value: %Schema{type: :string}
727 required: [:name, :value],
730 "value" => "https://pleroma.com"