wip
Some checks are pending
Build and Publish / build-release (push) Waiting to run

This commit is contained in:
2026-04-13 15:30:31 -05:00
parent a52f049a29
commit 5037bc3632
44 changed files with 2210 additions and 676 deletions

View File

@@ -1,85 +0,0 @@
# lib/policy_service_web/controllers/car_policy_controller.ex
defmodule PolicyServiceWeb.CarPolicyController do
use PolicyServiceWeb, :controller
use OpenApiSpex.ControllerSpecs
alias OpenApiSpex.Schema
alias PolicyServiceWeb.Schemas.CarPolicy.{QuoteRequest, QuoteResponse}
alias PolicyService.Commands.Car.SubmitCarPolicyApplication
tags(["Car Policy"])
security([%{"bearerAuth" => []}])
operation(:request_quote,
summary: "Solicitar cotización de seguro de auto",
description: "Envía una solicitud de cotización a los proveedores seleccionados",
request_body: {"Quote request body", "application/json", QuoteRequest, required: true},
responses: [
created: {"Solicitud creada exitosamente", "application/json", QuoteResponse},
unprocessable_entity:
{"Error de validación", "application/json",
%Schema{
type: :object,
properties: %{
errors: %Schema{type: :object}
}
}}
]
)
def request_quote(conn, params) do
user = %{"id" => "test", "org_id" => "test"}
cmd = %SubmitCarPolicyApplication{
application_id: Ecto.UUID.generate(),
org_id: user["org_id"],
submitted_by: user["id"],
applicant_info: %{
name: params["applicant_info"]["name"],
date_of_birth: Date.from_iso8601!(params["applicant_info"]["date_of_birth"]),
document_id: params["applicant_info"]["document_id"]
},
car_details: %{
plate: params["car_details"]["plate"],
make: params["car_details"]["make"],
model: params["car_details"]["model"],
year: params["car_details"]["year"],
car_value: parse_number(params["car_details"]["car_value"]),
use_type: String.to_atom(params["car_details"]["use_type"]),
car_type: String.to_atom(params["car_details"]["car_type"]),
chassis_number: params["car_details"]["chassis_number"],
engine_number: params["car_details"]["engine_number"]
},
selected_providers:
Enum.map(params["selected_providers"], fn p ->
%{id: p["id"], email: p["email"]}
end)
}
case PolicyService.CommandedApp.dispatch(cmd) do
:ok ->
conn
|> put_status(:created)
|> json(%{
application_id: cmd.applicant_info,
status: "awaiting_quotes"
})
{:error, reason} ->
conn
|> put_status(:unprocessable_entity)
|> json(%{errors: reason})
end
end
defp parse_number(val) when is_float(val), do: val
defp parse_number(val) when is_integer(val), do: val * 1.0
defp parse_number(val) when is_binary(val) do
case Float.parse(val) do
{f, _} -> f
:error -> raise "invalid number: #{val}"
end
end
end

View File

