Page MenuHomePhorge

No OneTemporary

Size
12 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/open_api_spex/schema_resolver.ex b/lib/open_api_spex/schema_resolver.ex
index 91118cd..c09eae5 100644
--- a/lib/open_api_spex/schema_resolver.ex
+++ b/lib/open_api_spex/schema_resolver.ex
@@ -1,185 +1,185 @@
defmodule OpenApiSpex.SchemaResolver do
@moduledoc """
Internal module used to resolve `OpenApiSpex.Schema` structs from atoms.
"""
alias OpenApiSpex.{
OpenApi,
Components,
PathItem,
Operation,
Parameter,
Reference,
MediaType,
Schema,
RequestBody,
Response
}
@doc """
Adds schemas to the api spec from the modules specified in the Operations.
Eg, if the response schema for an operation is defined with:
responses: %{
200 => Operation.response("User", "application/json", UserResponse)
}
Then the `UserResponse.schema()` function will be called to load the schema, and
a `Reference` to the loaded schema will be used in the operation response.
See `OpenApiSpex.schema` macro for a convenient syntax for defining schema modules.
"""
@spec resolve_schema_modules(OpenApi.t) :: OpenApi.t
def resolve_schema_modules(spec = %OpenApi{}) do
components = spec.components || %Components{}
schemas = components.schemas || %{}
{paths, schemas} = resolve_schema_modules_from_paths(spec.paths, schemas)
schemas = resolve_schema_modules_from_schemas(schemas)
%{spec | paths: paths, components: %{components| schemas: schemas}}
end
defp resolve_schema_modules_from_paths(paths = %{}, schemas = %{}) do
Enum.reduce(paths, {paths, schemas}, fn {path, path_item}, {paths, schemas} ->
{new_path_item, schemas} = resolve_schema_modules_from_path_item(path_item, schemas)
{Map.put(paths, path, new_path_item), schemas}
end)
end
defp resolve_schema_modules_from_path_item(path = %PathItem{}, schemas) do
path
|> Map.from_struct()
|> Enum.filter(fn {_k, v} -> match?(%Operation{}, v) end)
|> Enum.reduce({path, schemas}, fn {k, operation}, {path, schemas} ->
{new_operation, schemas} = resolve_schema_modules_from_operation(operation, schemas)
{Map.put(path, k, new_operation), schemas}
end)
end
defp resolve_schema_modules_from_operation(operation = %Operation{}, schemas) do
{parameters, schemas} = resolve_schema_modules_from_parameters(operation.parameters, schemas)
{request_body, schemas} = resolve_schema_modules_from_request_body(operation.requestBody, schemas)
{responses, schemas} = resolve_schema_modules_from_responses(operation.responses, schemas)
new_operation = %{operation | parameters: parameters, requestBody: request_body, responses: responses}
{new_operation, schemas}
end
defp resolve_schema_modules_from_parameters(nil, schemas), do: {nil, schemas}
defp resolve_schema_modules_from_parameters(parameters, schemas) do
{parameters, schemas} =
Enum.reduce(parameters, {[], schemas}, fn parameter, {parameters, schemas} ->
{new_parameter, schemas} = resolve_schema_modules_from_parameter(parameter, schemas)
{[new_parameter | parameters], schemas}
end)
{Enum.reverse(parameters), schemas}
end
- defp resolve_schema_modules_from_parameter(parameter = %Parameter{schema: schema, content: nil}, schemas) when is_atom(schema) do
- {ref, new_schemas} = resolve_schema_modules_from_schema(schema, schemas)
- new_parameter = %{parameter | schema: ref}
- {new_parameter, new_schemas}
+ defp resolve_schema_modules_from_parameter(parameter = %Parameter{schema: schema, content: nil}, schemas) do
+ {schema, schemas} = resolve_schema_modules_from_schema(schema, schemas)
+ new_parameter = %{parameter | schema: schema}
+ {new_parameter, schemas}
end
defp resolve_schema_modules_from_parameter(parameter = %Parameter{schema: nil, content: content = %{}}, schemas) do
{new_content, schemas} = resolve_schema_modules_from_content(content, schemas)
{%{parameter | content: new_content}, schemas}
end
defp resolve_schema_modules_from_parameter(parameter = %Parameter{}, schemas) do
{parameter, schemas}
end
defp resolve_schema_modules_from_parameter(parameter = %Reference{}, schemas) do
{parameter, schemas}
end
defp resolve_schema_modules_from_content(nil, schemas), do: {nil, schemas}
defp resolve_schema_modules_from_content(content, schemas) do
Enum.reduce(content, {content, schemas}, fn {mime, media}, {content, schemas} ->
{new_media, schemas} = resolve_schema_modules_from_media_type(media, schemas)
{Map.put(content, mime, new_media), schemas}
end)
end
- defp resolve_schema_modules_from_media_type(media = %MediaType{schema: schema}, schemas) when is_atom(schema) do
- {ref, new_schemas} = resolve_schema_modules_from_schema(schema, schemas)
- new_media = %{media | schema: ref}
- {new_media, new_schemas}
+ defp resolve_schema_modules_from_media_type(media = %MediaType{schema: schema}, schemas) do
+ {schema, schemas} = resolve_schema_modules_from_schema(schema, schemas)
+ new_media = %{media | schema: schema}
+ {new_media, schemas}
end
defp resolve_schema_modules_from_media_type(media = %MediaType{}, schemas) do
{media, schemas}
end
defp resolve_schema_modules_from_request_body(nil, schemas), do: {nil, schemas}
defp resolve_schema_modules_from_request_body(request_body = %RequestBody{}, schemas) do
{content, schemas} = resolve_schema_modules_from_content(request_body.content, schemas)
new_request_body = %{request_body | content: content}
{new_request_body, schemas}
end
defp resolve_schema_modules_from_request_body(request_body = %Reference{}, schemas) do
{request_body, schemas}
end
defp resolve_schema_modules_from_responses(responses = %{}, schemas = %{}) do
Enum.reduce(responses, {responses, schemas}, fn {status, response}, {responses, schemas} ->
{new_response, schemas} = resolve_schema_modules_from_response(response, schemas)
{Map.put(responses, status, new_response), schemas}
end)
end
defp resolve_schema_modules_from_response(response = %Response{}, schemas = %{}) do
{content, schemas} = resolve_schema_modules_from_content(response.content, schemas)
new_response = %{response | content: content}
{new_response, schemas}
end
defp resolve_schema_modules_from_schemas(schemas = %{}) do
Enum.reduce(schemas, schemas, fn {name, schema}, schemas ->
{schema, schemas} = resolve_schema_modules_from_schema(schema, schemas)
Map.put(schemas, name, schema)
end)
end
defp resolve_schema_modules_from_schema(false, schemas), do: {false, schemas}
defp resolve_schema_modules_from_schema(true, schemas), do: {true, schemas}
defp resolve_schema_modules_from_schema(nil, schemas), do: {nil, schemas}
defp resolve_schema_modules_from_schema(schema_list, schemas) when is_list(schema_list) do
Enum.map_reduce(schema_list, schemas, &resolve_schema_modules_from_schema/2)
end
defp resolve_schema_modules_from_schema(schema, schemas) when is_atom(schema) do
title = schema.schema().title
new_schemas =
if Map.has_key?(schemas, title) do
schemas
else
{new_schema, schemas} = resolve_schema_modules_from_schema(schema.schema(), schemas)
Map.put(schemas, title, new_schema)
end
{%Reference{"$ref": "#/components/schemas/#{title}"}, new_schemas}
end
defp resolve_schema_modules_from_schema(schema = %Schema{}, schemas) do
{all_of, schemas} = resolve_schema_modules_from_schema(schema.allOf, schemas)
{one_of, schemas} = resolve_schema_modules_from_schema(schema.oneOf, schemas)
{any_of, schemas} = resolve_schema_modules_from_schema(schema.anyOf, schemas)
{not_schema, schemas} = resolve_schema_modules_from_schema(schema.not, schemas)
{items, schemas} = resolve_schema_modules_from_schema(schema.items, schemas)
{additional, schemas} = resolve_schema_modules_from_schema(schema.additionalProperties, schemas)
{properties, schemas} = resolve_schema_modules_from_schema_properties(schema.properties, schemas)
schema =
%{schema |
allOf: all_of,
oneOf: one_of,
anyOf: any_of,
not: not_schema,
items: items,
additionalProperties: additional,
properties: properties
}
{schema, schemas}
end
defp resolve_schema_modules_from_schema(ref = %Reference{}, schemas), do: {ref, schemas}
defp resolve_schema_modules_from_schema_properties(nil, schemas), do: {nil, schemas}
defp resolve_schema_modules_from_schema_properties(properties, schemas) do
Enum.reduce(properties, {properties, schemas}, fn {name, property}, {properties, schemas} ->
{new_property, schemas} = resolve_schema_modules_from_schema(property, schemas)
{Map.put(properties, name, new_property), schemas}
end)
end
end
diff --git a/test/schema_resolver_test.exs b/test/schema_resolver_test.exs
index 32de8e0..cda9fe7 100644
--- a/test/schema_resolver_test.exs
+++ b/test/schema_resolver_test.exs
@@ -1,95 +1,123 @@
defmodule OpenApiSpex.SchemaResolverTest do
use ExUnit.Case
alias OpenApiSpex.{
Info,
MediaType,
OpenApi,
Operation,
PathItem,
Reference,
RequestBody,
Response,
Schema
}
test "Resolves schemas in OpenApi spec" do
spec = %OpenApi{
info: %Info{
title: "Test",
version: "1.0.0"
},
paths: %{
"/api/users" => %PathItem{
get: %Operation{
responses: %{
200 => %Response{
description: "Success",
content: %{
"application/json" => %MediaType{
schema: OpenApiSpexTest.Schemas.UsersResponse
}
}
}
}
},
post: %Operation{
description: "Create a user",
operationId: "UserController.create",
requestBody: %RequestBody{
content: %{
"application/json" => %MediaType{
schema: OpenApiSpexTest.Schemas.UserRequest
}
}
},
responses: %{
201 => %Response{
description: "Created",
content: %{
"application/json" => %MediaType{
schema: OpenApiSpexTest.Schemas.UserResponse
}
}
}
}
}
},
"/api/users/{id}/payment_details" => %PathItem{
get: %Operation{
responses: %{
200 => %Response{
description: "Success",
content: %{
"application/json" => %MediaType{
schema: OpenApiSpexTest.Schemas.PaymentDetails
}
}
}
}
}
+ },
+ "/api/users/{id}/friends" => %PathItem{
+ get: %Operation{
+ parameters: [
+ %OpenApiSpex.Parameter{
+ name: :id,
+ in: :path,
+ schema: %Schema{type: :integer}
+ }
+ ],
+ responses: %{
+ 200 => %Response{
+ description: "Success",
+ content: %{
+ "application/json" => %MediaType{
+ schema: %Schema{
+ type: :array,
+ items: OpenApiSpexTest.Schemas.User
+ }
+ }
+ }
+ }
+ }
+ }
}
}
}
resolved = OpenApiSpex.resolve_schema_modules(spec)
assert %Reference{"$ref": "#/components/schemas/UsersResponse"} =
resolved.paths["/api/users"].get.responses[200].content["application/json"].schema
assert %Reference{"$ref": "#/components/schemas/UserResponse"} =
resolved.paths["/api/users"].post.responses[201].content["application/json"].schema
assert %Reference{"$ref": "#/components/schemas/UserRequest"} =
resolved.paths["/api/users"].post.requestBody.content["application/json"].schema
assert %{
"UserRequest" => %Schema{},
"UserResponse" => %Schema{},
"User" => %Schema{},
"PaymentDetails" => %Schema{},
"CreditCardPaymentDetails" => %Schema{},
"DirectDebitPaymentDetails" => %Schema{}
} = resolved.components.schemas
+
+ get_friends = resolved.paths["/api/users/{id}/friends"].get
+ assert %Reference{"$ref": "#/components/schemas/User"} =
+ get_friends.responses[200].content["application/json"].schema.items
end
end

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 29, 8:46 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41305
Default Alt Text
(12 KB)

Event Timeline