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 +4 -4
- data/.github/workflows/codeql-analysis.yml +3 -3
- data/.github/workflows/rubocop.yml +1 -1
- data/.gitignore +1 -0
- data/README.md +6 -0
- data/lib/rspec/openapi/schema_builder.rb +133 -1
- data/lib/rspec/openapi/schema_merger.rb +2 -0
- data/lib/rspec/openapi/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fcdab2071b77e7d4df6910839d60df6d3493fcf3bc10c47829fc0609451c5094
|
|
4
|
+
data.tar.gz: 0efb97d546a285eaa1108dbec9b9dd6598a119b93f46dde04e070cdaf8f029d3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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@
|
|
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@
|
|
36
|
+
uses: github/codeql-action/autobuild@v4
|
|
37
37
|
|
|
38
38
|
- name: Perform CodeQL Analysis
|
|
39
|
-
uses: github/codeql-action/analyze@
|
|
39
|
+
uses: github/codeql-action/analyze@v4
|
data/.gitignore
CHANGED
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
|
-
|
|
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
|
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.
|
|
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.
|
|
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:
|