@@ -0,0 +1,378 @@
defmodule PolicyServiceWeb.PolicyController do
use PolicyServiceWeb, :controller
use OpenApiSpex.ControllerSpecs
alias PolicyService.CommandedApp
alias PolicyService.Queries.PolicyQueries
alias PolicyService.Aggregates.PolicyId
alias PolicyService.Commands.CarPolicy
alias PolicyServiceWeb.Schemas.Policy, as: S
tags(["Policies"])
security([%{"bearerAuth" => []}])
# ---------------------------------------------------------------------------
# GET /api/policies
# ---------------------------------------------------------------------------
operation(:index,
summary: "List policies",
parameters: [
"page[number]": [in: :query, type: :integer, required: false],
"page[size]": [in: :query, type: :integer, required: false],
"filters[0][field]": [in: :query, type: :string, required: false],
"filters[0][op]": [in: :query, type: :string, required: false],
"filters[0][value]": [in: :query, type: :string, required: false],
"filters[1][field]": [in: :query, type: :string, required: false],
"filters[1][op]": [in: :query, type: :string, required: false],
"filters[1][value]": [in: :query, type: :string, required: false],
"order_by[]": [in: :query, type: :string, required: false]
],
responses: [
ok: {"Policy list", "application/json", S.PolicyListResponse},
bad_request: {"Invalid params", "application/json", S.ErrorResponse}
]
)
def index(conn, params) do
org_id = conn.assigns[:org_id] || "test"
case PolicyQueries.list_by_org(org_id, params) do
{:ok, {policies, meta}} ->
conn
|> put_status(:ok)
|> json(%{
data: Enum.map(policies, &policy_summary/1),
meta: meta_json(meta)
})
{:error, _} ->
conn |> put_status(:bad_request) |> json(%{error: "invalid parameters"})
end
end
# ---------------------------------------------------------------------------
# GET /api/policies/:application_id
# ---------------------------------------------------------------------------
operation(:show,
summary: "Get policy detail",
parameters: [
application_id: [in: :path, type: :string, required: true]
],
responses: [
ok: {"Policy detail", "application/json", S.PolicyDetailResponse},
not_found: {"Not found", "application/json", S.ErrorResponse}
]
)
def show(conn, %{"application_id" => application_id}) do
org_id = conn.assigns[:org_id] || "test"
case PolicyQueries.get_by_application_id(org_id, application_id) do
{:ok, policy} ->
conn |> put_status(:ok) |> json(%{data: policy_detail(policy)})
{:error, :not_found} ->
conn |> put_status(:not_found) |> json(%{error: "policy not found"})
end
end
# ---------------------------------------------------------------------------
# POST /api/policies
# ---------------------------------------------------------------------------
operation(:create,
summary: "Submit a policy quote request",
request_body: {"Quote request", "application/json", S.CreatePolicyRequest, required: true},
responses: [
created: {"Submitted", "application/json", S.QuoteResponse},
unprocessable_entity: {"Validation error", "application/json", S.ErrorResponse}
]
)
def create(conn, params) do
application_id = Ecto.UUID.generate()
org_id = conn.assigns[:org_id] || "test"
submitted_by = conn.assigns[:user_id] || "test"
with {:ok, policy_type} <- parse_policy_type(params["policy_type"]),
{:ok, applicant_info} <- parse_applicant_info(params["applicant_info"]),
{:ok, policy_details} <- parse_policy_details(policy_type, params["policy_details"]),
{:ok, providers} <- parse_providers(params["selected_providers"]) do
command =
case policy_type do
"car" ->
%CarPolicy.SubmitPolicyApplication{
id: PolicyId.new(org_id, policy_type, application_id),
submitted_by: submitted_by,
applicant_info: applicant_info,
policy_details: policy_details,
selected_providers: providers
}
end
case CommandedApp.dispatch(command, consistency: :strong) do
:ok ->
conn
|> put_status(:created)
|> json(%{application_id: application_id, status: "awaiting_quotes"})
{:error, reason} ->
conn |> put_status(:unprocessable_entity) |> json(%{error: inspect(reason)})
end
else
{:error, reason} ->
conn |> put_status(:unprocessable_entity) |> json(%{error: inspect(reason)})
end
end
# ---------------------------------------------------------------------------
# POST /api/policies/:application_id/accept
# ---------------------------------------------------------------------------
operation(:accept,
summary: "Accept a quote plan and trigger solicitation",
parameters: [
application_id: [in: :path, type: :string, required: true]
],
request_body: {"Accept quote", "application/json", S.AcceptQuoteRequest, required: true},
responses: [
ok: {"Accepted", "application/json", S.PolicyDetailResponse},
not_found: {"Not found", "application/json", S.ErrorResponse},
unprocessable_entity: {"Error", "application/json", S.ErrorResponse}
]
)
def accept(conn, %{"application_id" => application_id} = params) do
org_id = conn.assigns[:org_id] || "test"
with {:ok, policy} <- PolicyQueries.get_by_application_id(org_id, application_id) do
command =
case policy.policy_type do
"car" ->
%CarPolicy.AcceptQuoteAndSolicit{
id: PolicyId.new(org_id, policy.policy_type, application_id),
quote_id: params["quote_id"],
plan_id: params["plan_id"],
solicitation_fields: params["solicitation_fields"] || %{}
}
end
case CommandedApp.dispatch(command, consistency: :strong) do
:ok ->
{:ok, updated} = PolicyQueries.get_by_application_id(org_id, application_id)
conn |> put_status(:ok) |> json(%{data: policy_detail(updated)})
{:error, :quote_not_found} ->
conn |> put_status(:not_found) |> json(%{error: "quote not found"})
{:error, :plan_not_found} ->
conn |> put_status(:not_found) |> json(%{error: "plan not found"})
{:error, reason} ->
conn |> put_status(:unprocessable_entity) |> json(%{error: inspect(reason)})
end
else
{:error, :not_found} ->
conn |> put_status(:not_found) |> json(%{error: "policy not found"})
end
end
# ---------------------------------------------------------------------------
# GET /api/policies/:application_id/solicitation-url
# ---------------------------------------------------------------------------
operation(:solicitation_url,
summary: "Get fresh presigned download URL for solicitation PDF",
parameters: [
application_id: [in: :path, type: :string, required: true],
version: [in: :query, type: :integer, required: false]
],
responses: [
ok: {"Presigned URL", "application/json", S.SolicitationUrlResponse},
not_found: {"Not found", "application/json", S.ErrorResponse}
]
)
def solicitation_url(conn, %{"application_id" => application_id} = params) do
org_id = conn.assigns[:org_id] || "test"
version = String.to_integer(params["version"] || "1")
case PolicyQueries.get_by_application_id(org_id, application_id) do
{:error, :not_found} ->
conn |> put_status(:not_found) |> json(%{error: "policy not found"})
{:ok, %{solicitation_id: nil}} ->
conn |> put_status(:not_found) |> json(%{error: "no solicitation yet"})
{:ok, policy} ->
url =
"#{solicitation_service_url()}/api/solicitations/#{policy.solicitation_id}/download-url"
case Req.get(url,
params: [org_id: org_id, application_id: application_id, version: version]
) do
{:ok, %{status: 200, body: body}} ->
conn |> put_status(:ok) |> json(body)
{:ok, %{status: status, body: body}} ->
conn
|> put_status(:bad_gateway)
|> json(%{error: "solicitation service returned #{status}: #{inspect(body)}"})
{:error, reason} ->
conn |> put_status(:bad_gateway) |> json(%{error: inspect(reason)})
end
end
end
# ---------------------------------------------------------------------------
# Serializers
# ---------------------------------------------------------------------------
defp policy_summary(p) do
%{
application_id: p.application_id,
policy_type: p.policy_type,
status: p.status,
applicant_info: p.applicant_info,
policy_details: p.policy_details,
policy_number: p.policy_number,
submitted_at: p.submitted_at
}
end
defp policy_detail(p) do
%{
application_id: p.application_id,
org_id: p.org_id,
submitted_by: p.submitted_by,
policy_type: p.policy_type,
status: p.status,
applicant_info: p.applicant_info,
policy_details: p.policy_details,
selected_providers: p.selected_providers,
quotes: p.quotes,
accepted_quote_id: p.accepted_quote_id,
accepted_plan_id: p.accepted_plan_id,
accepted_provider_id: p.accepted_provider_id,
accepted_at: p.accepted_at,
solicitation_id: p.solicitation_id,
solicitation_s3_key: p.solicitation_s3_key,
policy_number: p.policy_number,
premium: p.premium,
effective_date: p.effective_date,
expiry_date: p.expiry_date,
submitted_at: p.submitted_at,
solicitation_sent_at: p.solicitation_sent_at,
issued_at: p.issued_at
}
end
defp meta_json(meta) do
%{
total_count: meta.total_count,
total_pages: meta.total_pages,
current_page: meta.current_page,
page_size: meta.page_size,
has_next: meta.has_next_page?,
has_prev: meta.has_previous_page?
}
end
# ---------------------------------------------------------------------------
# Parse helpers
# ---------------------------------------------------------------------------
defp parse_policy_type(type) when type in ["car", "life", "fire"], do: {:ok, type}
defp parse_policy_type(_), do: {:error, :invalid_policy_type}
# individual — has document_id
defp parse_applicant_info(%{"document_id" => doc} = info)
when is_binary(doc) and byte_size(doc) > 0 do
case info["date_of_birth"] do
nil ->
{:error, :missing_date_of_birth}
dob ->
{:ok,
%{
"name" => info["name"],
"date_of_birth" => dob,
"document_id" => doc
}}
end
end
# corporate — has ruc
defp parse_applicant_info(%{"ruc" => ruc} = info)
when is_binary(ruc) and byte_size(ruc) > 0 do
{:ok,
%{
"company_name" => info["company_name"],
"ruc" => ruc,
"legal_rep_name" => info["legal_rep_name"],
"legal_rep_document" => info["legal_rep_document"]
}}
end
defp parse_applicant_info(_), do: {:error, :invalid_applicant_info}
# car details
defp parse_policy_details("car", nil), do: {:error, :missing_policy_details}
defp parse_policy_details("car", d) do
{:ok,
%{
"plate" => d["plate"],
"make" => d["make"],
"model" => d["model"],
"year" => d["year"],
"car_value" => d["car_value"],
"use_type" => d["use_type"],
"car_type" => d["car_type"],
"chassis_number" => d["chassis_number"],
"engine_number" => d["engine_number"]
}}
end
# life details
defp parse_policy_details("life", nil), do: {:error, :missing_policy_details}
defp parse_policy_details("life", d) do
{:ok,
%{
"coverage_amount" => d["coverage_amount"],
"beneficiary" => d["beneficiary"]
}}
end
# fire details
defp parse_policy_details("fire", nil), do: {:error, :missing_policy_details}
defp parse_policy_details("fire", d) do
{:ok,
%{
"property_address" => d["property_address"],
"property_value" => d["property_value"]
}}
end
defp parse_policy_details(_, _), do: {:error, :invalid_policy_details}
defp parse_providers(nil), do: {:error, :missing_providers}
defp parse_providers([]), do: {:error, :no_providers_selected}
defp parse_providers(list) when is_list(list) do
{:ok, Enum.map(list, fn p -> %{provider_id: p["provider_id"], email: p["email"]} end)}
end
defp parse_providers(_), do: {:error, :invalid_providers}
defp solicitation_service_url do
Application.get_env(:policy_service, :solicitation_service_url, "http://localhost:8081")
end
end

