diff --git a/Gemfile b/Gemfile index f060907..38c2229 100644 --- a/Gemfile +++ b/Gemfile @@ -31,6 +31,7 @@ end group :development do gem 'guard-rspec', require: false + gem 'rswag-specs', path: './rswag-specs' gem 'rubocop' end diff --git a/rswag-specs/lib/rswag/specs/example_group_helpers.rb b/rswag-specs/lib/rswag/specs/example_group_helpers.rb index 87f6e9b..ab2cdb9 100644 --- a/rswag-specs/lib/rswag/specs/example_group_helpers.rb +++ b/rswag-specs/lib/rswag/specs/example_group_helpers.rb @@ -42,6 +42,8 @@ module Rswag # TODO: setup travis CI? # MUST HAVES + # TODO: look at integrating and documenting the rest of the responses in the blog_spec and get a clean 3.0 output + # Then can look at handling different request_body things like $ref, etc # TODO: look at adding request_body method to handle diffs in Open API 2.0 to 3.0 # TODO: look at adding examples in content request_body # https://swagger.io/docs/specification/describing-request-body/ @@ -123,7 +125,7 @@ module Rswag end else before do |example| - submit_request(example.metadata) + submit_request(example.metadata) # end it "returns a #{metadata[:response][:code]} response" do |example| @@ -137,12 +139,14 @@ module Rswag if body_parameter && respond_to?(body_parameter[:name]) && example.metadata[:operation][:requestBody][:content]['application/json'] # save response examples by default example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) } unless response.body.to_s.empty? - + # save request examples using the let(:param_name) { REQUEST_BODY_HASH } syntax in the test - example.metadata[:operation][:requestBody][:content]['application/json'] = { examples: {} } unless example.metadata[:operation][:requestBody][:content]['application/json'][:examples] - json_request_examples = example.metadata[:operation][:requestBody][:content]['application/json'][:examples] - json_request_examples[body_parameter[:name]] = { value: send(body_parameter[:name]) } - example.metadata[:operation][:requestBody][:content]['application/json'][:examples] = json_request_examples + if response.code.to_s =~ /^2\d{2}$/ + example.metadata[:operation][:requestBody][:content]['application/json'] = { examples: {} } unless example.metadata[:operation][:requestBody][:content]['application/json'][:examples] + json_request_examples = example.metadata[:operation][:requestBody][:content]['application/json'][:examples] + json_request_examples[body_parameter[:name]] = { value: send(body_parameter[:name]) } + example.metadata[:operation][:requestBody][:content]['application/json'][:examples] = json_request_examples + end end end end diff --git a/rswag-specs/lib/rswag/specs/swagger_formatter.rb b/rswag-specs/lib/rswag/specs/swagger_formatter.rb index 7c1109a..1463bad 100644 --- a/rswag-specs/lib/rswag/specs/swagger_formatter.rb +++ b/rswag-specs/lib/rswag/specs/swagger_formatter.rb @@ -34,6 +34,15 @@ module Rswag def stop(_notification = nil) @config.swagger_docs.each do |url_path, doc| + # remove 2.0 parameters + doc[:paths].each_pair do |_k, v| + v.each_pair do |_verb, value| + if value&.dig(:parameters) + value[:parameters].reject! { |p| p[:in] == :body } + end + end + end + file_path = File.join(@config.swagger_root, url_path) dirname = File.dirname(file_path) FileUtils.mkdir_p dirname unless File.exist?(dirname) @@ -52,25 +61,19 @@ module Rswag response_code = metadata[:response][:code] response = metadata[:response].reject { |k, _v| k == :code } - if response_code.to_s == '201' - # need to merge in to resppnse - if response[:examples]&.dig('application/json') - example = response[:examples].dig('application/json').dup - response.merge!(content: { 'application/json' => { example: example } }) - response.delete(:examples) - end + # need to merge in to response + if response[:examples]&.dig('application/json') + example = response[:examples].dig('application/json').dup + response.merge!(content: { 'application/json' => { example: example } }) + response.delete(:examples) end + verb = metadata[:operation][:verb] operation = metadata[:operation] .reject { |k, _v| k == :verb } .merge(responses: { response_code => response }) - # can remove the 2.0 compliant body incoming parameters - if operation&.dig(:parameters) - operation[:parameters].reject! { |p| p[:in] == :body } - end - path_template = metadata[:path_item][:template] path_item = metadata[:path_item] .reject { |k, _v| k == :template } diff --git a/rswag-specs/lib/tasks/rswag-specs_tasks.rake b/rswag-specs/lib/tasks/rswag-specs_tasks.rake index adc128c..6b9e30a 100644 --- a/rswag-specs/lib/tasks/rswag-specs_tasks.rake +++ b/rswag-specs/lib/tasks/rswag-specs_tasks.rake @@ -6,7 +6,7 @@ namespace :rswag do desc 'Generate Swagger JSON files from integration specs' RSpec::Core::RakeTask.new('swaggerize') do |t| t.pattern = 'spec/requests/**/*_spec.rb, spec/api/**/*_spec.rb, spec/integration/**/*_spec.rb' - + # TODO: fix this, as dry-run is always true despite what is in the config # NOTE: rspec 2.x support if Rswag::Specs::RSPEC_VERSION > 2 && Rswag::Specs.config.swagger_dry_run t.rspec_opts = [ '--format Rswag::Specs::SwaggerFormatter', '--dry-run', '--order defined' ] diff --git a/test-app/swagger/v1/swagger.json b/test-app/swagger/v1/swagger.json index 6e24e14..e8fa6eb 100644 --- a/test-app/swagger/v1/swagger.json +++ b/test-app/swagger/v1/swagger.json @@ -5,81 +5,6 @@ "version": "v1" }, "paths": { - "/auth-tests/basic": { - "post": { - "summary": "Authenticates with basic auth", - "tags": [ - "Auth Tests" - ], - "operationId": "testBasicAuth", - "security": [ - { - "basic_auth": [ - - ] - } - ], - "responses": { - "204": { - "description": "Valid credentials" - }, - "401": { - "description": "Invalid credentials" - } - } - } - }, - "/auth-tests/api-key": { - "post": { - "summary": "Authenticates with an api key", - "tags": [ - "Auth Tests" - ], - "operationId": "testApiKey", - "security": [ - { - "api_key": [ - - ] - } - ], - "responses": { - "204": { - "description": "Valid credentials" - }, - "401": { - "description": "Invalid credentials" - } - } - } - }, - "/auth-tests/basic-and-api-key": { - "post": { - "summary": "Authenticates with basic auth and api key", - "tags": [ - "Auth Tests" - ], - "operationId": "testBasicAndApiKey", - "security": [ - { - "basic_auth": [ - - ], - "api_key": [ - - ] - } - ], - "responses": { - "204": { - "description": "Valid credentials" - }, - "401": { - "description": "Invalid credentials" - } - } - } - }, "/blogs": { "post": { "summary": "Creates a blog", @@ -94,23 +19,55 @@ "produces": [ "application/json" ], - "parameters": [ - { - "name": "blog", - "in": "body", - "schema": { - "$ref": "#/components/schemas/blog" + "requestBody": { + "required": true, + "content": { + "application/json": { + "examples": { + "blog": { + "value": { + "blog": { + "title": "foo", + "content": "bar" + } + } + } + } } } + }, + "parameters": [ + ], "responses": { "201": { - "description": "blog created" + "description": "blog created", + "content": { + "application/json": { + "example": { + "id": 1, + "title": "foo", + "content": "bar", + "thumbnail": null + } + } + } }, "422": { "description": "invalid request", "schema": { "$ref": "#/components/schemas/errors_object" + }, + "content": { + "application/json": { + "example": { + "errors": { + "content": [ + "can't be blank" + ] + } + } + } } } } @@ -138,91 +95,6 @@ } } } - }, - "/blogs/{id}": { - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "required": true - } - ], - "get": { - "summary": "Retrieves a blog", - "tags": [ - "Blogs" - ], - "description": "Retrieves a specific blog by id", - "operationId": "getBlog", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "blog found", - "headers": { - "ETag": { - "type": "string" - }, - "Last-Modified": { - "type": "string" - }, - "Cache-Control": { - "type": "string" - } - }, - "schema": { - "$ref": "#/components/schemas/blog" - }, - "examples": { - "application/json": { - "id": 1, - "title": "Hello world!", - "content": "Hello world and hello universe. Thank you all very much!!!", - "thumbnail": "thumbnail.png" - } - } - }, - "404": { - "description": "blog not found" - } - } - } - }, - "/blogs/{id}/upload": { - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "required": true - } - ], - "put": { - "summary": "Uploads a blog thumbnail", - "tags": [ - "Blogs" - ], - "description": "Upload a thumbnail for specific blog by id", - "operationId": "uploadThumbnailBlog", - "consumes": [ - "multipart/form-data" - ], - "parameters": [ - { - "name": "file", - "in": "formData", - "type": "file", - "required": true - } - ], - "responses": { - "200": { - "description": "blog updated" - } - } - } } }, "servers": [