rspec-openapi 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf9d4a386bc3e6a08230a32ae5593c8787f255cc901df5493daa8fb196796cc0
4
- data.tar.gz: ba75408ae751a4c010a6e42872376c6bc539e0a4820fe1c2387649d5f9826f7a
3
+ metadata.gz: 61aad8140991defb871eca3436054e43a6e933d6062ac813c987faf93aa767b5
4
+ data.tar.gz: 8c3d02cf5be2e31131c1d855528f597b0bd604d306dba79e91c782448ae111a3
5
5
  SHA512:
6
- metadata.gz: 22fc87ba0e0b3140f62d9b2396b2d24dbd9fc34fdbdf2664826db02fb3549c4ac3ddc4097ce85a348ff01b4b79279cfe5f450bc65bebb99c95982bf8c06beff0
7
- data.tar.gz: fdd836b66e540d2e2488810ac1570ea0565c963d5817da740da3691d337e5b090e21b287fed92a3c12c9c434916e2481982eee117ee0add52e2bd4ef66a80961
6
+ metadata.gz: 2b879c9733c1704b94c2f5649de5a3200df3bbdac2b5cb76d668e94da32a45262bf40301d74359f30a86af7622851131243c8e95e3ffcfbc716bbd1d6765b47f
7
+ data.tar.gz: 16f048fae7721a7986de3dc0a293a29f1986085c55cb6fc793d596826e3fedff0540de4ab94dca2a3363a3c233c58f7d2f63fdcf8ec66793eed4ae72fe237a48
@@ -23,28 +23,28 @@ class << RSpec::OpenAPI::ComponentsUpdater = Object.new
23
23
  # 0 1 2 ^...............................^
24
24
  # ["components", "schema", "Table", "properties", "owner", "properties", "company", "$ref"]
25
25
  # 0 1 2 ^...........................................^
26
- needle = paths.reject { |path| path.is_a?(Integer) || path == 'oneOf' }
26
+ needle = paths.reject { |path| path.is_a?(Integer) || path == :oneOf }
27
27
  needle = needle.slice(2, needle.size - 3)
28
28
  nested_schema = fresh_schemas.dig(*needle)
29
29
 
30
30
  # Skip if the property using $ref is not found in the parent schema. The property may be removed.
31
31
  next if nested_schema.nil?
32
32
 
33
- schema_name = base.dig(*paths)&.gsub('#/components/schemas/', '')
33
+ schema_name = base.dig(*paths)&.gsub('#/components/schemas/', '')&.to_sym
34
34
  fresh_schemas[schema_name] ||= {}
35
35
  RSpec::OpenAPI::SchemaMerger.merge!(fresh_schemas[schema_name], nested_schema)
36
36
  end
37
37
 
38
- RSpec::OpenAPI::SchemaMerger.merge!(base, { 'components' => { 'schemas' => fresh_schemas } })
39
- RSpec::OpenAPI::SchemaCleaner.cleanup_components_schemas!(base, { 'components' => { 'schemas' => fresh_schemas } })
38
+ RSpec::OpenAPI::SchemaMerger.merge!(base, { components: { schemas: fresh_schemas } })
39
+ RSpec::OpenAPI::SchemaCleaner.cleanup_components_schemas!(base, { components: { schemas: fresh_schemas } })
40
40
  end
41
41
 
42
42
  private
43
43
 
44
44
  def build_fresh_schemas(references, base, fresh)
45
45
  references.inject({}) do |acc, paths|
46
- ref_link = dig_schema(base, paths)['$ref']
47
- schema_name = ref_link.gsub('#/components/schemas/', '')
46
+ ref_link = dig_schema(base, paths)[:$ref]
47
+ schema_name = ref_link.to_s.gsub('#/components/schemas/', '')
48
48
  schema_body = dig_schema(fresh, paths.reject { |path| path.is_a?(Integer) })
49
49
 
50
50
  RSpec::OpenAPI::SchemaMerger.merge!(acc, { schema_name => schema_body })
