Let’s say I have next controller where {:error, _, _}
action result processed by FallbackController
:
defmodule SomeProjectWeb.Api.V1.SessionController do
use SomeProjectWeb, :controller
alias SomeProject.Session
alias SomeProject.Session.Otp
alias SomeProjectWeb.Api.V1.CastAndValidateRenderError
alias SomeProjectWeb.Api.V1.FallbackController
alias SomeProjectWeb.Api.V1.{CommonSchema, SessionSchema}
plug(OpenApiSpex.Plug.CastAndValidate, render_error: CastAndValidateRenderError)
action_fallback(FallbackController)
tags(["session"])
def action(conn, _) do
apply(__MODULE__, action_name(conn), [conn, conn.params, conn.body_params])
end
operation(:otp_create,
summary: "otp_create summary",
description: "otp_create description",
requestBody: {
"request body description",
"application/json",
SessionSchema.OtpCreateRequest,
required: true
},
responses: [
ok: {
"ok description",
"application/json",
SessionSchema.OtpCreateResponse
},
unauthorized: "...",
unprocessable_entity: {
"Unprocessable Content, ...",
"application/json",
%Schema{
allOf: [
CommonSchema.ErrorResponse,
%Schema{
type: :object,
properties: %{
code: %Schema{
enum: [
:no_otp_delivery_channel,
:runout_signup_account
]
}
}
}
]
}
},
internal_server_error: {
"Internal Server Error, ...",
"application/json",
%Schema{
allOf: [
CommonSchema.ErrorResponse,
%Schema{
type: :object,
properties: %{
code: %Schema{
enum: [
:external_api,
:demo_account_manager
]
}
}
}
]
}
}
]
)
def otp_create(conn, _params, %{user_email: email} = _body_params) do
case SomeProject.DemoAccountManager.retrieve(email) do
{:ok, i_account} ->
case SomeProject.ExternalApi.create_otp(i_account) do
{200, %{"success" => 1, "type" => "email", "from" => email}} ->
otp = Session.create_otp(%{i_account: i_account, demo: true})
render(conn, otp_id: otp.id, type: "email", from: email)
{500, %{"faultcode" => "channel-error"}} ->
{:error, :unprocessable_entity, :no_otp_delivery_channel}
_ ->
{:error, :internal_server_error, :external_api}
end
{:error, :runout} ->
{:error, :unprocessable_entity, :runout_signup_account}
{:error, _} ->
{:error, :internal_server_error, :demo_account_manager}
end
end
end
For now, I manually sync possible error codes for each result with relative result schema. That is diffidently not convenient and will lead to unsync in the future.
I want to implement some type of module macro that will collect and group all its functions :error
results and introduce the helper function to access them.
So I could replace:
internal_server_error: {
"Internal Server Error, ...",
"application/json",
%Schema{
allOf: [
CommonSchema.ErrorResponse,
%Schema{
type: :object,
properties: %{
code: %Schema{
enum: [
:external_api,
:demo_account_manager
]
}
}
}
]
}
}
with:
internal_server_error: {
"Internal Server Error, ...",
"application/json",
%Schema{
allOf: [
CommonSchema.ErrorResponse,
%Schema{
type: :object,
properties: %{
code: %Schema{
enum: error_codes(:otp_create, :internal_server_error)
}
}
}
]
}
}
(for sure such the final helper function could produce a more significant piece of repetitive code, but here I want to highlight its core functionality)
What is the best way to implement such functionality?