View File

@@ -42,6 +42,7 @@ defmodule PolicyServiceWeb.Endpoint do
pass: ["*/*"],
json_decoder: Phoenix.json_library()
plug CORSPlug, origin: ["http://localhost:3000"]
plug Plug.MethodOverride
plug Plug.Head
plug Plug.Session, @session_options

View File

@@ -1,6 +1,8 @@
defmodule PolicyServiceWeb.Router do
use PolicyServiceWeb, :router
alias PolicyServiceWeb.PolicyController
pipeline :api do
plug OpenApiSpex.Plug.PutApiSpec, module: PolicyServiceWeb.ApiSpec
end
@@ -11,13 +13,14 @@ defmodule PolicyServiceWeb.Router do
get "/openapi", OpenApiSpex.Plug.RenderSpec, []
scope "/v1" do
scope "/car-policies" do
post "/quotes", PolicyServiceWeb.CarPolicyController, :request_quote
end
get "/policies", PolicyController, :index
get "/policies/:application_id", PolicyController, :show
post "/policies", PolicyController, :create
post "/policies/:application_id/accept", PolicyController, :accept
get "/policies/:application_id/solicitation-url", PolicyController, :solicitation_url
end
end
# Swagger UI — only in dev
if Mix.env() == :dev do
scope "/swaggerui" do
get "/", OpenApiSpex.Plug.SwaggerUI, path: "/api/openapi"

