@@ -246,20 +246,84 @@ def normalize_content_disposition(content_disposition)
246246
247247 def build_array_items_schema ( array , record : nil )
248248 return { } if array . empty?
249+ return build_property ( array . first , record : record ) if array . size == 1
250+ return build_property ( array . first , record : record ) unless array . all? { |item | item . is_a? ( Hash ) }
249251
250- merged_schema = build_property ( array . first , record : record )
252+ all_schemas = array . map { |item | build_property ( item , record : record ) }
253+ merged_schema = all_schemas . first . dup
254+ merged_schema [ :properties ] = { }
251255
252- # Future improvement - cover other types than just hashes
253- if array . size > 1 && array . all? { |item | item . is_a? ( Hash ) }
254- array [ 1 ..] . each do |item |
255- item_schema = build_property ( item , record : record )
256- merged_schema = merge_object_schemas ( merged_schema , item_schema )
256+ all_keys = all_schemas . flat_map { |s | s [ :properties ] &.keys || [ ] } . uniq
257+
258+ all_keys . each do |key |
259+ property_variations = all_schemas . map { |s | s [ :properties ] &.[]( key ) } . compact
260+
261+ next if property_variations . empty?
262+
263+ if property_variations . size == 1
264+ merged_schema [ :properties ] [ key ] = make_property_nullable ( property_variations . first )
265+ else
266+ unique_types = property_variations . map { |p | p [ :type ] } . compact . uniq
267+
268+ case unique_types . first
269+ when 'array'
270+ merged_schema [ :properties ] [ key ] = { type : 'array' }
271+ items_variations = property_variations . map { |p | p [ :items ] } . compact
272+ merged_schema [ :properties ] [ key ] [ :items ] = build_merged_schema_from_variations ( items_variations )
273+ when 'object'
274+ merged_schema [ :properties ] [ key ] = build_merged_schema_from_variations ( property_variations )
275+ else
276+ merged_schema [ :properties ] [ key ] = property_variations . first . dup
277+ end
278+
279+ merged_schema [ :properties ] [ key ] [ :nullable ] = true if property_variations . size < all_schemas . size
257280 end
258281 end
259282
283+ all_required_sets = all_schemas . map { |s | s [ :required ] || [ ] }
284+ merged_schema [ :required ] = all_required_sets . reduce ( :& ) || [ ]
285+
260286 merged_schema
261287 end
262288
289+ def build_merged_schema_from_variations ( variations )
290+ return { } if variations . empty?
291+ return variations . first if variations . size == 1
292+
293+ types = variations . map { |v | v [ :type ] } . compact . uniq
294+
295+ if types . size == 1 && types . first == 'object'
296+ merged = { type : 'object' , properties : { } }
297+ all_keys = variations . flat_map { |v | v [ :properties ] &.keys || [ ] } . uniq
298+
299+ all_keys . each do |key |
300+ prop_variations = variations . map { |v | v [ :properties ] &.[]( key ) } . compact
301+
302+ if prop_variations . size == 1
303+ merged [ :properties ] [ key ] = make_property_nullable ( prop_variations . first )
304+ elsif prop_variations . size > 1
305+ prop_types = prop_variations . map { |p | p [ :type ] } . compact . uniq
306+
307+ if prop_types . size == 1
308+ merged [ :properties ] [ key ] = prop_variations . first . dup
309+ else
310+ unique_props = prop_variations . map { |p | p . reject { |k , _ | k == :nullable } } . uniq
311+ merged [ :properties ] [ key ] = { oneOf : unique_props }
312+ end
313+
314+ merged [ :properties ] [ key ] [ :nullable ] = true if prop_variations . size < variations . size
315+ end
316+ end
317+
318+ all_required = variations . map { |v | v [ :required ] || [ ] }
319+ merged [ :required ] = all_required . reduce ( :& ) || [ ]
320+
321+ merged
322+ else
323+ variations . first
324+ end
325+ end
326+
263327 def merge_object_schemas ( schema1 , schema2 )
264328 return schema1 unless schema2 . is_a? ( Hash ) && schema1 . is_a? ( Hash )
265329 return schema1 unless schema1 [ :type ] == 'object' && schema2 [ :type ] == 'object'
0 commit comments