@@ -12,36 +12,35 @@ defmodule OpenApiSpex.Cast.Object do
12
12
{ :ok , value }
13
13
end
14
14
15
- def cast ( % { value: value , schema: schema , schemas: schemas } = ctx ) do
16
- original_value = value
17
- schema_properties = schema . properties || % { }
15
+ def cast ( ctx ) do
16
+ ctx = handle_struct_value ( ctx )
18
17
19
- with :ok <- check_unrecognized_properties ( ctx , schema_properties ) ,
20
- resolved_schema_properties <-
21
- resolve_schema_properties_references ( schema_properties , schemas ) ,
22
- value = cast_atom_keys ( value , resolved_schema_properties ) ,
23
- ctx = % { ctx | value: value } ,
24
- { :ok , ctx } <- cast_additional_properties ( ctx , original_value ) ,
18
+ with :ok <- check_unrecognized_properties ( ctx ) ,
19
+ ctx = resolve_schema_properties_references ( ctx ) ,
20
+ ctx = cast_atom_keys ( ctx ) ,
21
+ { :ok , ctx } <- cast_additional_properties ( ctx ) ,
25
22
:ok <- Utils . check_required_fields ( ctx ) ,
26
23
:ok <- check_max_properties ( ctx ) ,
27
24
:ok <- check_min_properties ( ctx ) ,
28
- { :ok , value } <- cast_properties ( % { ctx | schema: resolved_schema_properties } ) do
29
- value_with_defaults =
30
- if Keyword . get ( ctx . opts , :apply_defaults , true ) do
31
- apply_defaults ( value , resolved_schema_properties )
32
- else
33
- value
34
- end
25
+ { :ok , ctx } <- cast_properties ( ctx ) do
26
+ ctx =
27
+ ctx
28
+ |> apply_defaults ( )
29
+ |> to_struct ( )
35
30
36
- ctx = to_struct ( % { ctx | value: value_with_defaults } , original_value )
37
31
{ :ok , ctx }
38
32
end
39
33
end
40
34
41
- defp resolve_schema_properties_references ( schema_properties , schemas ) do
42
- Enum . reduce ( schema_properties , schema_properties , fn property , properties ->
43
- resolve_property_if_reference ( property , properties , schemas )
44
- end )
35
+ defp resolve_schema_properties_references ( % { schema: schema , schemas: schemas } = ctx ) do
36
+ schema_properties = schema . properties || % { }
37
+
38
+ resolved_schema_properties =
39
+ Enum . reduce ( schema_properties , schema_properties , fn property , properties ->
40
+ resolve_property_if_reference ( property , properties , schemas )
41
+ end )
42
+
43
+ % { ctx | schema: % { schema | properties: resolved_schema_properties } }
45
44
end
46
45
47
46
defp resolve_property_if_reference ( { key , % Reference { } = reference } , properties , schemas ) do
@@ -51,14 +50,14 @@ defmodule OpenApiSpex.Cast.Object do
51
50
defp resolve_property_if_reference ( _not_a_reference , properties , _schemas ) , do: properties
52
51
53
52
# When additionalProperties is not false, extra properties are allowed in input
54
- defp check_unrecognized_properties ( % { schema: % { additionalProperties: ap } } , _expected_keys )
55
- when ap != false do
53
+ defp check_unrecognized_properties ( % { schema: % { additionalProperties: ap } } ) when ap != false do
56
54
:ok
57
55
end
58
56
59
- defp check_unrecognized_properties ( % { value: value } = ctx , expected_keys ) do
57
+ defp check_unrecognized_properties ( % { value: value , schema: schema } = ctx ) do
58
+ schema_properties = schema . properties || % { }
60
59
input_keys = value |> Map . keys ( ) |> Enum . map ( & to_string / 1 )
61
- schema_keys = expected_keys |> Map . keys ( ) |> Enum . map ( & to_string / 1 )
60
+ schema_keys = schema_properties |> Map . keys ( ) |> Enum . map ( & to_string / 1 )
62
61
extra_keys = input_keys -- schema_keys
63
62
64
63
if extra_keys == [ ] do
@@ -96,20 +95,25 @@ defmodule OpenApiSpex.Cast.Object do
96
95
97
96
defp check_min_properties ( _ctx ) , do: :ok
98
97
99
- defp cast_atom_keys ( input_map , properties ) do
100
- Enum . reduce ( properties , % { } , fn { key , _ } , output ->
101
- string_key = to_string ( key )
98
+ defp cast_atom_keys ( % { value: input_map , schema: % { properties: properties } } = ctx ) do
99
+ value =
100
+ Enum . reduce ( properties , input_map , fn { key , _ } , output ->
101
+ string_key = to_string ( key )
102
102
103
- case input_map do
104
- % { ^ key => value } -> Map . put ( output , key , value )
105
- % { ^ string_key => value } -> Map . put ( output , key , value )
106
- _ -> output
107
- end
108
- end )
103
+ if Map . has_key? ( output , string_key ) do
104
+ { value , output } = Map . pop! ( output , string_key )
105
+ Map . put ( output , key , value )
106
+ else
107
+ output
108
+ end
109
+ end )
110
+
111
+ % { ctx | value: value }
109
112
end
110
113
111
- defp cast_properties ( % { value: object , schema: schema_properties } = ctx ) do
112
- Enum . reduce ( object , { % { } , [ ] } , fn { key , value } , { output , object_errors } ->
114
+ defp cast_properties ( % { value: object , schema: % { properties: schema_properties } } = ctx ) do
115
+ object
116
+ |> Enum . reduce ( { % { } , [ ] } , fn { key , value } , { output , object_errors } ->
113
117
case cast_property ( % { ctx | key: key , value: value , schema: schema_properties } , output ) do
114
118
{ :ok , output } ->
115
119
{ output , object_errors }
@@ -119,16 +123,16 @@ defmodule OpenApiSpex.Cast.Object do
119
123
end
120
124
end )
121
125
|> case do
122
- { output , [ ] } ->
123
- { :ok , output }
126
+ { value , [ ] } ->
127
+ { :ok , % { ctx | value: value } }
124
128
125
129
{ _ , errors } ->
126
130
{ :error , errors }
127
131
end
128
132
end
129
133
130
- defp cast_additional_properties ( % { schema: % { additionalProperties: ap } } = ctx , original_value ) do
131
- original_value
134
+ defp cast_additional_properties ( % { value: value , schema: % { additionalProperties: ap } } = ctx ) do
135
+ value
132
136
|> get_additional_properties ( ctx )
133
137
|> Enum . reduce ( { :ok , ctx } , fn
134
138
{ key , value } , { :ok , ctx } ->
@@ -140,15 +144,14 @@ defmodule OpenApiSpex.Cast.Object do
140
144
end )
141
145
end
142
146
143
- defp get_additional_properties ( original_value , ctx ) do
147
+ defp get_additional_properties ( value , ctx ) do
144
148
recognized_keys =
145
149
( ctx . schema . properties || % { } )
146
150
|> Map . keys ( )
147
151
|> Enum . flat_map ( & [ & 1 , to_string ( & 1 ) ] )
148
152
|> MapSet . new ( )
149
153
150
- for { key , _value } = prop <- ensure_not_struct ( original_value ) ,
151
- not MapSet . member? ( recognized_keys , key ) do
154
+ for { key , _value } = prop <- value , not MapSet . member? ( recognized_keys , key ) do
152
155
prop
153
156
end
154
157
end
@@ -172,8 +175,15 @@ defmodule OpenApiSpex.Cast.Object do
172
175
end
173
176
end
174
177
175
- defp apply_defaults ( object_value , schema_properties ) do
176
- Enum . reduce ( schema_properties , object_value , & apply_default / 2 )
178
+ defp apply_defaults ( % { opts: opts , value: value , schema: % { properties: properties } } = ctx ) do
179
+ value =
180
+ if Keyword . get ( opts , :apply_defaults , true ) do
181
+ Enum . reduce ( properties , value , & apply_default / 2 )
182
+ else
183
+ value
184
+ end
185
+
186
+ % { ctx | value: value }
177
187
end
178
188
179
189
defp apply_default ( { _key , % { default: nil } } , object_value ) , do: object_value
@@ -188,16 +198,21 @@ defmodule OpenApiSpex.Cast.Object do
188
198
189
199
defp apply_default ( _ , object_value ) , do: object_value
190
200
191
- defp to_struct ( % { value: value = % _ { } } , _original_value ) , do: value
201
+ defp to_struct ( % { value: value = % _ { } } ) , do: value
202
+
203
+ defp to_struct ( % { value: value , schema: % { "x-struct": module } } ) when not is_nil ( module ) ,
204
+ do: struct ( module , value )
192
205
193
- defp to_struct ( % { value: value , schema: % { "x-struct": module } } , _ )
194
- when not is_nil ( module ) ,
195
- do: struct ( module , value )
206
+ defp to_struct ( % { value: value } ) , do: value
196
207
197
- defp to_struct ( % { value: value } , % original_module { } ) , do: struct ( original_module , value )
208
+ defp handle_struct_value ( % { value: % _ { } = value , schema: % { "x-struct": module } } = ctx )
209
+ when not is_nil ( module ) do
210
+ % { ctx | value: Map . from_struct ( value ) }
211
+ end
198
212
199
- defp to_struct ( % { value: value } , _original_value ) , do: value
213
+ defp handle_struct_value ( % { value: % struct { } = value , schema: schema } = ctx ) do
214
+ % { ctx | value: Map . from_struct ( value ) , schema: % { schema | "x-struct": struct } }
215
+ end
200
216
201
- defp ensure_not_struct ( val ) when is_struct ( val ) , do: Map . from_struct ( val )
202
- defp ensure_not_struct ( val ) , do: val
217
+ defp handle_struct_value ( ctx ) , do: ctx
203
218
end
0 commit comments