Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F112874
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
13 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/open_api_spex.ex b/lib/open_api_spex.ex
index 5544eda..c73794f 100644
--- a/lib/open_api_spex.ex
+++ b/lib/open_api_spex.ex
@@ -1,36 +1,36 @@
defmodule OpenApiSpex do
- alias OpenApiSpex.{OpenApi, Operation, Parameter, Reference, RequestBody, Schema, SchemaResolver}
+ alias OpenApiSpex.{OpenApi, Operation, Reference, Schema, SchemaResolver}
@moduledoc """
"""
def resolve_schema_modules(spec = %OpenApi{}) do
SchemaResolver.resolve_schema_modules(spec)
end
@doc """
Cast params to conform to a Schema or Operation spec.
"""
def cast(spec = %OpenApi{}, schema = %Schema{}, params) do
Schema.cast(schema, params, spec.compnents.schemas)
end
def cast(spec = %OpenApi{}, schema = %Reference{}, params) do
Schema.cast(schema, params, spec.compnents.schemas)
end
def cast(spec = %OpenApi{}, operation = %Operation{}, params, content_type \\ nil) do
Operation.cast(operation, params, content_type, spec.components.schemas)
end
@doc """
Validate params against a Schema or Operation spec.
"""
def validate(spec = %OpenApi{}, schema = %Schema{}, params) do
Schema.validate(schema, params, spec.components.schemas)
end
def validate(spec = %OpenApi{}, schema = %Reference{}, params) do
Schema.validate(schema, params, spec.components.schemas)
end
def validate(spec = %OpenApi{}, operation = %Operation{}, params = %{}, content_type \\ nil) do
Operation.validate(operation, params, content_type, spec.components.schemas)
end
end
diff --git a/lib/open_api_spex/schema.ex b/lib/open_api_spex/schema.ex
index e399d84..0fea192 100644
--- a/lib/open_api_spex/schema.ex
+++ b/lib/open_api_spex/schema.ex
@@ -1,302 +1,299 @@
defmodule OpenApiSpex.Schema do
alias OpenApiSpex.{
Schema, Reference, Discriminator, Xml, ExternalDocumentation
}
defstruct [
:title,
:multipleOf,
:maximum,
:exclusiveMaximum,
:minimum,
:exclusiveMinimum,
:maxLength,
:minLength,
:pattern,
:maxItems,
:minItems,
:uniqueItems,
:maxProperties,
:minProperties,
:required,
:enum,
:type,
:allOf,
:oneOf,
:anyOf,
:not,
:items,
:properties,
- :additionalProperties,
+ {:additionalProperties, true},
:description,
:format,
:default,
:nullable,
:discriminator,
:readOnly,
:writeOnly,
:xml,
:externalDocs,
:example,
:deprecated,
:"x-struct"
]
@type t :: %__MODULE__{
title: String.t,
multipleOf: number,
maximum: number,
exclusiveMaximum: boolean,
minimum: number,
exclusiveMinimum: boolean,
maxLength: integer,
minLength: integer,
pattern: String.t,
maxItems: integer,
minItems: integer,
uniqueItems: boolean,
maxProperties: integer,
minProperties: integer,
required: [String.t],
enum: [String.t],
type: String.t,
allOf: [Schema.t | Reference.t],
oneOf: [Schema.t | Reference.t],
anyOf: [Schema.t | Reference.t],
not: Schema.t | Reference.t,
items: Schema.t | Reference.t,
properties: %{String.t => Schema.t | Reference.t},
additionalProperties: boolean | Schema.t | Reference.t,
description: String.t,
format: String.t,
default: any,
nullable: boolean,
discriminator: Discriminator.t,
readOnly: boolean,
writeOnly: boolean,
xml: Xml.t,
externalDocs: ExternalDocumentation.t,
example: any,
deprecated: boolean,
"x-struct": module
}
def resolve_schema(schema = %Schema{}, _), do: schema
def resolve_schema(%Reference{"$ref": "#/components/schemas/" <> name}, schemas), do: schemas[name]
def cast(schema = %Schema{"x-struct": mod}, value, schemas) when not is_nil(mod) do
with {:ok, data} <- cast(%{schema | "x-struct": nil}, value, schemas) do
{:ok, struct(mod, data)}
end
end
-
def cast(%Schema{type: :boolean}, value, _schemas) when is_boolean(value), do: {:ok, value}
def cast(%Schema{type: :boolean}, value, _schemas) when is_binary(value) do
case value do
"true" -> true
"false" -> false
_ -> {:error, "Invalid boolean: #{inspect(value)}"}
end
end
def cast(%Schema{type: :integer}, value, _schemas) when is_integer(value), do: {:ok, value}
def cast(%Schema{type: :integer}, value, _schemas) when is_binary(value) do
case Integer.parse(value) do
{i, ""} -> {:ok, i}
_ -> {:error, :bad_integer}
end
end
def cast(%Schema{type: :number}, value, _schemas) when is_number(value), do: {:ok, value}
def cast(%Schema{type: :number}, value, _schemas) when is_binary(value) do
case Float.parse(value) do
{x, ""} -> {:ok, x}
_ -> {:error, :bad_float}
end
end
def cast(%Schema{type: :string, format: :"date-time"}, value, _schemas) when is_binary(value) do
case DateTime.from_iso8601(value) do
{:ok, datetime = %DateTime{}, _offset} -> {:ok, datetime}
error = {:error, _reason} -> error
end
end
def cast(%Schema{type: :string, format: :date}, value, _schemas) when is_binary(value) do
case Date.from_iso8601(value) do
{:ok, date = %Date{}} -> {:ok, date}
error = {:error, _reason} -> error
end
end
def cast(%Schema{type: :string}, value, _schemas) when is_binary(value), do: {:ok, value}
def cast(%Schema{type: :array, items: nil}, value, _schemas) when is_list(value), do: {:ok, value}
def cast(%Schema{type: :array}, [], _schemas), do: {:ok, []}
def cast(schema = %Schema{type: :array, items: items_schema}, [x | rest], schemas) do
with {:ok, x_cast} <- cast(items_schema, x, schemas),
{:ok, rest_cast} <- cast(schema, rest, schemas) do
{:ok, [x_cast | rest_cast]}
end
- end
- def cast(%Schema{type: :object, properties: properties}, value, schemas) when is_map(value) do
- properties
- |> Stream.filter(fn {name, _} -> Map.has_key?(value, name) || Map.has_key?(value, Atom.to_string(name)) end)
- |> Stream.map(fn {name, schema} -> {name, resolve_schema(schema, schemas)} end)
- |> Stream.map(fn {name, schema} -> {name, schema, Map.get(value, name, value[Atom.to_string(name)])} end)
- |> Stream.map(fn {name, schema, property_val} -> cast_property(name, schema, property_val, schemas) end)
- |> Enum.reduce({:ok, %{}}, fn
- _, {:error, reason} -> {:error, reason}
- {:error, reason}, _ -> {:error, reason}
- {:ok, {name, property_val}}, {:ok, acc} -> {:ok, Map.put(acc, name, property_val)}
- end)
+ def cast(schema = %Schema{type: :object}, value, schemas) when is_map(value) do
+ with {:ok, props} <- cast_properties(schema, Enum.to_list(value), schemas) do
+ {:ok, Map.new(props)}
+ end
end
def cast(ref = %Reference{}, val, schemas), do: cast(resolve_schema(ref, schemas), val, schemas)
+ def cast(additionalProperties, val, _schemas) when is_boolean(additionalProperties), do: val
+
+ defp cast_properties(%Schema{}, [], _schemas), do: {:ok, []}
+ defp cast_properties(object_schema = %Schema{}, [{key, value} | rest], schemas) do
+ {name, schema} = Enum.find(
+ object_schema.properties,
+ {key, object_schema.additionalProperties},
+ fn {name, _schema} -> to_string(name) == to_string(key) end)
- defp cast_property(name, schema, value, schemas) do
- casted = cast(schema, value, schemas)
- case casted do
- {:ok, new_value} -> {:ok, {name, new_value}}
- {:error, reason} -> {:error, reason}
+ with {:ok, new_value} <- cast(schema, value, schemas),
+ {:ok, cast_tail} <- cast_properties(object_schema, rest, schemas) do
+ {:ok, [{name, new_value} | cast_tail]}
end
end
def validate(ref = %Reference{}, val, schemas), do: validate(resolve_schema(ref, schemas), val, schemas)
def validate(schema = %Schema{type: type}, value, _schemas) when type in [:integer, :number] do
with :ok <- validate_multiple(schema, value),
:ok <- validate_maximum(schema, value),
:ok <- validate_minimum(schema, value) do
:ok
end
end
def validate(schema = %Schema{type: :string}, value, _schemas) do
with :ok <- validate_max_length(schema, value),
:ok <- validate_min_length(schema, value),
:ok <- validate_pattern(schema, value) do
:ok
end
end
def validate(%Schema{type: :boolean}, value, _schemas) do
case is_boolean(value) do
true -> :ok
_ -> {:error, "Invalid boolean: #{inspect(value)}"}
end
end
def validate(schema = %Schema{type: :array}, value, schemas) do
with :ok <- validate_max_items(schema, value),
:ok <- validate_min_items(schema, value),
:ok <- validate_unique_items(schema, value),
:ok <- validate_array_items(schema, value, schemas) do
:ok
end
end
def validate(schema = %Schema{type: :object, properties: properties}, value, schemas) do
with :ok <- validate_required_properties(schema, value),
:ok <- validate_max_properties(schema, value),
:ok <- validate_min_properties(schema, value),
:ok <- validate_object_properties(properties, value, schemas) do
:ok
end
end
def validate_multiple(%{multipleOf: nil}, _), do: :ok
def validate_multiple(%{multipleOf: n}, value) when (round(value / n) * n == value), do: :ok
def validate_multiple(%{multipleOf: n}, value), do: {:error, "#{value} is not a multiple of #{n}"}
def validate_maximum(%{maximum: nil}, _), do: :ok
def validate_maximum(%{maximum: n, exclusiveMaximum: true}, value) when value < n, do: :ok
def validate_maximum(%{maximum: n}, value) when value <= n, do: :ok
def validate_maximum(%{maximum: n}, value), do: {:error, "#{value} is larger than maximum #{n}"}
def validate_minimum(%{minimum: nil}, _), do: :ok
def validate_minimum(%{minimum: n, exclusiveMinimum: true}, value) when value > n, do: :ok
def validate_minimum(%{minimum: n}, value) when value >= n, do: :ok
def validate_minimum(%{minimum: n}, value), do: {:error, "#{value} is smaller than minimum #{n}"}
def validate_max_length(%{maxLength: nil}, _), do: :ok
def validate_max_length(%{maxLength: n}, value) do
case String.length(value) <= n do
true -> :ok
_ -> {:error, "String length is larger than maxLength: #{n}"}
end
end
def validate_min_length(%{minLength: nil}, _), do: :ok
def validate_min_length(%{minLength: n}, value) do
case String.length(value) >= n do
true -> :ok
_ -> {:error, "String length is smaller than minLength: #{n}"}
end
end
def validate_pattern(%{pattern: nil}, _), do: :ok
def validate_pattern(schema = %{pattern: regex}, val) when is_binary(regex) do
validate_pattern(%{schema | pattern: Regex.compile(regex)}, val)
end
def validate_pattern(%{pattern: regex = %Regex{}}, val) do
case Regex.match?(regex, val) do
true -> :ok
_ -> {:error, "Value does not match pattern: #{regex.source}"}
end
end
def validate_max_items(%Schema{maxItems: nil}, _), do: :ok
def validate_max_items(%Schema{maxItems: n}, value) when length(value) <= n, do: :ok
def validate_max_items(%Schema{maxItems: n}, value) do
{:error, "Array length #{length(value)} is larger than maxItems: #{n}"}
end
def validate_min_items(%Schema{minItems: nil}, _), do: :ok
def validate_min_items(%Schema{minItems: n}, value) when length(value) >= n, do: :ok
def validate_min_items(%Schema{minItems: n}, value) do
{:error, "Array length #{length(value)} is smaller than minItems: #{n}"}
end
def validate_unique_items(%Schema{uniqueItems: true}, value) do
unique_size =
value
|> MapSet.new()
|> MapSet.size()
case unique_size == length(value) do
true -> :ok
_ -> {:error, "Array items must be unique"}
end
end
def validate_unique_items(_, _), do: :ok
def validate_array_items(%Schema{type: :array, items: nil}, value, _schemas) when is_list(value), do: :ok
def validate_array_items(%Schema{type: :array}, [], _schemas), do: :ok
def validate_array_items(schema = %Schema{type: :array, items: item_schema}, [x | rest], schemas) do
with :ok <- validate(item_schema, x, schemas) do
validate(schema, rest, schemas)
end
end
def validate_required_properties(%Schema{type: :object, required: nil}, _), do: :ok
def validate_required_properties(%Schema{type: :object, required: required}, value) do
missing = required -- Map.keys(value)
case missing do
[] -> :ok
_ -> {:error, "Missing required properties: #{inspect(missing)}"}
end
end
def validate_max_properties(%Schema{type: :object, maxProperties: nil}, _), do: :ok
def validate_max_properties(%Schema{type: :object, maxProperties: n}, val) when map_size(val) <= n, do: :ok
def validate_max_properties(%Schema{type: :object, maxProperties: n}, val) do
{:error, "Object property count #{map_size(val)} is greater than maxProperties: #{n}"}
end
def validate_min_properties(%Schema{type: :object, minProperties: nil}, _), do: :ok
def validate_min_properties(%Schema{type: :object, minProperties: n}, val) when map_size(val) >= n, do: :ok
def validate_min_properties(%Schema{type: :object, minProperties: n}, val) do
{:error, "Object property count #{map_size(val)} is less than minProperties: #{n}"}
end
def validate_object_properties(properties = %{}, value, schemas) do
properties
|> Enum.filter(fn {name, _schema} -> Map.has_key?(value, name) end)
|> validate_object_properties(value, schemas)
end
def validate_object_properties([], _, _), do: :ok
def validate_object_properties([{name, schema} | rest], value, schemas) do
case validate(schema, Map.fetch!(value, name), schemas) do
:ok -> validate_object_properties(rest, value, schemas)
error -> error
end
end
end
\ No newline at end of file
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Nov 24, 2:23 AM (19 h, 23 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39263
Default Alt Text
(13 KB)
Attached To
Mode
R22 open_api_spex
Attached
Detach File
Event Timeline
Log In to Comment