View File

@@ -1,114 +0,0 @@
defmodule PolicyServiceWeb.Schemas.CarPolicy do
alias OpenApiSpex.Schema
defmodule ApplicantInfo do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "ApplicantInfo",
type: :object,
required: [:name, :date_of_birth, :document_id],
properties: %{
name: %Schema{type: :string, example: "Juan Pérez"},
date_of_birth: %Schema{type: :string, format: :date, example: "1985-06-15"},
document_id: %Schema{type: :string, example: "V-12345678"}
}
})
end
defmodule CarDetails do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "CarDetails",
type: :object,
required: [
:plate,
:make,
:model,
:year,
:car_value,
:use_type,
:car_type,
:chassis_number,
:engine_number
],
properties: %{
plate: %Schema{type: :string, example: "ABC-1234"},
make: %Schema{type: :string, example: "Toyota"},
model: %Schema{type: :string, example: "Corolla"},
year: %Schema{type: :integer, example: 2022},
car_value: %Schema{type: :number, example: 18000},
use_type: %Schema{
type: :string,
enum: ["private", "commercial", "bus", "taxi", "school"],
example: "private"
},
car_type: %Schema{
type: :string,
enum: [
"sedan",
"suv",
"hatchback",
"coupe",
"convertible",
"pickup",
"van",
"minivan",
"truck"
],
example: "sedan"
},
chassis_number: %Schema{type: :string, example: "9BWZZZ377VT004251"},
engine_number: %Schema{type: :string, example: "1NZ-FE-1234567"}
}
})
end
defmodule Provider do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "Provider",
type: :object,
required: [:id, :email],
properties: %{
id: %Schema{type: :string, example: "provider-uuid"},
email: %Schema{type: :string, format: :email, example: "cotizaciones@aseguradora.com"}
}
})
end
defmodule QuoteRequest do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "QuoteRequest",
type: :object,
required: [:applicant_info, :car_details, :selected_providers],
properties: %{
applicant_info: ApplicantInfo,
car_details: CarDetails,
selected_providers: %Schema{
type: :array,
items: Provider,
minItems: 1,
example: [%{id: "provider-uuid", email: "cotizaciones@aseguradora.com"}]
}
}
})
end
defmodule QuoteResponse do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "QuoteResponse",
type: :object,
properties: %{
application_id: %Schema{type: :string, example: "550e8400-e29b-41d4-a716-446655440000"},
status: %Schema{type: :string, example: "awaiting_quotes"}
}
})
end
end

