diff --git a/lib/rspec/openapi/schema_builder.rb b/lib/rspec/openapi/schema_builder.rb index 80273796..1c655d1d 100644 --- a/lib/rspec/openapi/schema_builder.rb +++ b/lib/rspec/openapi/schema_builder.rb @@ -50,7 +50,7 @@ def build_parameters(record) record.path_params.each do |key, value| parameters << { - name: key.to_s, + name: build_parameter_name(key, value), in: 'path', required: true, schema: build_property(try_cast(value)), @@ -60,7 +60,7 @@ def build_parameters(record) record.query_params.each do |key, value| parameters << { - name: key.to_s, + name: build_parameter_name(key, value), in: 'query', schema: build_property(try_cast(value)), example: (try_cast(value) if example_enabled?), @@ -71,6 +71,16 @@ def build_parameters(record) parameters end + def build_parameter_name(key, value) + key = key.to_s + if value.is_a?(Hash) && (value_keys = value.keys).size == 1 + value_key = value_keys.first + build_parameter_name("#{key}[#{value_key}]", value[value_key]) + else + key + end + end + def build_request_body(record) return nil if record.request_content_type.nil? return nil if record.request_params.empty? diff --git a/lib/rspec/openapi/schema_merger.rb b/lib/rspec/openapi/schema_merger.rb index 570d2dfc..1bedcd5b 100644 --- a/lib/rspec/openapi/schema_merger.rb +++ b/lib/rspec/openapi/schema_merger.rb @@ -26,10 +26,18 @@ def normalize_keys(spec) # Should we probably force-merge `summary` regardless of manual modifications? def deep_reverse_merge!(base, spec) spec.each do |key, value| - if base[key].is_a?(Hash) && value.is_a?(Hash) - deep_reverse_merge!(base[key], value) + base_key_value = base[key] + if base_key_value.is_a?(Hash) && value.is_a?(Hash) + deep_reverse_merge!(base_key_value, value) elsif !base.key?(key) base[key] = value + elsif base_key_value.is_a?(Array) && value.is_a?(Array) + if key == "parameters" + # merge arrays + base[key] |= value + end + else + nil # no-op end end base diff --git a/spec/rails/doc/openapi.yaml b/spec/rails/doc/openapi.yaml index 90cc1d2b..6f064264 100644 --- a/spec/rails/doc/openapi.yaml +++ b/spec/rails/doc/openapi.yaml @@ -24,6 +24,24 @@ paths: schema: type: integer example: 10 + - name: filter[name] + in: query + schema: + type: object + properties: + name: + type: string + example: + name: Example Table + - name: filter[price] + in: query + schema: + type: object + properties: + price: + type: string + example: + price: '0' responses: '200': description: returns a list of tables @@ -63,7 +81,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' @@ -135,7 +153,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' @@ -151,6 +169,12 @@ paths: schema: type: integer example: 1 + - name: id + in: path + required: true + schema: + type: integer + example: 2 responses: '200': description: returns a table @@ -188,7 +212,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' @@ -273,7 +297,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' @@ -325,7 +349,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' diff --git a/spec/requests/rails_spec.rb b/spec/requests/rails_spec.rb index 251ff8e4..80620024 100644 --- a/spec/requests/rails_spec.rb +++ b/spec/requests/rails_spec.rb @@ -13,9 +13,21 @@ RSpec.describe 'Tables', type: :request do describe '#index' do - it 'returns a list of tables' do - get '/tables', params: { page: '1', per: '10' }, headers: { authorization: 'k0kubun' } - expect(response.status).to eq(200) + context it 'returns a list of tables' do + it 'with flat query parameters' do + get '/tables', params: { page: '1', per: '10' }, headers: { authorization: 'k0kubun' } + expect(response.status).to eq(200) + end + + it 'with deep query parameters' do + get '/tables', params: { filter: { "name" => "Example Table" } }, headers: { authorization: 'k0kubun' } + expect(response.status).to eq(200) + end + + it 'with different deep query parameters' do + get '/tables', params: { filter: { "price" => 0 } }, headers: { authorization: 'k0kubun' } + expect(response.status).to eq(200) + end end it 'has a request spec which does not make any request' do