total rebase
[anni] / lib / pleroma / web / api_spec / operations / account_operation.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
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
17
18   import Pleroma.Web.ApiSpec.Helpers
19
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, [])
24   end
25
26   @spec create_operation() :: Operation.t()
27   def create_operation do
28     %Operation{
29       tags: ["Account credentials"],
30       summary: "Register an account",
31       description:
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),
35       responses: %{
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)
40       }
41     }
42   end
43
44   def verify_credentials_operation do
45     %Operation{
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"]}],
51       responses: %{
52         200 => Operation.response("Account", "application/json", Account)
53       }
54     }
55   end
56
57   def update_credentials_operation do
58     %Operation{
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),
65       responses: %{
66         200 => Operation.response("Account", "application/json", Account),
67         403 => Operation.response("Error", "application/json", ApiError),
68         413 => Operation.response("Error", "application/json", ApiError)
69       }
70     }
71   end
72
73   def relationships_operation do
74     %Operation{
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"]}],
80       parameters: [
81         Operation.parameter(
82           :id,
83           :query,
84           %Schema{
85             oneOf: [%Schema{type: :array, items: %Schema{type: :string}}, %Schema{type: :string}]
86           },
87           "Account IDs",
88           example: "123"
89         )
90       ],
91       responses: %{
92         200 => Operation.response("Account", "application/json", array_of_relationships())
93       }
94     }
95   end
96
97   def show_operation do
98     %Operation{
99       tags: ["Retrieve account information"],
100       summary: "Account",
101       operationId: "AccountController.show",
102       description: "View information about a profile.",
103       parameters: [
104         %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
105         with_relationships_param()
106       ],
107       responses: %{
108         200 => Operation.response("Account", "application/json", Account),
109         401 => Operation.response("Error", "application/json", ApiError),
110         404 => Operation.response("Error", "application/json", ApiError)
111       }
112     }
113   end
114
115   def statuses_operation do
116     %Operation{
117       summary: "Statuses",
118       tags: ["Retrieve account information"],
119       operationId: "AccountController.statuses",
120       description:
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)",
122       parameters:
123         [
124           %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
125           Operation.parameter(
126             :pinned,
127             :query,
128             BooleanLike.schema(),
129             "Include only pinned statuses"
130           ),
131           Operation.parameter(:tagged, :query, :string, "With tag"),
132           Operation.parameter(
133             :only_media,
134             :query,
135             BooleanLike.schema(),
136             "Include only statuses with media attached"
137           ),
138           Operation.parameter(
139             :with_muted,
140             :query,
141             BooleanLike.schema(),
142             "Include statuses from muted accounts."
143           ),
144           Operation.parameter(:exclude_reblogs, :query, BooleanLike.schema(), "Exclude reblogs"),
145           Operation.parameter(:exclude_replies, :query, BooleanLike.schema(), "Exclude replies"),
146           Operation.parameter(
147             :exclude_visibilities,
148             :query,
149             %Schema{type: :array, items: VisibilityScope},
150             "Exclude visibilities"
151           ),
152           Operation.parameter(
153             :with_muted,
154             :query,
155             BooleanLike.schema(),
156             "Include reactions from muted accounts."
157           )
158         ] ++ pagination_params(),
159       responses: %{
160         200 => Operation.response("Statuses", "application/json", array_of_statuses()),
161         401 => Operation.response("Error", "application/json", ApiError),
162         404 => Operation.response("Error", "application/json", ApiError)
163       }
164     }
165   end
166
167   def followers_operation do
168     %Operation{
169       tags: ["Retrieve account information"],
170       summary: "Followers",
171       operationId: "AccountController.followers",
172       security: [%{"oAuth" => ["read:accounts"]}],
173       description:
174         "Accounts which follow the given account, if network is not hidden by the account owner.",
175       parameters: [
176         %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
177         Operation.parameter(:id, :query, :string, "ID of the resource owner"),
178         with_relationships_param() | pagination_params()
179       ],
180       responses: %{
181         200 => Operation.response("Accounts", "application/json", array_of_accounts())
182       }
183     }
184   end
185
186   def following_operation do
187     %Operation{
188       tags: ["Retrieve account information"],
189       summary: "Following",
190       operationId: "AccountController.following",
191       security: [%{"oAuth" => ["read:accounts"]}],
192       description:
193         "Accounts which the given account is following, if network is not hidden by the account owner.",
194       parameters: [
195         %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
196         Operation.parameter(:id, :query, :string, "ID of the resource owner"),
197         with_relationships_param() | pagination_params()
198       ],
199       responses: %{200 => Operation.response("Accounts", "application/json", array_of_accounts())}
200     }
201   end
202
203   def lists_operation do
204     %Operation{
205       tags: ["Retrieve account information"],
206       summary: "Lists containing this account",
207       operationId: "AccountController.lists",
208       security: [%{"oAuth" => ["read:lists"]}],
209       description: "User lists that you have added this account to.",
210       parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
211       responses: %{200 => Operation.response("Lists", "application/json", array_of_lists())}
212     }
213   end
214
215   def follow_operation do
216     %Operation{
217       tags: ["Account actions"],
218       summary: "Follow",
219       operationId: "AccountController.follow",
220       security: [%{"oAuth" => ["follow", "write:follows"]}],
221       description: "Follow the given account",
222       parameters: [
223         %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
224       ],
225       requestBody:
226         request_body(
227           "Parameters",
228           %Schema{
229             type: :object,
230             properties: %{
231               reblogs: %Schema{
232                 allOf: [BooleanLike],
233                 description: "Receive this account's reblogs in home timeline? Defaults to true.",
234                 default: true
235               },
236               notify: %Schema{
237                 allOf: [BooleanLike],
238                 description:
239                   "Receive notifications for all statuses posted by the account? Defaults to false.",
240                 default: false
241               }
242             }
243           },
244           required: false
245         ),
246       responses: %{
247         200 => Operation.response("Relationship", "application/json", AccountRelationship),
248         400 => Operation.response("Error", "application/json", ApiError),
249         404 => Operation.response("Error", "application/json", ApiError)
250       }
251     }
252   end
253
254   def unfollow_operation do
255     %Operation{
256       tags: ["Account actions"],
257       summary: "Unfollow",
258       operationId: "AccountController.unfollow",
259       security: [%{"oAuth" => ["follow", "write:follows"]}],
260       description: "Unfollow the given account",
261       parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
262       responses: %{
263         200 => Operation.response("Relationship", "application/json", AccountRelationship),
264         400 => Operation.response("Error", "application/json", ApiError),
265         404 => Operation.response("Error", "application/json", ApiError)
266       }
267     }
268   end
269
270   def mute_operation do
271     %Operation{
272       tags: ["Account actions"],
273       summary: "Mute",
274       operationId: "AccountController.mute",
275       security: [%{"oAuth" => ["follow", "write:mutes"]}],
276       requestBody: request_body("Parameters", mute_request()),
277       description:
278         "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).",
279       parameters: [
280         %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
281         Operation.parameter(
282           :notifications,
283           :query,
284           %Schema{allOf: [BooleanLike], default: true},
285           "Mute notifications in addition to statuses? Defaults to `true`."
286         ),
287         Operation.parameter(
288           :duration,
289           :query,
290           %Schema{type: :integer},
291           "Expire the mute in `duration` seconds. Default 0 for infinity"
292         ),
293         Operation.parameter(
294           :expires_in,
295           :query,
296           %Schema{type: :integer, default: 0},
297           "Deprecated, use `duration` instead"
298         )
299       ],
300       responses: %{
301         200 => Operation.response("Relationship", "application/json", AccountRelationship)
302       }
303     }
304   end
305
306   def unmute_operation do
307     %Operation{
308       tags: ["Account actions"],
309       summary: "Unmute",
310       operationId: "AccountController.unmute",
311       security: [%{"oAuth" => ["follow", "write:mutes"]}],
312       description: "Unmute the given account.",
313       parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
314       responses: %{
315         200 => Operation.response("Relationship", "application/json", AccountRelationship)
316       }
317     }
318   end
319
320   def block_operation do
321     %Operation{
322       tags: ["Account actions"],
323       summary: "Block",
324       operationId: "AccountController.block",
325       security: [%{"oAuth" => ["follow", "write:blocks"]}],
326       description:
327         "Block the given account. Clients should filter statuses from this account if received (e.g. due to a boost in the Home timeline)",
328       parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
329       responses: %{
330         200 => Operation.response("Relationship", "application/json", AccountRelationship)
331       }
332     }
333   end
334
335   def unblock_operation do
336     %Operation{
337       tags: ["Account actions"],
338       summary: "Unblock",
339       operationId: "AccountController.unblock",
340       security: [%{"oAuth" => ["follow", "write:blocks"]}],
341       description: "Unblock the given account.",
342       parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
343       responses: %{
344         200 => Operation.response("Relationship", "application/json", AccountRelationship)
345       }
346     }
347   end
348
349   def endorse_operation do
350     %Operation{
351       tags: ["Account actions"],
352       summary: "Endorse",
353       operationId: "AccountController.endorse",
354       security: [%{"oAuth" => ["follow", "write:accounts"]}],
355       description: "Adds the given account to endorsed accounts list.",
356       parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
357       responses: %{
358         200 => Operation.response("Relationship", "application/json", AccountRelationship),
359         400 =>
360           Operation.response("Bad Request", "application/json", %Schema{
361             allOf: [ApiError],
362             title: "Unprocessable Entity",
363             example: %{
364               "error" => "You have already pinned the maximum number of users"
365             }
366           })
367       }
368     }
369   end
370
371   def unendorse_operation do
372     %Operation{
373       tags: ["Account actions"],
374       summary: "Unendorse",
375       operationId: "AccountController.unendorse",
376       security: [%{"oAuth" => ["follow", "write:accounts"]}],
377       description: "Removes the given account from endorsed accounts list.",
378       parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
379       responses: %{
380         200 => Operation.response("Relationship", "application/json", AccountRelationship)
381       }
382     }
383   end
384
385   def remove_from_followers_operation do
386     %Operation{
387       tags: ["Account actions"],
388       summary: "Remove from followers",
389       operationId: "AccountController.remove_from_followers",
390       security: [%{"oAuth" => ["follow", "write:follows"]}],
391       description: "Remove the given account from followers",
392       parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
393       responses: %{
394         200 => Operation.response("Relationship", "application/json", AccountRelationship),
395         400 => Operation.response("Error", "application/json", ApiError),
396         404 => Operation.response("Error", "application/json", ApiError)
397       }
398     }
399   end
400
401   def note_operation do
402     %Operation{
403       tags: ["Account actions"],
404       summary: "Set a private note about a user.",
405       operationId: "AccountController.note",
406       security: [%{"oAuth" => ["follow", "write:accounts"]}],
407       requestBody: request_body("Parameters", note_request()),
408       description: "Create a note for the given account.",
409       parameters: [
410         %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
411         Operation.parameter(
412           :comment,
413           :query,
414           %Schema{type: :string},
415           "Account note body"
416         )
417       ],
418       responses: %{
419         200 => Operation.response("Relationship", "application/json", AccountRelationship)
420       }
421     }
422   end
423
424   def follow_by_uri_operation do
425     %Operation{
426       tags: ["Account actions"],
427       summary: "Follow by URI",
428       operationId: "AccountController.follows",
429       security: [%{"oAuth" => ["follow", "write:follows"]}],
430       requestBody: request_body("Parameters", follow_by_uri_request(), required: true),
431       responses: %{
432         200 => Operation.response("Account", "application/json", AccountRelationship),
433         400 => Operation.response("Error", "application/json", ApiError),
434         404 => Operation.response("Error", "application/json", ApiError)
435       }
436     }
437   end
438
439   def mutes_operation do
440     %Operation{
441       tags: ["Blocks and mutes"],
442       summary: "Retrieve list of mutes",
443       operationId: "AccountController.mutes",
444       description: "Accounts the user has muted.",
445       security: [%{"oAuth" => ["follow", "read:mutes"]}],
446       parameters: [with_relationships_param() | pagination_params()],
447       responses: %{
448         200 => Operation.response("Accounts", "application/json", array_of_accounts())
449       }
450     }
451   end
452
453   def blocks_operation do
454     %Operation{
455       tags: ["Blocks and mutes"],
456       summary: "Retrieve list of blocks",
457       operationId: "AccountController.blocks",
458       description: "View your blocks. See also accounts/:id/{block,unblock}",
459       security: [%{"oAuth" => ["read:blocks"]}],
460       parameters: [with_relationships_param() | pagination_params()],
461       responses: %{
462         200 => Operation.response("Accounts", "application/json", array_of_accounts())
463       }
464     }
465   end
466
467   def lookup_operation do
468     %Operation{
469       tags: ["Retrieve account information"],
470       summary: "Find a user by nickname",
471       operationId: "AccountController.lookup",
472       parameters: [
473         Operation.parameter(
474           :acct,
475           :query,
476           :string,
477           "User nickname"
478         )
479       ],
480       responses: %{
481         200 => Operation.response("Account", "application/json", Account),
482         404 => Operation.response("Error", "application/json", ApiError)
483       }
484     }
485   end
486
487   def endorsements_operation do
488     %Operation{
489       tags: ["Retrieve account information"],
490       summary: "Endorsements",
491       operationId: "AccountController.endorsements",
492       description: "Returns endorsed accounts",
493       security: [%{"oAuth" => ["read:accounts"]}],
494       responses: %{
495         200 => Operation.response("Array of Accounts", "application/json", array_of_accounts())
496       }
497     }
498   end
499
500   def identity_proofs_operation do
501     %Operation{
502       tags: ["Retrieve account information"],
503       summary: "Identity proofs",
504       operationId: "AccountController.identity_proofs",
505       # Validators complains about unused path params otherwise
506       parameters: [
507         %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
508       ],
509       description: "Not implemented",
510       responses: %{
511         200 => empty_array_response()
512       }
513     }
514   end
515
516   defp create_request do
517     %Schema{
518       title: "AccountCreateRequest",
519       description: "POST body for creating an account",
520       type: :object,
521       required: [:username, :password, :agreement],
522       properties: %{
523         reason: %Schema{
524           type: :string,
525           nullable: true,
526           description:
527             "Text that will be reviewed by moderators if registrations require manual approval"
528         },
529         username: %Schema{type: :string, description: "The desired username for the account"},
530         email: %Schema{
531           type: :string,
532           nullable: true,
533           description:
534             "The email address to be used for login. Required when `account_activation_required` is enabled.",
535           format: :email
536         },
537         password: %Schema{
538           type: :string,
539           description: "The password to be used for login",
540           format: :password
541         },
542         agreement: %Schema{
543           allOf: [BooleanLike],
544           description:
545             "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."
546         },
547         locale: %Schema{
548           type: :string,
549           nullable: true,
550           description: "The language of the confirmation email that will be sent"
551         },
552         # Pleroma-specific properties:
553         fullname: %Schema{type: :string, nullable: true, description: "Full name"},
554         bio: %Schema{type: :string, description: "Bio", nullable: true, default: ""},
555         captcha_solution: %Schema{
556           type: :string,
557           nullable: true,
558           description: "Provider-specific captcha solution"
559         },
560         captcha_token: %Schema{
561           type: :string,
562           nullable: true,
563           description: "Provider-specific captcha token"
564         },
565         captcha_answer_data: %Schema{
566           type: :string,
567           nullable: true,
568           description: "Provider-specific captcha data"
569         },
570         token: %Schema{
571           type: :string,
572           nullable: true,
573           description: "Invite token required when the registrations aren't public"
574         },
575         birthday: %Schema{
576           nullable: true,
577           description: "User's birthday",
578           anyOf: [
579             %Schema{
580               type: :string,
581               format: :date
582             },
583             %Schema{
584               type: :string,
585               maxLength: 0
586             }
587           ]
588         },
589         language: %Schema{
590           type: :string,
591           nullable: true,
592           description: "User's preferred language for emails"
593         }
594       },
595       example: %{
596         "username" => "cofe",
597         "email" => "cofe@example.com",
598         "password" => "secret",
599         "agreement" => "true",
600         "bio" => "☕️"
601       }
602     }
603   end
604
605   # Note: this is a token response (if login succeeds!), but there's no oauth operation file yet.
606   defp create_response do
607     %Schema{
608       title: "AccountCreateResponse",
609       description: "Response schema for an account",
610       type: :object,
611       properties: %{
612         # The response when auto-login on create succeeds (token is issued):
613         token_type: %Schema{type: :string},
614         access_token: %Schema{type: :string},
615         refresh_token: %Schema{type: :string},
616         scope: %Schema{type: :string},
617         created_at: %Schema{type: :integer, format: :"date-time"},
618         me: %Schema{type: :string},
619         expires_in: %Schema{type: :integer},
620         #
621         # The response when registration succeeds but auto-login fails (no token):
622         identifier: %Schema{type: :string},
623         message: %Schema{type: :string}
624       },
625       # Note: example of successful registration with failed login response:
626       # example: %{
627       #   "identifier" => "missing_confirmed_email",
628       #   "message" => "You have been registered. Please check your email for further instructions."
629       # },
630       example: %{
631         "token_type" => "Bearer",
632         "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk",
633         "refresh_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzz",
634         "created_at" => 1_585_918_714,
635         "expires_in" => 600,
636         "scope" => "read write follow push",
637         "me" => "https://gensokyo.2hu/users/raymoo"
638       }
639     }
640   end
641
642   defp update_credentials_request do
643     %Schema{
644       title: "AccountUpdateCredentialsRequest",
645       description: "POST body for creating an account",
646       type: :object,
647       properties: %{
648         bot: %Schema{
649           allOf: [BooleanLike],
650           nullable: true,
651           description: "Whether the account has a bot flag."
652         },
653         display_name: %Schema{
654           type: :string,
655           nullable: true,
656           description: "The display name to use for the profile."
657         },
658         note: %Schema{type: :string, description: "The account bio."},
659         avatar: %Schema{
660           type: :string,
661           nullable: true,
662           description: "Avatar image encoded using multipart/form-data",
663           format: :binary
664         },
665         header: %Schema{
666           type: :string,
667           nullable: true,
668           description: "Header image encoded using multipart/form-data",
669           format: :binary
670         },
671         locked: %Schema{
672           allOf: [BooleanLike],
673           nullable: true,
674           description: "Whether manual approval of follow requests is required."
675         },
676         accepts_chat_messages: %Schema{
677           allOf: [BooleanLike],
678           nullable: true,
679           description: "Whether the user accepts receiving chat messages."
680         },
681         fields_attributes: %Schema{
682           nullable: true,
683           oneOf: [
684             %Schema{type: :array, items: attribute_field()},
685             %Schema{type: :object, additionalProperties: attribute_field()}
686           ]
687         },
688         # NOTE: `source` field is not supported
689         #
690         # source: %Schema{
691         #   type: :object,
692         #   properties: %{
693         #     privacy: %Schema{type: :string},
694         #     sensitive: %Schema{type: :boolean},
695         #     language: %Schema{type: :string}
696         #   }
697         # },
698
699         # Pleroma-specific fields
700         no_rich_text: %Schema{
701           allOf: [BooleanLike],
702           nullable: true,
703           description: "html tags are stripped from all statuses requested from the API"
704         },
705         hide_followers: %Schema{
706           allOf: [BooleanLike],
707           nullable: true,
708           description: "user's followers will be hidden"
709         },
710         hide_follows: %Schema{
711           allOf: [BooleanLike],
712           nullable: true,
713           description: "user's follows will be hidden"
714         },
715         hide_followers_count: %Schema{
716           allOf: [BooleanLike],
717           nullable: true,
718           description: "user's follower count will be hidden"
719         },
720         hide_follows_count: %Schema{
721           allOf: [BooleanLike],
722           nullable: true,
723           description: "user's follow count will be hidden"
724         },
725         hide_favorites: %Schema{
726           allOf: [BooleanLike],
727           nullable: true,
728           description: "user's favorites timeline will be hidden"
729         },
730         show_role: %Schema{
731           allOf: [BooleanLike],
732           nullable: true,
733           description: "user's role (e.g admin, moderator) will be exposed to anyone in the
734         API"
735         },
736         default_scope: VisibilityScope,
737         pleroma_settings_store: %Schema{
738           type: :object,
739           nullable: true,
740           description: "Opaque user settings to be saved on the backend."
741         },
742         skip_thread_containment: %Schema{
743           allOf: [BooleanLike],
744           nullable: true,
745           description: "Skip filtering out broken threads"
746         },
747         allow_following_move: %Schema{
748           allOf: [BooleanLike],
749           nullable: true,
750           description: "Allows automatically follow moved following accounts"
751         },
752         also_known_as: %Schema{
753           type: :array,
754           items: %Schema{type: :string},
755           nullable: true,
756           description: "List of alternate ActivityPub IDs"
757         },
758         pleroma_background_image: %Schema{
759           type: :string,
760           nullable: true,
761           description: "Sets the background image of the user.",
762           format: :binary
763         },
764         discoverable: %Schema{
765           allOf: [BooleanLike],
766           nullable: true,
767           description:
768             "Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed."
769         },
770         actor_type: ActorType,
771         birthday: %Schema{
772           nullable: true,
773           description: "User's birthday",
774           anyOf: [
775             %Schema{
776               type: :string,
777               format: :date
778             },
779             %Schema{
780               type: :string,
781               maxLength: 0
782             }
783           ]
784         },
785         show_birthday: %Schema{
786           allOf: [BooleanLike],
787           nullable: true,
788           description: "User's birthday will be visible"
789         }
790       },
791       example: %{
792         bot: false,
793         display_name: "cofe",
794         note: "foobar",
795         fields_attributes: [%{name: "foo", value: "bar"}],
796         no_rich_text: false,
797         hide_followers: true,
798         hide_follows: false,
799         hide_followers_count: false,
800         hide_follows_count: false,
801         hide_favorites: false,
802         show_role: false,
803         default_scope: "private",
804         pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
805         skip_thread_containment: false,
806         allow_following_move: false,
807         also_known_as: ["https://foo.bar/users/foo"],
808         discoverable: false,
809         actor_type: "Person",
810         show_birthday: false,
811         birthday: "2001-02-12"
812       }
813     }
814   end
815
816   def array_of_accounts do
817     %Schema{
818       title: "ArrayOfAccounts",
819       type: :array,
820       items: Account,
821       example: [Account.schema().example]
822     }
823   end
824
825   defp array_of_relationships do
826     %Schema{
827       title: "ArrayOfRelationships",
828       description: "Response schema for account relationships",
829       type: :array,
830       items: AccountRelationship,
831       example: [
832         %{
833           "id" => "1",
834           "following" => true,
835           "showing_reblogs" => true,
836           "followed_by" => true,
837           "blocking" => false,
838           "blocked_by" => true,
839           "muting" => false,
840           "muting_notifications" => false,
841           "note" => "",
842           "requested" => false,
843           "domain_blocking" => false,
844           "subscribing" => false,
845           "notifying" => false,
846           "endorsed" => true
847         },
848         %{
849           "id" => "2",
850           "following" => true,
851           "showing_reblogs" => true,
852           "followed_by" => true,
853           "blocking" => false,
854           "blocked_by" => true,
855           "muting" => true,
856           "muting_notifications" => false,
857           "note" => "",
858           "requested" => true,
859           "domain_blocking" => false,
860           "subscribing" => false,
861           "notifying" => false,
862           "endorsed" => false
863         },
864         %{
865           "id" => "3",
866           "following" => true,
867           "showing_reblogs" => true,
868           "followed_by" => true,
869           "blocking" => true,
870           "blocked_by" => false,
871           "muting" => true,
872           "muting_notifications" => false,
873           "note" => "",
874           "requested" => false,
875           "domain_blocking" => true,
876           "subscribing" => true,
877           "notifying" => true,
878           "endorsed" => false
879         }
880       ]
881     }
882   end
883
884   defp follow_by_uri_request do
885     %Schema{
886       title: "AccountFollowsRequest",
887       description: "POST body for muting an account",
888       type: :object,
889       properties: %{
890         uri: %Schema{type: :string, nullable: true, format: :uri}
891       },
892       required: [:uri]
893     }
894   end
895
896   defp mute_request do
897     %Schema{
898       title: "AccountMuteRequest",
899       description: "POST body for muting an account",
900       type: :object,
901       properties: %{
902         notifications: %Schema{
903           allOf: [BooleanLike],
904           nullable: true,
905           description: "Mute notifications in addition to statuses? Defaults to true.",
906           default: true
907         },
908         duration: %Schema{
909           type: :integer,
910           nullable: true,
911           description: "Expire the mute in `expires_in` seconds. Default 0 for infinity"
912         },
913         expires_in: %Schema{
914           type: :integer,
915           nullable: true,
916           description: "Deprecated, use `duration` instead",
917           default: 0
918         }
919       },
920       example: %{
921         "notifications" => true,
922         "expires_in" => 86_400
923       }
924     }
925   end
926
927   defp note_request do
928     %Schema{
929       title: "AccountNoteRequest",
930       description: "POST body for adding a note for an account",
931       type: :object,
932       properties: %{
933         comment: %Schema{
934           type: :string,
935           description: "Account note body"
936         }
937       },
938       example: %{
939         "comment" => "Example note"
940       }
941     }
942   end
943
944   defp array_of_lists do
945     %Schema{
946       title: "ArrayOfLists",
947       description: "Response schema for lists",
948       type: :array,
949       items: List,
950       example: [
951         %{"id" => "123", "title" => "my list"},
952         %{"id" => "1337", "title" => "anotehr list"}
953       ]
954     }
955   end
956
957   defp array_of_statuses do
958     %Schema{
959       title: "ArrayOfStatuses",
960       type: :array,
961       items: Status
962     }
963   end
964
965   defp attribute_field do
966     %Schema{
967       title: "AccountAttributeField",
968       description: "Request schema for account custom fields",
969       type: :object,
970       properties: %{
971         name: %Schema{type: :string},
972         value: %Schema{type: :string}
973       },
974       required: [:name, :value],
975       example: %{
976         "name" => "Website",
977         "value" => "https://pleroma.com"
978       }
979     }
980   end
981 end