rspec-openapi 0.26.0 → 0.27.0
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/create_release.yml +1 -1
- data/.rubocop.yml +4 -0
- data/.rubocop_todo.yml +13 -13
- data/lib/rspec/openapi/extractors/hanami.rb +10 -37
- data/lib/rspec/openapi/extractors/rack.rb +7 -32
- data/lib/rspec/openapi/extractors/rails.rb +9 -35
- data/lib/rspec/openapi/extractors/shared_extractor.rb +33 -26
- data/lib/rspec/openapi/record_builder.rb +8 -29
- data/lib/rspec/openapi/schema_builder/build_context.rb +20 -0
- data/lib/rspec/openapi/schema_builder.rb +271 -348
- data/lib/rspec/openapi/schema_cleaner.rb +1 -1
- data/lib/rspec/openapi/schema_file.rb +4 -6
- data/lib/rspec/openapi/schema_merger.rb +79 -49
- data/lib/rspec/openapi/version.rb +1 -1
- data/lib/rspec/openapi.rb +2 -2
- metadata +4 -3
|
@@ -27,7 +27,7 @@ class << RSpec::OpenAPI::SchemaCleaner = Object.new
|
|
|
27
27
|
cleanup_hash!(base, spec, 'paths.*.*')
|
|
28
28
|
|
|
29
29
|
# cleanup parameters
|
|
30
|
-
cleanup_array!(base, spec, 'paths.*.*.parameters',
|
|
30
|
+
cleanup_array!(base, spec, 'paths.*.*.parameters', [:name, :in])
|
|
31
31
|
|
|
32
32
|
# cleanup requestBody
|
|
33
33
|
cleanup_hash!(base, spec, 'paths.*.*.requestBody.content.application/json.schema.properties.*')
|
|
@@ -27,12 +27,10 @@ class RSpec::OpenAPI::SchemaFile
|
|
|
27
27
|
def read
|
|
28
28
|
return {} unless File.exist?(@path)
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
),
|
|
35
|
-
) # this can also parse JSON
|
|
30
|
+
content = YAML.safe_load(File.read(@path), permitted_classes: [Date, Time]) # this can also parse JSON
|
|
31
|
+
return {} if content.nil?
|
|
32
|
+
|
|
33
|
+
RSpec::OpenAPI::KeyTransformer.symbolize(content)
|
|
36
34
|
end
|
|
37
35
|
|
|
38
36
|
# @param [Hash] spec
|
|
@@ -25,37 +25,51 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
|
|
|
25
25
|
return base
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
prune_stale_object_fields!(base, spec)
|
|
29
|
+
|
|
30
|
+
spec.each { |key, value| merge_entry!(base, key, value) }
|
|
31
|
+
base
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# When the new spec converts an object to a dictionary (introduces
|
|
35
|
+
# `additionalProperties` on a node that previously had `properties` /
|
|
36
|
+
# `required`), drop the stale fields so the merged result reflects the
|
|
37
|
+
# new intent. We only prune when base does not already declare
|
|
38
|
+
# `additionalProperties`, to preserve manual edits that intentionally
|
|
39
|
+
# combine fixed and dynamic keys.
|
|
40
|
+
def prune_stale_object_fields!(base, spec)
|
|
41
|
+
return unless spec.is_a?(Hash) && spec.key?(:additionalProperties) && !base.key?(:additionalProperties)
|
|
42
|
+
|
|
43
|
+
base.delete(:properties)
|
|
44
|
+
base.delete(:required)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def merge_entry!(base, key, value)
|
|
48
|
+
if base[key].is_a?(Hash) && value.is_a?(Hash)
|
|
49
|
+
merge_hash_entry!(base, key, value)
|
|
50
|
+
elsif base[key].is_a?(Array) && value.is_a?(Array)
|
|
51
|
+
# parameters need to be merged as if `name` and `in` were the Hash keys.
|
|
52
|
+
merge_arrays(base, key, value)
|
|
53
|
+
elsif !skip_due_to_additional_properties?(base, key)
|
|
54
|
+
base[key] = value
|
|
37
55
|
end
|
|
56
|
+
end
|
|
38
57
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
merge_schema!(base[key], value) unless base[key].key?(:$ref)
|
|
49
|
-
end
|
|
50
|
-
elsif base[key].is_a?(Array) && value.is_a?(Array)
|
|
51
|
-
# parameters need to be merged as if `name` and `in` were the Hash keys.
|
|
52
|
-
merge_arrays(base, key, value)
|
|
53
|
-
else
|
|
54
|
-
# do not ADD `properties` or `required` fields if `additionalProperties` field is present
|
|
55
|
-
base[key] = value unless base.key?(:additionalProperties) && %i[properties required].include?(key)
|
|
56
|
-
end
|
|
58
|
+
def merge_hash_entry!(base, key, value)
|
|
59
|
+
# Handle example/examples conflict - convert to examples when mixed
|
|
60
|
+
normalize_example_fields!(base[key], value)
|
|
61
|
+
|
|
62
|
+
# If the new value has oneOf, replace the entire value instead of merging
|
|
63
|
+
if value.key?(:oneOf)
|
|
64
|
+
base[key] = value
|
|
65
|
+
elsif !base[key].key?(:$ref)
|
|
66
|
+
merge_schema!(base[key], value)
|
|
57
67
|
end
|
|
58
|
-
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# do not ADD `properties` or `required` fields if `additionalProperties` field is present
|
|
71
|
+
def skip_due_to_additional_properties?(base, key)
|
|
72
|
+
base.key?(:additionalProperties) && [:properties, :required].include?(key)
|
|
59
73
|
end
|
|
60
74
|
|
|
61
75
|
def merge_arrays(base, key, value)
|
|
@@ -72,21 +86,33 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
|
|
|
72
86
|
end
|
|
73
87
|
|
|
74
88
|
def merge_parameters(base, key, value)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
base_params = index_parameters_by_identity(base[key])
|
|
90
|
+
new_params = index_parameters_by_identity(value)
|
|
91
|
+
|
|
92
|
+
base[key] = (base_params.keys | new_params.keys).map do |param_key|
|
|
93
|
+
base_param = base_params[param_key]
|
|
94
|
+
new_param = new_params[param_key]
|
|
95
|
+
|
|
96
|
+
if base_param && new_param
|
|
97
|
+
merge_parameter_with_schema(base_param, new_param)
|
|
98
|
+
elsif new_param
|
|
99
|
+
# Parameter only in the new spec. Treat as optional unless its `required: true`
|
|
100
|
+
# came from explicit `required_request_params` metadata — distinguishable only
|
|
101
|
+
# for `query`, where the schema_builder default is `required: false`. `header`
|
|
102
|
+
# defaults to `required: true`, so the value alone can't signal user intent.
|
|
103
|
+
new_param[:in] == 'query' && new_param[:required] ? new_param : mark_optional_unless_path(new_param)
|
|
83
104
|
else
|
|
84
|
-
|
|
105
|
+
mark_optional_unless_path(base_param)
|
|
85
106
|
end
|
|
86
107
|
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# OpenAPI requires `in: path` parameters to be `required: true`, so this leaves
|
|
111
|
+
# them untouched.
|
|
112
|
+
def mark_optional_unless_path(parameter)
|
|
113
|
+
return parameter if parameter[:in] == 'path'
|
|
87
114
|
|
|
88
|
-
|
|
89
|
-
base[key] = all_parameters
|
|
115
|
+
parameter.merge(required: false)
|
|
90
116
|
end
|
|
91
117
|
|
|
92
118
|
def merge_parameter_with_schema(base_param, new_param)
|
|
@@ -94,12 +120,18 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
|
|
|
94
120
|
new_schema = new_param[:schema]
|
|
95
121
|
|
|
96
122
|
# If schemas have different types, create a oneOf
|
|
97
|
-
if base_schema && new_schema && schemas_have_different_types?(base_schema, new_schema)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
123
|
+
merged = if base_schema && new_schema && schemas_have_different_types?(base_schema, new_schema)
|
|
124
|
+
merged_schema = merge_schemas_into_one_of(base_schema, new_schema)
|
|
125
|
+
base_param.merge(new_param).merge(schema: merged_schema)
|
|
126
|
+
else
|
|
127
|
+
base_param.merge(new_param)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Once a parameter has been seen missing in any earlier test case, keep it optional
|
|
131
|
+
# even if later test cases mark it required again.
|
|
132
|
+
merged = mark_optional_unless_path(merged) if base_param[:required] == false || new_param[:required] == false
|
|
133
|
+
|
|
134
|
+
merged
|
|
103
135
|
end
|
|
104
136
|
|
|
105
137
|
def schemas_have_different_types?(schema1, schema2)
|
|
@@ -133,10 +165,8 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
|
|
|
133
165
|
end
|
|
134
166
|
end
|
|
135
167
|
|
|
136
|
-
def
|
|
137
|
-
|
|
138
|
-
[[parameter[:name], parameter[:in]], parameter]
|
|
139
|
-
end
|
|
168
|
+
def index_parameters_by_identity(parameters)
|
|
169
|
+
parameters.to_h { |p| [[p[:name], p[:in]], p] }
|
|
140
170
|
end
|
|
141
171
|
|
|
142
172
|
# Normalize example/examples fields when there's a conflict
|
data/lib/rspec/openapi.rb
CHANGED
|
@@ -43,10 +43,10 @@ module RSpec::OpenAPI
|
|
|
43
43
|
@request_headers = []
|
|
44
44
|
@servers = []
|
|
45
45
|
@security_schemes = []
|
|
46
|
-
@example_types =
|
|
46
|
+
@example_types = [:request]
|
|
47
47
|
@response_headers = []
|
|
48
48
|
@path_records = Hash.new { |h, k| h[k] = [] }
|
|
49
|
-
@ignored_path_params =
|
|
49
|
+
@ignored_path_params = [:controller, :action, :format]
|
|
50
50
|
@ignored_paths = []
|
|
51
51
|
@post_process_hook = nil
|
|
52
52
|
|
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.27.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Takashi Kokubun
|
|
@@ -97,6 +97,7 @@ files:
|
|
|
97
97
|
- lib/rspec/openapi/result_recorder.rb
|
|
98
98
|
- lib/rspec/openapi/rspec_hooks.rb
|
|
99
99
|
- lib/rspec/openapi/schema_builder.rb
|
|
100
|
+
- lib/rspec/openapi/schema_builder/build_context.rb
|
|
100
101
|
- lib/rspec/openapi/schema_cleaner.rb
|
|
101
102
|
- lib/rspec/openapi/schema_file.rb
|
|
102
103
|
- lib/rspec/openapi/schema_merger.rb
|
|
@@ -114,7 +115,7 @@ licenses:
|
|
|
114
115
|
metadata:
|
|
115
116
|
homepage_uri: https://github.com/exoego/rspec-openapi
|
|
116
117
|
source_code_uri: https://github.com/exoego/rspec-openapi
|
|
117
|
-
changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.
|
|
118
|
+
changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.27.0
|
|
118
119
|
rubygems_mfa_required: 'true'
|
|
119
120
|
rdoc_options: []
|
|
120
121
|
require_paths:
|
|
@@ -130,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
130
131
|
- !ruby/object:Gem::Version
|
|
131
132
|
version: '0'
|
|
132
133
|
requirements: []
|
|
133
|
-
rubygems_version: 4.0.
|
|
134
|
+
rubygems_version: 4.0.10
|
|
134
135
|
specification_version: 4
|
|
135
136
|
summary: Generate OpenAPI schema from RSpec request specs
|
|
136
137
|
test_files: []
|