View File

@@ -0,0 +1,368 @@
defmodule PolicyServiceWeb.Schemas.Policy do
alias OpenApiSpex.Schema
defmodule PaginationMeta do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "PaginationMeta",
type: :object,
properties: %{
total_count: %Schema{type: :integer},
total_pages: %Schema{type: :integer},
current_page: %Schema{type: :integer},
page_size: %Schema{type: :integer},
has_next: %Schema{type: :boolean},
has_prev: %Schema{type: :boolean}
}
})
end
# ---------------------------------------------------------------------------
# Applicant — discriminated by presence of keys
# ---------------------------------------------------------------------------
defmodule ApplicantIndividual do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "ApplicantIndividual",
type: :object,
required: [:name, :date_of_birth, :document_id],
properties: %{
name: %Schema{type: :string, example: "Juan Pérez"},
date_of_birth: %Schema{type: :string, format: :date, example: "1985-06-15"},
document_id: %Schema{type: :string, example: "8-123-456"}
}
})
end
defmodule ApplicantCorporate do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "ApplicantCorporate",
type: :object,
required: [:company_name, :ruc, :legal_rep_name, :legal_rep_document],
properties: %{
company_name: %Schema{type: :string, example: "Empresa ABC S.A."},
ruc: %Schema{type: :string, example: "123456-1-123456"},
legal_rep_name: %Schema{type: :string, example: "María García"},
legal_rep_document: %Schema{type: :string, example: "8-456-789"}
}
})
end
defmodule ApplicantInfo do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "ApplicantInfo",
oneOf: [ApplicantIndividual, ApplicantCorporate]
})
end
# ---------------------------------------------------------------------------
# Policy details — one per policy type
# ---------------------------------------------------------------------------
defmodule CarPolicyDetails do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "CarPolicyDetails",
type: :object,
required: [
:plate,
:make,
:model,
:year,
:car_value,
:use_type,
:car_type,
:chassis_number,
:engine_number
],
properties: %{
plate: %Schema{type: :string, example: "ABC-1234"},
make: %Schema{type: :string, example: "Toyota"},
model: %Schema{type: :string, example: "Corolla"},
year: %Schema{type: :integer, example: 2022},
car_value: %Schema{type: :number, example: 18000},
use_type: %Schema{type: :string, enum: ["private", "commercial", "bus", "taxi", "school"]},
car_type: %Schema{
type: :string,
enum: [
"sedan",
"suv",
"hatchback",
"coupe",
"convertible",
"pickup",
"van",
"minivan",
"truck"
]
},
chassis_number: %Schema{type: :string, example: "9BWZZZ377VT004251"},
engine_number: %Schema{type: :string, example: "1NZ-FE-1234567"}
}
})
end
defmodule LifePolicyDetails do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "LifePolicyDetails",
type: :object,
required: [:coverage_amount, :beneficiary],
properties: %{
coverage_amount: %Schema{type: :number, example: 100_000},
beneficiary: %Schema{type: :string, example: "María Pérez"}
}
})
end
defmodule FirePolicyDetails do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "FirePolicyDetails",
type: :object,
required: [:property_address, :property_value],
properties: %{
property_address: %Schema{type: :string, example: "Calle 50, Panama City"},
property_value: %Schema{type: :number, example: 250_000}
}
})
end
defmodule PolicyDetails do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "PolicyDetails",
oneOf: [CarPolicyDetails, LifePolicyDetails, FirePolicyDetails]
})
end
# ---------------------------------------------------------------------------
# Shared
# ---------------------------------------------------------------------------
defmodule SelectedProvider do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "SelectedProvider",
type: :object,
required: [:provider_id, :email],
properties: %{
provider_id: %Schema{type: :string, format: :uuid},
email: %Schema{type: :string, format: :email}
}
})
end
defmodule Plan do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "Plan",
type: :object,
properties: %{
plan_id: %Schema{type: :string},
name: %Schema{type: :string},
premium: %Schema{type: :number},
coverage_details: %Schema{type: :string},
deductible: %Schema{type: :number, nullable: true},
coverage_limit: %Schema{type: :number, nullable: true}
}
})
end
defmodule QuoteData do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "QuoteData",
type: :object,
properties: %{
quote_id: %Schema{type: :string},
valid_until: %Schema{type: :string, format: :date},
received_at: %Schema{type: :string, format: :"date-time"},
plans: %Schema{type: :array, items: Plan}
}
})
end
# ---------------------------------------------------------------------------
# Requests
# ---------------------------------------------------------------------------
defmodule CreatePolicyRequest do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "CreatePolicyRequest",
type: :object,
required: [:policy_type, :applicant_info, :policy_details, :selected_providers],
properties: %{
policy_type: %Schema{
type: :string,
enum: ["car", "life", "fire"],
description: "Determines the shape of policy_details"
},
applicant_info: ApplicantInfo,
policy_details: PolicyDetails,
selected_providers: %Schema{type: :array, items: SelectedProvider, minItems: 1}
}
})
end
defmodule AcceptQuoteRequest do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "AcceptQuoteRequest",
type: :object,
required: [:quote_id, :plan_id],
properties: %{
quote_id: %Schema{type: :string},
plan_id: %Schema{type: :string},
solicitation_fields: %Schema{
type: :object,
additionalProperties: %Schema{type: :string},
description: "Optional flat map of AcroForm field names to values",
nullable: true
}
}
})
end
# ---------------------------------------------------------------------------
# Responses
# ---------------------------------------------------------------------------
defmodule QuoteResponse do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "QuoteResponse",
type: :object,
properties: %{
application_id: %Schema{type: :string},
status: %Schema{type: :string}
}
})
end
defmodule SolicitationUrlResponse do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "SolicitationUrlResponse",
type: :object,
properties: %{
download_url: %Schema{type: :string},
s3_key: %Schema{type: :string},
version: %Schema{type: :integer}
}
})
end
defmodule PolicySummary do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "PolicySummary",
type: :object,
properties: %{
application_id: %Schema{type: :string},
policy_type: %Schema{type: :string, enum: ["car", "life", "fire"]},
status: %Schema{
type: :string,
enum: ["quote_requested", "quotes_received", "solicitation_sent", "issued"]
},
applicant_info: ApplicantInfo,
policy_details: PolicyDetails,
policy_number: %Schema{type: :string, nullable: true},
submitted_at: %Schema{type: :string, format: :"date-time"}
}
})
end
defmodule PolicyDetail do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "PolicyDetail",
type: :object,
properties: %{
application_id: %Schema{type: :string},
org_id: %Schema{type: :string},
submitted_by: %Schema{type: :string},
policy_type: %Schema{type: :string, enum: ["car", "life", "fire"]},
status: %Schema{
type: :string,
enum: ["quote_requested", "quotes_received", "solicitation_sent", "issued"]
},
applicant_info: ApplicantInfo,
policy_details: PolicyDetails,
selected_providers: %Schema{type: :array, items: %Schema{type: :string}},
quotes: %Schema{type: :object, additionalProperties: QuoteData},
accepted_quote_id: %Schema{type: :string, nullable: true},
accepted_plan_id: %Schema{type: :string, nullable: true},
accepted_provider_id: %Schema{type: :string, nullable: true},
accepted_at: %Schema{type: :string, format: :"date-time", nullable: true},
solicitation_id: %Schema{type: :string, nullable: true},
solicitation_s3_key: %Schema{type: :string, nullable: true},
policy_number: %Schema{type: :string, nullable: true},
premium: %Schema{type: :number, nullable: true},
effective_date: %Schema{type: :string, format: :date, nullable: true},
expiry_date: %Schema{type: :string, format: :date, nullable: true},
submitted_at: %Schema{type: :string, format: :"date-time"},
solicitation_sent_at: %Schema{type: :string, format: :"date-time", nullable: true},
issued_at: %Schema{type: :string, format: :"date-time", nullable: true}
}
})
end
defmodule PolicyListResponse do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "PolicyListResponse",
type: :object,
properties: %{
data: %Schema{type: :array, items: PolicySummary},
meta: PaginationMeta
}
})
end
defmodule PolicyDetailResponse do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "PolicyDetailResponse",
type: :object,
properties: %{
data: PolicyDetail
}
})
end
defmodule ErrorResponse do
require OpenApiSpex
OpenApiSpex.schema(%{
title: "ErrorResponse",
type: :object,
properties: %{
error: %Schema{type: :string}
}
})
end
end