@@ -52,9 +52,11 @@ class << RSpec::OpenAPI::ComponentsUpdater = Object.new
52
52
  end
53
53
 
54
54
  def dig_schema(obj, paths)
55
- item_schema = obj.dig(*paths, 'schema', 'items')
56
- object_schema = obj.dig(*paths, 'schema')
57
- one_of_schema = obj.dig(*paths.take(paths.size - 1), 'schema', 'oneOf', paths.last)
55
+ # Response code can be an integer
56
+ paths = paths.map { |path| path.is_a?(Integer) ? path : path.to_sym }
57
+ item_schema = obj.dig(*paths, :schema, :items)
58
+ object_schema = obj.dig(*paths, :schema)
59
+ one_of_schema = obj.dig(*paths.take(paths.size - 1), :schema, :oneOf, paths.last)
58
60
 
59
61
  item_schema || object_schema || one_of_schema
60
62
  end
@@ -85,12 +87,12 @@ class << RSpec::OpenAPI::ComponentsUpdater = Object.new
85
87
  end
86
88
 
87
89
  def find_one_of_refs(base, paths)
88
- dig_schema(base, paths)&.dig('oneOf')&.map&.with_index do |schema, index|
89
- paths + [index] if schema&.dig('$ref')&.start_with?('#/components/schemas/')
90
+ dig_schema(base, paths)&.dig(:oneOf)&.map&.with_index do |schema, index|
91
+ paths + [index] if schema&.dig(:$ref)&.start_with?('#/components/schemas/')
90
92
  end&.compact
91
93
  end
92
94
 
93
95
  def find_object_refs(base, paths)
94
- [paths] if dig_schema(base, paths)&.dig('$ref')&.start_with?('#/components/schemas/')
96
+ [paths] if dig_schema(base, paths)&.dig(:$ref)&.start_with?('#/components/schemas/')
95
97
  end
96
98
  end
@@ -5,7 +5,7 @@ class << RSpec::OpenAPI::HashHelper = Object.new
5
5
  case obj
6
6
  when Hash
7
7
  obj.each.flat_map do |k, v|
8
- k = k.to_s
8
+ k = k.to_sym
9
9
  [[k]] + paths_to_all_fields(v).map { |x| [k, *x] }
10
10
  end
11
11
  when Array
@@ -18,10 +18,10 @@ class << RSpec::OpenAPI::HashHelper = Object.new
18
18
  end
19
19
 
20
20
  def matched_paths(obj, selector)
21
- selector_parts = selector.split('.').map(&:to_s)
21
+ selector_parts = selector.split('.').map(&:to_sym)
22
22
  paths_to_all_fields(obj).select do |key_parts|
23
23
  key_parts.size == selector_parts.size && key_parts.zip(selector_parts).all? do |kp, sp|
24
- kp == sp || (sp == '*' && !kp.nil?)
24
+ kp == sp || (sp == :* && !kp.nil?)
25
25
  end
26
26
  end
27
27
  end
@@ -29,7 +29,9 @@ class << RSpec::OpenAPI::HashHelper = Object.new
29
29
  def matched_paths_deeply_nested(obj, begin_selector, end_selector)
30
30
  path_depth_sizes = paths_to_all_fields(obj).map(&:size).uniq
31
31
  path_depth_sizes.map do |depth|
32
- diff = depth - begin_selector.count('.') - end_selector.count('.')
32
+ begin_selector_count = begin_selector.is_a?(Symbol) ? 0 : begin_selector.count('.')
33
+ end_selector_count = end_selector.is_a?(Symbol) ? 0 : end_selector.count('.')
34
+ diff = depth - begin_selector_count - end_selector_count
33
35
  if diff >= 0
34
36
  selector = "#{begin_selector}.#{'*.' * diff}#{end_selector}"
