First
[anni] / lib / pleroma / web / api_spec / render_error.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.RenderError do
6   @behaviour Plug
7
8   import Plug.Conn, only: [put_status: 2]
9   import Phoenix.Controller, only: [json: 2]
10   import Pleroma.Web.Gettext
11
12   @impl Plug
13   def init(opts), do: opts
14
15   @impl Plug
16
17   def call(conn, errors) do
18     errors =
19       Enum.map(errors, fn
20         %{name: nil, reason: :invalid_enum} = err ->
21           %OpenApiSpex.Cast.Error{err | name: err.value}
22
23         %{name: nil} = err ->
24           %OpenApiSpex.Cast.Error{err | name: List.last(err.path)}
25
26         err ->
27           err
28       end)
29
30     conn
31     |> put_status(:bad_request)
32     |> json(%{
33       error: errors |> Enum.map(&message/1) |> Enum.join(" "),
34       errors: errors |> Enum.map(&render_error/1)
35     })
36   end
37
38   defp render_error(error) do
39     pointer = OpenApiSpex.path_to_string(error)
40
41     %{
42       title: "Invalid value",
43       source: %{
44         pointer: pointer
45       },
46       message: OpenApiSpex.Cast.Error.message(error)
47     }
48   end
49
50   defp message(%{reason: :invalid_schema_type, type: type, name: name}) do
51     gettext("%{name} - Invalid schema.type. Got: %{type}.",
52       name: name,
53       type: inspect(type)
54     )
55   end
56
57   defp message(%{reason: :null_value, name: name} = error) do
58     case error.type do
59       nil ->
60         gettext("%{name} - null value.", name: name)
61
62       type ->
63         gettext("%{name} - null value where %{type} expected.",
64           name: name,
65           type: type
66         )
67     end
68   end
69
70   defp message(%{reason: :all_of, meta: %{invalid_schema: invalid_schema}}) do
71     gettext(
72       "Failed to cast value as %{invalid_schema}. Value must be castable using `allOf` schemas listed.",
73       invalid_schema: invalid_schema
74     )
75   end
76
77   defp message(%{reason: :any_of, meta: %{failed_schemas: failed_schemas}}) do
78     gettext("Failed to cast value using any of: %{failed_schemas}.",
79       failed_schemas: failed_schemas
80     )
81   end
82
83   defp message(%{reason: :one_of, meta: %{failed_schemas: failed_schemas}}) do
84     gettext("Failed to cast value to one of: %{failed_schemas}.", failed_schemas: failed_schemas)
85   end
86
87   defp message(%{reason: :min_length, length: length, name: name}) do
88     gettext("%{name} - String length is smaller than minLength: %{length}.",
89       name: name,
90       length: length
91     )
92   end
93
94   defp message(%{reason: :max_length, length: length, name: name}) do
95     gettext("%{name} - String length is larger than maxLength: %{length}.",
96       name: name,
97       length: length
98     )
99   end
100
101   defp message(%{reason: :unique_items, name: name}) do
102     gettext("%{name} - Array items must be unique.", name: name)
103   end
104
105   defp message(%{reason: :min_items, length: min, value: array, name: name}) do
106     gettext("%{name} - Array length %{length} is smaller than minItems: %{min}.",
107       name: name,
108       length: length(array),
109       min: min
110     )
111   end
112
113   defp message(%{reason: :max_items, length: max, value: array, name: name}) do
114     gettext("%{name} - Array length %{length} is larger than maxItems: %{}.",
115       name: name,
116       length: length(array),
117       max: max
118     )
119   end
120
121   defp message(%{reason: :multiple_of, length: multiple, value: count, name: name}) do
122     gettext("%{name} - %{count} is not a multiple of %{multiple}.",
123       name: name,
124       count: count,
125       multiple: multiple
126     )
127   end
128
129   defp message(%{reason: :exclusive_max, length: max, value: value, name: name})
130        when value >= max do
131     gettext("%{name} - %{value} is larger than exclusive maximum %{max}.",
132       name: name,
133       value: value,
134       max: max
135     )
136   end
137
138   defp message(%{reason: :maximum, length: max, value: value, name: name})
139        when value > max do
140     gettext("%{name} - %{value} is larger than inclusive maximum %{max}.",
141       name: name,
142       value: value,
143       max: max
144     )
145   end
146
147   defp message(%{reason: :exclusive_multiple, length: min, value: value, name: name})
148        when value <= min do
149     gettext("%{name} - %{value} is smaller than exclusive minimum %{min}.",
150       name: name,
151       value: value,
152       min: min
153     )
154   end
155
156   defp message(%{reason: :minimum, length: min, value: value, name: name})
157        when value < min do
158     gettext("%{name} - %{value} is smaller than inclusive minimum %{min}.",
159       name: name,
160       value: value,
161       min: min
162     )
163   end
164
165   defp message(%{reason: :invalid_type, type: type, value: value, name: name}) do
166     gettext("%{name} - Invalid %{type}. Got: %{value}.",
167       name: name,
168       value: OpenApiSpex.TermType.type(value),
169       type: type
170     )
171   end
172
173   defp message(%{reason: :invalid_format, format: format, name: name}) do
174     gettext("%{name} - Invalid format. Expected %{format}.", name: name, format: inspect(format))
175   end
176
177   defp message(%{reason: :invalid_enum, name: name}) do
178     gettext("%{name} - Invalid value for enum.", name: name)
179   end
180
181   defp message(%{reason: :polymorphic_failed, type: polymorphic_type}) do
182     gettext("Failed to cast to any schema in %{polymorphic_type}",
183       polymorphic_type: polymorphic_type
184     )
185   end
186
187   defp message(%{reason: :unexpected_field, name: name}) do
188     gettext("Unexpected field: %{name}.", name: safe_string(name))
189   end
190
191   defp message(%{reason: :no_value_for_discriminator, name: field}) do
192     gettext("Value used as discriminator for `%{field}` matches no schemas.", name: field)
193   end
194
195   defp message(%{reason: :invalid_discriminator_value, name: field}) do
196     gettext("No value provided for required discriminator `%{field}`.", name: field)
197   end
198
199   defp message(%{reason: :unknown_schema, name: name}) do
200     gettext("Unknown schema: %{name}.", name: name)
201   end
202
203   defp message(%{reason: :missing_field, name: name}) do
204     gettext("Missing field: %{name}.", name: name)
205   end
206
207   defp message(%{reason: :missing_header, name: name}) do
208     gettext("Missing header: %{name}.", name: name)
209   end
210
211   defp message(%{reason: :invalid_header, name: name}) do
212     gettext("Invalid value for header: %{name}.", name: name)
213   end
214
215   defp message(%{reason: :max_properties, meta: meta}) do
216     gettext(
217       "Object property count %{property_count} is greater than maxProperties: %{max_properties}.",
218       property_count: meta.property_count,
219       max_properties: meta.max_properties
220     )
221   end
222
223   defp message(%{reason: :min_properties, meta: meta}) do
224     gettext(
225       "Object property count %{property_count} is less than minProperties: %{min_properties}",
226       property_count: meta.property_count,
227       min_properties: meta.min_properties
228     )
229   end
230
231   defp safe_string(string) do
232     to_string(string) |> String.slice(0..39)
233   end
234 end