rspec-openapi 0.21.0 → 0.21.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b40fcb7598d29cf3c8bf2f95f6aafb5a011ddbb2beff06d6366052d11effa02
4
- data.tar.gz: 85bd479d5b7e4da9e19dcc8bbbb963a594eed99437cb911a965695d04768a4bd
3
+ metadata.gz: 3d8b74f9e75f015e91e3ebc37ab353459d0a8d887fb096ad7e9fcf829898290e
4
+ data.tar.gz: 1fdbc903fdb61f685ca02d63a88a82eb9a3184e00a0766f1e31e82001ed068b1
5
5
  SHA512:
6
- metadata.gz: 89c7e3788cfccd022711d1ce76be3ee1b1568af4bade87908afb3384e64e88714a0d4b30499f0eeebb45909fe5eab5267c96afc231ea1db60a6b2fe8fc0155d6
7
- data.tar.gz: 71e418493c5f3db1c8a5388bd2826fe36e15375445d3ddc7c1b2364cdfb7d160cdf3c9b02554e6134abf82e0f994355b725223c773857fb034e3b7eb034c2db7
6
+ metadata.gz: eb64851800a090a4cf4d9f78921f1203456ddf8b9d4e820af73d9449a259c7d6e5dd9997e01058136b0dc7b551bc5406e2e176f2818c27b6d7e5a6a00a136fb3
7
+ data.tar.gz: 82600b4917083f3b4a27f7bf4ee8b8b4314dc76a4c9c03f5143e5e182d652edea3cb6cef97f7b2f3598a2c27f143fdcb714058143d47fb44d2ccb6181fafe4fd
@@ -25,7 +25,7 @@ jobs:
25
25
 
26
26
  steps:
27
27
  - name: Checkout repository
28
- uses: actions/checkout@v5
28
+ uses: actions/checkout@v6
29
29
 
30
30
  - name: Initialize CodeQL
31
31
  uses: github/codeql-action/init@v4
@@ -14,7 +14,7 @@ jobs:
14
14
 
15
15
  steps:
16
16
  - name: Checkout repository
17
- uses: actions/checkout@v5
17
+ uses: actions/checkout@v6
18
18
 
19
19
  - name: Set up Ruby
20
20
  uses: ruby/setup-ruby@v1
@@ -32,7 +32,7 @@ jobs:
32
32
  RAILS_VERSION: ${{ matrix.rails == '' && '6.1.6' || matrix.rails }}
33
33
  COVERAGE: ${{ matrix.coverage || '' }}
34
34
  steps:
35
- - uses: actions/checkout@v5
35
+ - uses: actions/checkout@v6
36
36
  - name: bundle install
37
37
  run: bundle install -j$(nproc) --retry 3
38
38
  - run: bundle exec rspec
data/.gitignore CHANGED
@@ -7,6 +7,7 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
  /Gemfile.lock
10
+ .DS_Store
10
11
 
11
12
  # rspec failure tracking
12
13
  .rspec_status
@@ -246,69 +246,99 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
246
246
 
247
247
  def build_array_items_schema(array, record: nil)
248
248
  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) }
249
251
 
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] = {}
251
255
 
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)
257
- end
258
- end
256
+ all_keys = all_schemas.flat_map { |s| s[:properties]&.keys || [] }.uniq
259
257
 
260
- merged_schema
261
- end
258
+ all_keys.each do |key|
259
+ all_property_schemas = all_schemas.map { |s| s[:properties]&.[](key) }
260
+
261
+ nullable_only_schemas = all_property_schemas.select { |p| p && p.keys == [:nullable] }
262
+ property_variations = all_property_schemas.select { |p| p && p.keys != [:nullable] }
262
263
 
263
- def merge_object_schemas(schema1, schema2)
264
- return schema1 unless schema2.is_a?(Hash) && schema1.is_a?(Hash)
265
- return schema1 unless schema1[:type] == 'object' && schema2[:type] == 'object'
264
+ has_nullable = all_property_schemas.any?(&:nil?) || nullable_only_schemas.any?
266
265
 
267
- merged = schema1.dup
266
+ next if property_variations.empty? && !has_nullable
268
267
 
269
- if schema1[:properties] && schema2[:properties]
270
- merged[:properties] = schema1[:properties].dup
268
+ if property_variations.empty? && has_nullable
269
+ merged_schema[:properties][key] = { nullable: true }
270
+ elsif property_variations.size == 1
271
+ merged_schema[:properties][key] = property_variations.first.dup
272
+ else
273
+ unique_types = property_variations.map { |p| p[:type] }.compact.uniq
271
274
 
272
- schema2[:properties].each do |key, prop2|
273
- if merged[:properties][key]
274
- prop1 = merged[:properties][key]
275
- merged[:properties][key] = merge_property_schemas(prop1, prop2)
275
+ case unique_types.first
276
+ when 'array'
277
+ merged_schema[:properties][key] = { type: 'array' }
278
+ items_variations = property_variations.map { |p| p[:items] }.compact
279
+ merged_schema[:properties][key][:items] = build_merged_schema_from_variations(items_variations)
280
+ when 'object'
281
+ merged_schema[:properties][key] = build_merged_schema_from_variations(property_variations)
276
282
  else