35
37
  matched_paths(obj, selector)
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class << RSpec::OpenAPI::KeyTransformer = Object.new
4
+ def symbolize(value)
5
+ case value
6
+ when Hash
7
+ value.to_h { |k, v| [k.to_sym, symbolize(v)] }
8
+ when Array
9
+ value.map { |v| symbolize(v) }
10
+ else
11
+ value
12
+ end
13
+ end
14
+
15
+ def stringify(value)
16
+ case value
17
+ when Hash
18
+ value.to_h { |k, v| [k.to_s, stringify(v)] }
19
+ when Array
20
+ value.map { |v| stringify(v) }
21
+ else
22
+ value
23
+ end
24
+ end
25
+ end
@@ -53,7 +53,7 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
53
53
 
54
54
  def extract_headers(request, response)
55
55
  request_headers = RSpec::OpenAPI.request_headers.each_with_object([]) do |header, headers_arr|
56
- header_key = header.gsub('-', '_').upcase
56
+ header_key = header.gsub('-', '_').upcase.to_sym
57
57
  header_value = request.get_header(['HTTP', header_key].join('_')) || request.get_header(header_key)
58
58
  headers_arr << [header, header_value] if header_value
59
59
  end
@@ -25,16 +25,17 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
25
25
  end
26
26
  end
27
27
 
