Improve OpenAPI errors

This commit is contained in:
Egor Kislitsyn 2020-04-17 19:27:22 +04:00
parent 65f04b7806
commit 163341857a
No known key found for this signature in database
GPG key ID: 1B49CB15B71E7805
2 changed files with 222 additions and 9 deletions

View file

@ -5,23 +5,227 @@
defmodule Pleroma.Web.ApiSpec.RenderError do
@behaviour Plug
alias OpenApiSpex.Plug.JsonRenderError
alias Plug.Conn
import Plug.Conn, only: [put_status: 2]
import Phoenix.Controller, only: [json: 2]
import Pleroma.Web.Gettext
@impl Plug
def init(opts), do: opts
@impl Plug
def call(%{private: %{open_api_spex: %{operation_id: "AccountController.create"}}} = conn, _) do
def call(conn, errors) do
errors =
Enum.map(errors, fn
%{name: nil} = err ->
%OpenApiSpex.Cast.Error{err | name: List.last(err.path)}
err ->
err
end)
conn
|> Conn.put_status(:bad_request)
|> Phoenix.Controller.json(%{"error" => "Missing parameters"})
|> put_status(:bad_request)
|> json(%{
error: errors |> Enum.map(&message/1) |> Enum.join(" "),
errors: errors |> Enum.map(&render_error/1)
})
end
def call(conn, reason) do
opts = JsonRenderError.init(reason)
defp render_error(error) do
pointer = OpenApiSpex.path_to_string(error)
JsonRenderError.call(conn, opts)
%{
title: "Invalid value",
source: %{
pointer: pointer
},
message: OpenApiSpex.Cast.Error.message(error)
}
end
defp message(%{reason: :invalid_schema_type, type: type, name: name}) do
gettext("%{name} - Invalid schema.type. Got: %{type}.",
name: name,
type: inspect(type)
)
end
defp message(%{reason: :null_value, name: name} = error) do
case error.type do
nil ->
gettext("%{name} - null value.", name: name)
type ->
gettext("%{name} - null value where %{type} expected.",
name: name,
type: type
)
end
end
defp message(%{reason: :all_of, meta: %{invalid_schema: invalid_schema}}) do
gettext(
"Failed to cast value as %{invalid_schema}. Value must be castable using `allOf` schemas listed.",
invalid_schema: invalid_schema
)
end
defp message(%{reason: :any_of, meta: %{failed_schemas: failed_schemas}}) do
gettext("Failed to cast value using any of: %{failed_schemas}.",
failed_schemas: failed_schemas
)
end
defp message(%{reason: :one_of, meta: %{failed_schemas: failed_schemas}}) do
gettext("Failed to cast value to one of: %{failed_schemas}.", failed_schemas: failed_schemas)
end
defp message(%{reason: :min_length, length: length, name: name}) do
gettext("%{name} - String length is smaller than minLength: %{length}.",
name: name,
length: length
)
end
defp message(%{reason: :max_length, length: length, name: name}) do
gettext("%{name} - String length is larger than maxLength: %{length}.",
name: name,
length: length
)
end
defp message(%{reason: :unique_items, name: name}) do
gettext("%{name} - Array items must be unique.", name: name)
end
defp message(%{reason: :min_items, length: min, value: array, name: name}) do
gettext("%{name} - Array length %{length} is smaller than minItems: %{min}.",
name: name,
length: length(array),
min: min
)
end
defp message(%{reason: :max_items, length: max, value: array, name: name}) do
gettext("%{name} - Array length %{length} is larger than maxItems: %{}.",
name: name,
length: length(array),
max: max
)
end
defp message(%{reason: :multiple_of, length: multiple, value: count, name: name}) do
gettext("%{name} - %{count} is not a multiple of %{multiple}.",
name: name,
count: count,
multiple: multiple
)
end
defp message(%{reason: :exclusive_max, length: max, value: value, name: name})
when value >= max do
gettext("%{name} - %{value} is larger than exclusive maximum %{max}.",
name: name,
value: value,
max: max
)
end
defp message(%{reason: :maximum, length: max, value: value, name: name})
when value > max do
gettext("%{name} - %{value} is larger than inclusive maximum %{max}.",
name: name,
value: value,
max: max
)
end
defp message(%{reason: :exclusive_multiple, length: min, value: value, name: name})
when value <= min do
gettext("%{name} - %{value} is smaller than exclusive minimum %{min}.",
name: name,
value: value,
min: min
)
end
defp message(%{reason: :minimum, length: min, value: value, name: name})
when value < min do
gettext("%{name} - %{value} is smaller than inclusive minimum %{min}.",
name: name,
value: value,
min: min
)
end
defp message(%{reason: :invalid_type, type: type, value: value, name: name}) do
gettext("%{name} - Invalid %{type}. Got: %{value}.",
name: name,
value: OpenApiSpex.TermType.type(value),
type: type
)
end
defp message(%{reason: :invalid_format, format: format, name: name}) do
gettext("%{name} - Invalid format. Expected %{format}.", name: name, format: inspect(format))
end
defp message(%{reason: :invalid_enum, name: name}) do
gettext("%{name} - Invalid value for enum.", name: name)
end
defp message(%{reason: :polymorphic_failed, type: polymorphic_type}) do
gettext("Failed to cast to any schema in %{polymorphic_type}",
polymorphic_type: polymorphic_type
)
end
defp message(%{reason: :unexpected_field, name: name}) do
gettext("Unexpected field: %{name}.", name: safe_string(name))
end
defp message(%{reason: :no_value_for_discriminator, name: field}) do
gettext("Value used as discriminator for `%{field}` matches no schemas.", name: field)
end
defp message(%{reason: :invalid_discriminator_value, name: field}) do
gettext("No value provided for required discriminator `%{field}`.", name: field)
end
defp message(%{reason: :unknown_schema, name: name}) do
gettext("Unknown schema: %{name}.", name: name)
end
defp message(%{reason: :missing_field, name: name}) do
gettext("Missing field: %{name}.", name: name)
end
defp message(%{reason: :missing_header, name: name}) do
gettext("Missing header: %{name}.", name: name)
end
defp message(%{reason: :invalid_header, name: name}) do
gettext("Invalid value for header: %{name}.", name: name)
end
defp message(%{reason: :max_properties, meta: meta}) do
gettext(
"Object property count %{property_count} is greater than maxProperties: %{max_properties}.",
property_count: meta.property_count,
max_properties: meta.max_properties
)
end
defp message(%{reason: :min_properties, meta: meta}) do
gettext(
"Object property count %{property_count} is less than minProperties: %{min_properties}",
property_count: meta.property_count,
min_properties: meta.min_properties
)
end
defp safe_string(string) do
to_string(string) |> String.slice(0..39)
end
end

View file

@ -952,7 +952,16 @@ test "returns bad_request if missing required params", %{
|> post("/api/v1/accounts", Map.delete(valid_params, attr))
|> json_response(400)
assert res == %{"error" => "Missing parameters"}
assert res == %{
"error" => "Missing field: #{attr}.",
"errors" => [
%{
"message" => "Missing field: #{attr}",
"source" => %{"pointer" => "/#{attr}"},
"title" => "Invalid value"
}
]
}
end)
end