277
- merged[:properties][key] = make_property_nullable(prop2)
283
+ merged_schema[:properties][key] = property_variations.first.dup
278
284
  end
279
285
  end
280
286
 
281
- schema1[:properties].each do |key, prop1|
282
- merged[:properties][key] = make_property_nullable(prop1) unless schema2[:properties][key]
283
- end
284
-
285
- required1 = Set.new(schema1[:required] || [])
286
- required2 = Set.new(schema2[:required] || [])
287
- merged[:required] = (required1 & required2).to_a
287
+ merged_schema[:properties][key][:nullable] = true if has_nullable && merged_schema[:properties][key].is_a?(Hash)
288
288
  end
289
289
 
290
- merged
290
+ all_required_sets = all_schemas.map { |s| s[:required] || [] }
291
+ merged_schema[:required] = all_required_sets.reduce(:&) || []
292
+
293
+ merged_schema
291
294
  end
292
295
 
293
- def merge_property_schemas(prop1, prop2)
294
- return prop1 unless prop2.is_a?(Hash) && prop1.is_a?(Hash)
296
+ def build_merged_schema_from_variations(variations)
297
+ return {} if variations.empty?
298
+ return variations.first if variations.size == 1
295
299
 
296
- merged = prop1.dup
300
+ types = variations.map { |v| v[:type] }.compact.uniq
297
301
 
298
- # If either property is nullable, the merged property should be nullable
299
- merged[:nullable] = true if prop2[:nullable] && !prop1[:nullable]
302
+ if types.size == 1 && types.first == 'object'
303
+ merged = { type: 'object', properties: {} }
304
+ all_keys = variations.flat_map { |v| v[:properties]&.keys || [] }.uniq
300
305
 
301
- # If both are objects, recursively merge their properties
302
- merged = merge_object_schemas(prop1, prop2) if prop1[:type] == 'object' && prop2[:type] == 'object'
306
+ all_keys.each do |key|
307
+ all_prop_variations = variations.map { |v| v[:properties]&.[](key) }
303
308
 
304
- merged
305
- end
309
+ nullable_only = all_prop_variations.select { |p| p && p.keys == [:nullable] }
310
+ prop_variations = all_prop_variations.select { |p| p && p.keys != [:nullable] }.compact
306
311
 
307
- def make_property_nullable(property)
308
- return property unless property.is_a?(Hash)
312
+ has_nullable = all_prop_variations.any?(&:nil?) || nullable_only.any?
309
313
 
310
- nullable_prop = property.dup
311
- nullable_prop[:nullable] = true unless nullable_prop[:nullable]
312
- nullable_prop
314
+ if prop_variations.size == 1
315
+ merged[:properties][key] = prop_variations.first.dup
316
+ merged[:properties][key][:nullable] = true if has_nullable
317
+ elsif prop_variations.size > 1
318
+ prop_types = prop_variations.map { |p| p[:type] }.compact.uniq
319
+
320
+ if prop_types.size == 1
321
+ # Only recursively merge if it's an object type
322
+ merged[:properties][key] = if prop_types.first == 'object'
323
+ build_merged_schema_from_variations(prop_variations)
324
+ else
325
+ prop_variations.first.dup
326
+ end
327
+ else
328
+ unique_props = prop_variations.map { |p| p.reject { |k, _| k == :nullable } }.uniq
329
+ merged[:properties][key] = { oneOf: unique_props }
330
+ end
331
+
332
+ merged[:properties][key][:nullable] = true if has_nullable || prop_variations.size < variations.size
333
+ end
334
+ end
335
+
336
+ all_required = variations.map { |v| v[:required] || [] }
337
+ merged[:required] = all_required.reduce(:&) || []
338
+
339
+ merged
340
+ else
341
+ variations.first
342
+ end
313
343
  end
314
344
  end
@@ -77,6 +77,8 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
77
77
 
78
78
  return if option&.key?(:$ref)
79
79
 
80
+ return if spec[:oneOf]
81
+
80
82
  if score.to_f > SIMILARITY_THRESHOLD
81
83
  merge_schema!(option, spec)
82
84
  else
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module OpenAPI
5
- VERSION = '0.21.0'
5
+ VERSION = '0.21.2'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-openapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.0
4
+ version: 0.21.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takashi Kokubun
@@ -108,7 +108,7 @@ licenses:
108
108
  metadata:
109
109
  homepage_uri: https://github.com/exoego/rspec-openapi
110
110
  source_code_uri: https://github.com/exoego/rspec-openapi
111
- changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.21.0
111
+ changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.21.2
112
112
  rubygems_mfa_required: 'true'
113
113
  rdoc_options: []
114
114
  require_paths: