rspec-openapi 0.20.0 → 0.21.1

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: a94cc7be13ab8a500bf3eb4bb5831f1c06c7722e96f9f7157e424fa402f2f137
4
- data.tar.gz: db6591ab069d1f7bd1d7f07b6f2c4d9be9ef86f90df497c28a2b8ed01ee1d5e4
3
+ metadata.gz: fcdab2071b77e7d4df6910839d60df6d3493fcf3bc10c47829fc0609451c5094
4
+ data.tar.gz: 0efb97d546a285eaa1108dbec9b9dd6598a119b93f46dde04e070cdaf8f029d3
5
5
  SHA512:
6
- metadata.gz: 785d100550f4ecde118a8986ee7c98349e310de9c870a04067a9f84892d37b63cadf9a88d0ff2158012c153c561c3a0576a3c0c79860a645bb53bb295c64bbc5
7
- data.tar.gz: 660bd242fa8c5686bc6fbead56f595be959f1f1cae465723ed89e93c0b0096ee6c86094c8561193cabf119cedb74762e84e6b9c3651d34fca7691b63317fc611
6
+ metadata.gz: cb16c77c27f8a766cb5e09bce79d2ef7af160277b274dbd75785f8ca9466e28e6800f498f167644fb16e66b4f6a1dc7becd77b0632d4c1a717b49739e4ed014c
7
+ data.tar.gz: efa94d5ea193fd56f3ac3b4f220453c3ceb184596688422fff7fe9e14223a44c5427ad17206a7cd4014e16f8e1f4bafc3f3ebdce3a336251a036f5daee83ee2e
@@ -28,12 +28,12 @@ jobs:
28
28
  uses: actions/checkout@v5
29
29
 
30
30
  - name: Initialize CodeQL
31
- uses: github/codeql-action/init@v3
31
+ uses: github/codeql-action/init@v4
32
32
  with:
33
33
  languages: ${{ matrix.language }}
34
34
 
35
35
  - name: Autobuild
36
- uses: github/codeql-action/autobuild@v3
36
+ uses: github/codeql-action/autobuild@v4
37
37
 
38
38
  - name: Perform CodeQL Analysis
39
- uses: github/codeql-action/analyze@v3
39
+ uses: github/codeql-action/analyze@v4
@@ -30,6 +30,6 @@ jobs:
30
30
  "
31
31
 
32
32
  - name: Upload Sarif output
33
- uses: github/codeql-action/upload-sarif@v3
33
+ uses: github/codeql-action/upload-sarif@v4
34
34
  with:
35
35
  sarif_file: rubocop.sarif
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
data/README.md CHANGED
@@ -408,6 +408,12 @@ Existing RSpec plugins which have OpenAPI integration:
408
408
  * Orignally created by [k0kubun](https://github.com/k0kubun) and the ownership was transferred to [exoego](https://github.com/exoego) in 2022-11-29.
409
409
 
410
410
 
411
+ ## Releasing
412
+
413
+ 1. Bump version in `lib/rspec/openapi/version.rb`
414
+ 2. Run `bundle exec rake release`
415
+ 3. Push tag
416
+
411
417
  ## License
412
418
 
413
419
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -151,7 +151,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
151
151
  property[:items] = if value.empty?
152
152
  {} # unknown
153
153
  else
154
- build_property(value.first, record: record)
154
+ build_array_items_schema(value, record: record)
155
155
  end
156
156
  when Hash
157
157
  property[:properties] = {}.tap do |properties|
@@ -243,4 +243,136 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
243
243
  def normalize_content_disposition(content_disposition)
244
244
  content_disposition&.sub(/;.+\z/, '')
245
245
  end
246
+
247
+ def build_array_items_schema(array, record: nil)
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) }
251
+
252
+ all_schemas = array.map { |item| build_property(item, record: record) }
253
+ merged_schema = all_schemas.first.dup
254
+ merged_schema[:properties] = {}
255
+
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
280
+ end
281
+ end
282
+
283
+ all_required_sets = all_schemas.map { |s| s[:required] || [] }
284
+ merged_schema[:required] = all_required_sets.reduce(:&) || []
285
+
286
+ merged_schema
287
+ end
288
+
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
+
327
+ def merge_object_schemas(schema1, schema2)
328
+ return schema1 unless schema2.is_a?(Hash) && schema1.is_a?(Hash)
329
+ return schema1 unless schema1[:type] == 'object' && schema2[:type] == 'object'
330
+
331
+ merged = schema1.dup
332
+
333
+ if schema1[:properties] && schema2[:properties]
334
+ merged[:properties] = schema1[:properties].dup
335
+
336
+ schema2[:properties].each do |key, prop2|
337
+ if merged[:properties][key]
338
+ prop1 = merged[:properties][key]
339
+ merged[:properties][key] = merge_property_schemas(prop1, prop2)
340
+ else
341
+ merged[:properties][key] = make_property_nullable(prop2)
342
+ end
343
+ end
344
+
345
+ schema1[:properties].each do |key, prop1|
346
+ merged[:properties][key] = make_property_nullable(prop1) unless schema2[:properties][key]
347
+ end
348
+
349
+ required1 = Set.new(schema1[:required] || [])
350
+ required2 = Set.new(schema2[:required] || [])
351
+ merged[:required] = (required1 & required2).to_a
352
+ end
353
+
354
+ merged
355
+ end
356
+
357
+ def merge_property_schemas(prop1, prop2)
358
+ return prop1 unless prop2.is_a?(Hash) && prop1.is_a?(Hash)
359
+
360
+ merged = prop1.dup
361
+
362
+ # If either property is nullable, the merged property should be nullable
363
+ merged[:nullable] = true if prop2[:nullable] && !prop1[:nullable]
364
+
365
+ # If both are objects, recursively merge their properties
366
+ merged = merge_object_schemas(prop1, prop2) if prop1[:type] == 'object' && prop2[:type] == 'object'
367
+
368
+ merged
369
+ end
370
+
371
+ def make_property_nullable(property)
372
+ return property unless property.is_a?(Hash)
373
+
374
+ nullable_prop = property.dup
375
+ nullable_prop[:nullable] = true unless nullable_prop[:nullable]
376
+ nullable_prop
377
+ end
246
378
  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.20.0'
5
+ VERSION = '0.21.1'
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.20.0
4
+ version: 0.21.1
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.20.0
111
+ changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.21.1
112
112
  rubygems_mfa_required: 'true'
113
113
  rdoc_options: []
114
114
  require_paths: