diff --git a/rswag-specs/lib/rswag/specs/example_group_helpers.rb b/rswag-specs/lib/rswag/specs/example_group_helpers.rb index b09420d..3024b15 100644 --- a/rswag-specs/lib/rswag/specs/example_group_helpers.rb +++ b/rswag-specs/lib/rswag/specs/example_group_helpers.rb @@ -2,9 +2,9 @@ module Rswag module Specs module ExampleGroupHelpers - def path(path, &block) - api_metadata = { path: path} - describe(path, api_metadata, &block) + def path(template, &block) + api_metadata = { path_item: { template: template } } + describe(template, api_metadata, &block) end [ :get, :post, :patch, :put, :delete, :head ].each do |verb| @@ -37,8 +37,14 @@ module Rswag def parameter(attributes) attributes[:required] = true if attributes[:in].to_sym == :path - metadata[:operation][:parameters] ||= [] - metadata[:operation][:parameters] << attributes + + if metadata.has_key?(:operation) + metadata[:operation][:parameters] ||= [] + metadata[:operation][:parameters] << attributes + else + metadata[:path_item][:parameters] ||= [] + metadata[:path_item][:parameters] << attributes + end end def response(code, description, &block) diff --git a/rswag-specs/lib/rswag/specs/request_factory.rb b/rswag-specs/lib/rswag/specs/request_factory.rb index 39ade56..d1d6045 100644 --- a/rswag-specs/lib/rswag/specs/request_factory.rb +++ b/rswag-specs/lib/rswag/specs/request_factory.rb @@ -11,7 +11,7 @@ module Rswag end def build_fullpath(example) - @api_metadata[:path].dup.tap do |t| + @api_metadata[:path_item][:template].dup.tap do |t| t.prepend(@global_metadata[:basePath] || '') parameters_in(:path).each { |p| t.gsub!("{#{p[:name]}}", example.send(p[:name]).to_s) } t.concat(build_query_string(example)) @@ -44,7 +44,13 @@ module Rswag private def parameters_in(location) - (@api_metadata[:operation][:parameters] || []) + path_item_params = @api_metadata[:path_item][:parameters] || [] + operation_params = @api_metadata[:operation][:parameters] || [] + applicable_params = operation_params + .concat(path_item_params) + .uniq { |p| p[:name] } # operation params should override path_item params + + applicable_params .map { |p| p['$ref'] ? resolve_parameter(p['$ref']) : p } # resolve any references .concat(resolve_api_key_parameters) .select { |p| p[:in] == location } diff --git a/rswag-specs/lib/rswag/specs/swagger_formatter.rb b/rswag-specs/lib/rswag/specs/swagger_formatter.rb index 11359c1..794a9d9 100644 --- a/rswag-specs/lib/rswag/specs/swagger_formatter.rb +++ b/rswag-specs/lib/rswag/specs/swagger_formatter.rb @@ -49,18 +49,18 @@ module Rswag def metadata_to_swagger(metadata) response_code = metadata[:response][:code] response = metadata[:response].reject { |k,v| k == :code } + verb = metadata[:operation][:verb] operation = metadata[:operation] .reject { |k,v| k == :verb } .merge(responses: { response_code => response }) - { - paths: { - metadata[:path] => { - verb => operation - } - } - } + path_template = metadata[:path_item][:template] + path_item = metadata[:path_item] + .reject { |k,v| k == :template } + .merge(verb => operation) + + { paths: { path_template => path_item } } end end end diff --git a/rswag-specs/spec/rswag/specs/example_group_helpers_spec.rb b/rswag-specs/spec/rswag/specs/example_group_helpers_spec.rb index 15ed51c..76c3278 100644 --- a/rswag-specs/spec/rswag/specs/example_group_helpers_spec.rb +++ b/rswag-specs/spec/rswag/specs/example_group_helpers_spec.rb @@ -19,7 +19,7 @@ module Rswag it "delegates to 'describe' with 'path' metadata" do expect(subject).to have_received(:describe).with( - '/blogs', path: '/blogs' + '/blogs', path_item: { template: '/blogs' } ) end end @@ -87,10 +87,21 @@ module Rswag end describe '#parameter(attributes)' do - let(:api_metadata) { { operation: {} } } - context 'always' do + context "when called at the 'path' level" do before { subject.parameter(name: :blog, in: :body, schema: { type: 'object' }) } + let(:api_metadata) { { path_item: {} } } # i.e. operation not defined yet + + it "adds to the 'path_item parameters' metadata" do + expect(api_metadata[:path_item][:parameters]).to match( + [ name: :blog, in: :body, schema: { type: 'object' } ] + ) + end + end + + context "when called at the 'operation' level" do + before { subject.parameter(name: :blog, in: :body, schema: { type: 'object' }) } + let(:api_metadata) { { path_item: {}, operation: {} } } # i.e. operation defined it "adds to the 'operation parameters' metadata" do expect(api_metadata[:operation][:parameters]).to match( @@ -101,6 +112,7 @@ module Rswag context "'path' parameter" do before { subject.parameter(name: :id, in: :path) } + let(:api_metadata) { { operation: {} } } it "automatically sets the 'required' flag" do expect(api_metadata[:operation][:parameters]).to match( diff --git a/rswag-specs/spec/rswag/specs/example_helpers_spec.rb b/rswag-specs/spec/rswag/specs/example_helpers_spec.rb index cb541df..f76d023 100644 --- a/rswag-specs/spec/rswag/specs/example_helpers_spec.rb +++ b/rswag-specs/spec/rswag/specs/example_helpers_spec.rb @@ -16,7 +16,7 @@ module Rswag end let(:api_metadata) do { - path: '/blogs/{blog_id}/comments/{id}', + path_item: { template: '/blogs/{blog_id}/comments/{id}' }, operation: { verb: :put, summary: 'Updates a blog', diff --git a/rswag-specs/spec/rswag/specs/request_factory_spec.rb b/rswag-specs/spec/rswag/specs/request_factory_spec.rb index 40799f4..6a5d92a 100644 --- a/rswag-specs/spec/rswag/specs/request_factory_spec.rb +++ b/rswag-specs/spec/rswag/specs/request_factory_spec.rb @@ -12,7 +12,7 @@ module Rswag end let(:api_metadata) do { - path: '/blogs/{blog_id}/comments/{id}', + path_item: { template: '/blogs/{blog_id}/comments/{id}' }, operation: { verb: :put, summary: 'Updates a blog', @@ -125,6 +125,17 @@ module Rswag expect(path).to eq('/foobar/blogs/1/comments/2') end end + + context "defined at the 'path' level" do + before do + api_metadata[:path_item][:parameters] = [ { name: :blog_id, in: :path } ] + api_metadata[:operation][:parameters] = [ { name: :id, in: :path } ] + end + + it "builds path from parameters defined at path and operation levels" do + expect(path).to eq('/blogs/1/comments/2') + end + end end describe '#build_body(example)' do diff --git a/rswag-specs/spec/rswag/specs/swagger_formatter_spec.rb b/rswag-specs/spec/rswag/specs/swagger_formatter_spec.rb index 93afc71..f904fa5 100644 --- a/rswag-specs/spec/rswag/specs/swagger_formatter_spec.rb +++ b/rswag-specs/spec/rswag/specs/swagger_formatter_spec.rb @@ -24,7 +24,7 @@ module Rswag let(:notification) { OpenStruct.new(group: OpenStruct.new(metadata: api_metadata)) } let(:api_metadata) do { - path: '/blogs', + path_item: { template: '/blogs' }, operation: { verb: :post, summary: 'Creates a blog' }, response: { code: '201', description: 'blog created' } } diff --git a/test-app/spec/integration/blogs_spec.rb b/test-app/spec/integration/blogs_spec.rb index 8d5aa41..15fe8a9 100644 --- a/test-app/spec/integration/blogs_spec.rb +++ b/test-app/spec/integration/blogs_spec.rb @@ -41,12 +41,13 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do end path '/blogs/{id}' do + parameter name: :id, :in => :path, :type => :string + get 'Retrieves a blog' do tags 'Blogs' description 'Retrieves a specific blog by id' operationId 'getBlog' produces 'application/json' - parameter name: :id, :in => :path, :type => :string response '200', 'blog found' do schema '$ref' => '#/definitions/blog' diff --git a/test-app/swagger/v1/swagger.json b/test-app/swagger/v1/swagger.json index 820a0c3..3b135ff 100644 --- a/test-app/swagger/v1/swagger.json +++ b/test-app/swagger/v1/swagger.json @@ -68,6 +68,14 @@ } }, "/blogs/{id}": { + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "required": true + } + ], "get": { "summary": "Retrieves a blog", "tags": [ @@ -78,14 +86,6 @@ "produces": [ "application/json" ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "required": true - } - ], "responses": { "200": { "description": "blog found",