28
+ http_method = record.http_method.downcase
28
29
  {
29
30
  paths: {
30
31
  normalize_path(record.path) => {
31
- record.http_method.downcase => {
32
+ http_method => {
32
33
  summary: record.summary,
33
34
  tags: record.tags,
34
35
  operationId: record.operation_id,
35
36
  security: record.security,
36
37
  parameters: build_parameters(record),
37
- requestBody: build_request_body(record),
38
+ requestBody: http_method == 'get' ? nil : build_request_body(record),
38
39
  responses: {
39
40
  record.status.to_s => response,
40
41
  },
@@ -47,7 +48,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
47
48
  private
48
49
 
49
50
  def enrich_with_required_keys(obj)
50
- obj[:required] = obj[:properties]&.keys
51
+ obj[:required] = obj[:properties]&.keys || []
51
52
  obj
52
53
  end
53
54
 
@@ -123,7 +124,6 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
123
124
 
124
125
  def build_request_body(record)
125
126
  return nil if record.request_content_type.nil?
126
- return nil if record.request_params.empty?
127
127
  return nil if record.status >= 400
128
128
 
129
129
  {
@@ -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', %w[name in])
30
+ cleanup_array!(base, spec, 'paths.*.*.parameters', %i[name in])
31
31
 
32
32
  # cleanup requestBody
33
33
  cleanup_hash!(base, spec, 'paths.*.*.requestBody.content.application/json.schema.properties.*')
@@ -40,7 +40,7 @@ class << RSpec::OpenAPI::SchemaCleaner = Object.new
40
40
  end
41
41
 
42
42
  def cleanup_conflicting_security_parameters!(base)
43
- security_schemes = base.dig('components', 'securitySchemes') || {}
43
+ security_schemes = base.dig(:components, :securitySchemes) || {}
44
44
 
45
45
  return if security_schemes.empty?
46
46
 
@@ -65,22 +65,22 @@ class << RSpec::OpenAPI::SchemaCleaner = Object.new
65
65
  paths_to_objects.each do |path|
66
66
  parent = base.dig(*path.take(path.length - 1))
67
67
  # "required" array must not be present if empty
68
- parent.delete('required') if parent['required'] && parent['required'].empty?
68
+ parent.delete(:required) if parent[:required] && parent[:required].empty?
69
69
  end
70
70
  end
71
71
 
72
72
  private
73
73
 
74
74
  def remove_parameters_conflicting_with_security_sceheme!(path_definition, security_scheme, security_scheme_name)
75
- return unless path_definition['security']
76
- return unless path_definition['parameters']
77
- return unless path_definition.dig('security', 0).keys.include?(security_scheme_name)
75
+ return unless path_definition[:security]
76
+ return unless path_definition[:parameters]
77
+ return unless path_definition.dig(:security, 0).keys.include?(security_scheme_name)
78
78
 
79
- path_definition['parameters'].reject! do |parameter|
80
- parameter['in'] == security_scheme['in'] && # same location (ie. header)
81
- parameter['name'] == security_scheme['name'] # same name (ie. AUTHORIZATION)
79
+ path_definition[:parameters].reject! do |parameter|
80
+ parameter[:in] == security_scheme[:in] && # same location (ie. header)
81
+ parameter[:name] == security_scheme[:name] # same name (ie. AUTHORIZATION)
82
82
  end
83
- path_definition.delete('parameters') if path_definition['parameters'].empty?
83
+ path_definition.delete(:parameters) if path_definition[:parameters].empty?
84
84
  end
85
85
 
86
86
  def cleanup_array!(base, spec, selector, fields_for_identity = [])
@@ -15,7 +15,7 @@ class RSpec::OpenAPI::SchemaFile
15
15
  spec = read
16
16
  block.call(spec)
17
17
  ensure
18
- write(spec)
18
+ write(RSpec::OpenAPI::KeyTransformer.stringify(spec))
19
19
  end
20
20
 
21
21
  private
@@ -24,7 +24,7 @@ class RSpec::OpenAPI::SchemaFile
24
24
  def read
25
25
  return {} unless File.exist?(@path)
26
26
 
27
- YAML.safe_load(File.read(@path)) # this can also parse JSON
27
+ RSpec::OpenAPI::KeyTransformer.symbolize(YAML.safe_load(File.read(@path))) # this can also parse JSON
28
28
  end
29
29
 
30
30
  # @param [Hash] spec
@@ -4,32 +4,20 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
4
4
  # @param [Hash] base
5
5
  # @param [Hash] spec
6
6
  def merge!(base, spec)
7
- spec = normalize_keys(spec)
7
+ spec = RSpec::OpenAPI::KeyTransformer.symbolize(spec)
8
+ base.replace(RSpec::OpenAPI::KeyTransformer.symbolize(base))
8
9
  merge_schema!(base, spec)
9
10
  end
10
11
 
11
12
  private
12
13
 
13
- def normalize_keys(spec)
14
- case spec
15
- when Hash
16
- spec.to_h do |key, value|
17
- [key.to_s, normalize_keys(value)]
18
- end
19
- when Array
20
- spec.map { |s| normalize_keys(s) }
21
- else
22
- spec
23
- end
24
- end
25
-
26
14
  # Not doing `base.replace(deep_merge(base, spec))` to preserve key orders.
27
15
  # Also this needs to be aware of OpenAPI details because a Hash-like structure
28
16
  # may be an array whose Hash elements have a key name.
29
17
  #
30
18
  # TODO: Should we probably force-merge `summary` regardless of manual modifications?
31
19
  def merge_schema!(base, spec)
32
- if (options = base['oneOf'])
20
+ if (options = base[:oneOf])
33
21
  merge_closest_match!(options, spec)
34
22
 
35
23
  return base
@@ -37,13 +25,13 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
37
25
 
38
26
  spec.each do |key, value|
39
27
  if base[key].is_a?(Hash) && value.is_a?(Hash)
40
- merge_schema!(base[key], value) unless base[key].key?('$ref')
28
+ merge_schema!(base[key], value) unless base[key].key?(:$ref)
41
29
  elsif base[key].is_a?(Array) && value.is_a?(Array)
42
30
  # parameters need to be merged as if `name` and `in` were the Hash keys.
43
31
  merge_arrays(base, key, value)
44
32
  else
45
33
  # do not ADD `properties` or `required` fields if `additionalProperties` field is present
46
- base[key] = value unless base.key?('additionalProperties') && %w[properties required].include?(key)
34
+ base[key] = value unless base.key?(:additionalProperties) && %i[properties required].include?(key)
47
35
  end
48
36
  end
49
37
  base
@@ -51,9 +39,9 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
51
39
 
52
40
  def merge_arrays(base, key, value)
53
41
  base[key] = case key
54
- when 'parameters'
42
+ when :parameters
55
43
  merge_parameters(base, key, value)
56
- when 'required'
44
+ when :required
57
45
  # Preserve properties that appears in all test cases
58
46
  value & base[key]
59
47
  else
@@ -65,13 +53,13 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
65
53
  def merge_parameters(base, key, value)
66
54
  all_parameters = value | base[key]
67
55
 
68
- unique_base_parameters = base[key].index_by { |parameter| [parameter['name'], parameter['in']] }
56
+ unique_base_parameters = base[key].index_by { |parameter| [parameter[:name], parameter[:in]] }
69
57
  all_parameters = all_parameters.map do |parameter|
70
- base_parameter = unique_base_parameters[[parameter['name'], parameter['in']]] || {}
58
+ base_parameter = unique_base_parameters[[parameter[:name], parameter[:in]]] || {}
71
59
  base_parameter ? base_parameter.merge(parameter) : parameter
72
60
  end
73
61
 
74
- all_parameters.uniq! { |param| param.slice('name', 'in') }
62
+ all_parameters.uniq! { |param| param.slice(:name, :in) }
75
63
  base[key] = all_parameters
76
64
  end
77
65
 
@@ -80,7 +68,7 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
80
68
  def merge_closest_match!(options, spec)
81
69
  score, option = options.map { |option| [similarity(option, spec), option] }.max_by(&:first)
82
70
 
83
- return if option&.key?('$ref')
71
+ return if option&.key?(:$ref)
84
72
 
85
73
  if score.to_f > SIMILARITY_THRESHOLD
86
74
  merge_schema!(option, spec)
@@ -97,7 +85,7 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
97
85
  when [Array, Array]
98
86
  (first & second).size / [first.size, second.size].max.to_f
99
87
  when [Hash, Hash]
100
- return 1 if first.merge(second).key?('$ref')
88
+ return 1 if first.merge(second).key?(:$ref)
101
89
 
102
90
  intersection = first.keys & second.keys
103
91
  total_size = [first.size, second.size].max.to_f
@@ -29,7 +29,7 @@ class << RSpec::OpenAPI::SchemaSorter = Object.new
29
29
  end
30
30
 
31
31
  def deep_sort_hash!(hash)
32
- sorted = hash.entries.sort_by { |k, _| k }.to_h
32
+ sorted = hash.entries.sort_by { |k, _| k.to_s }.to_h.transform_keys(&:to_sym)
33
33
  hash.replace(sorted)
34
34
  end
35
35
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module OpenAPI
5
- VERSION = '0.15.0'
5
+ VERSION = '0.16.0'
6
6
  end
7
7
  end
data/lib/rspec/openapi.rb CHANGED
@@ -10,6 +10,7 @@ require 'rspec/openapi/schema_file'
10
10
  require 'rspec/openapi/schema_merger'
11
11
  require 'rspec/openapi/schema_cleaner'
12
12
  require 'rspec/openapi/schema_sorter'
13
+ require 'rspec/openapi/key_transformer'
13
14
 
14
15
  require 'rspec/openapi/minitest_hooks' if Object.const_defined?('Minitest')
15
16
  require 'rspec/openapi/rspec_hooks' if ENV['OPENAPI'] && Object.const_defined?('RSpec')
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.15.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takashi Kokubun
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2024-03-25 00:00:00.000000000 Z
12
+ date: 2024-03-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -67,6 +67,7 @@ files:
67
67
  - lib/rspec/openapi/components_updater.rb
68
68
  - lib/rspec/openapi/default_schema.rb
69
69
  - lib/rspec/openapi/hash_helper.rb
70
+ - lib/rspec/openapi/key_transformer.rb
70
71
  - lib/rspec/openapi/minitest_hooks.rb
71
72
  - lib/rspec/openapi/record.rb
72
73
  - lib/rspec/openapi/record_builder.rb
@@ -88,7 +89,7 @@ licenses:
88
89
  metadata:
89
90
  homepage_uri: https://github.com/exoego/rspec-openapi
90
91
  source_code_uri: https://github.com/exoego/rspec-openapi
91
- changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.15.0
92
+ changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.16.0
92
93
  rubygems_mfa_required: 'true'
93
94
  post_install_message:
94
95
  rdoc_options: []