From 7b01ae1aa14c41356b231f60da3149be58788599 Mon Sep 17 00:00:00 2001 From: Igor Kapkov Date: Wed, 22 Jan 2020 15:54:24 +1100 Subject: [PATCH 01/61] Add ENV var to configure --dry-run --- rswag-specs/lib/rswag/specs/configuration.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rswag-specs/lib/rswag/specs/configuration.rb b/rswag-specs/lib/rswag/specs/configuration.rb index 4c6ee6827..8743198e1 100644 --- a/rswag-specs/lib/rswag/specs/configuration.rb +++ b/rswag-specs/lib/rswag/specs/configuration.rb @@ -26,9 +26,11 @@ def swagger_docs end def swagger_dry_run - @swagger_dry_run ||= begin - @rspec_config.swagger_dry_run.nil? || @rspec_config.swagger_dry_run + return @swagger_dry_run if defined? @swagger_dry_run + if ENV.key?('SWAGGER_DRY_RUN') + @rspec_config.swagger_dry_run = ENV['SWAGGER_DRY_RUN'] == '1' end + @swagger_dry_run = @rspec_config.swagger_dry_run.nil? || @rspec_config.swagger_dry_run end def swagger_format From 56eec5948e9d58e798cb759ee6cc3f59a21e60cd Mon Sep 17 00:00:00 2001 From: Igor Kapkov Date: Wed, 25 Mar 2020 16:57:58 +1100 Subject: [PATCH 02/61] Update readme --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5018d3afd..1f0c8b938 100644 --- a/README.md +++ b/README.md @@ -452,14 +452,15 @@ after do |example| example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) } end ``` -You need to disable --dry-run option for Rspec > 3 +You need to disable `--dry-run` option for Rspec > 3. You can do one of the following: -Add to application.rb: -```ruby -RSpec.configure do |config| - config.swagger_dry_run = false -end -``` +- use environment varible `SWAGGER_DRY_RUN` set to `1` during generation command +- or add the following to your `application.rb`: + ```ruby + RSpec.configure do |config| + config.swagger_dry_run = false + end + ``` ### Running tests without documenting ### From f8dbd98bbc877710edd2f249364c517099f42a7a Mon Sep 17 00:00:00 2001 From: Gabriel Sobrinho Date: Thu, 16 Apr 2020 22:00:55 -0300 Subject: [PATCH 03/61] Add a macro for complexes multiparts This will allow to describe multipart in a short way, like JSON payload: Before: put 'Creates a blog with thumbnail' do consumes 'multipart/form-data' parameter name: :title, in: :formData, type: :string, required: true parameter name: :content, in: :formData, type: :string, required: true parameter name: :file, in: :formData, type: :file, required: true let(:blog) { FactoryBot.build(:blog) } let(:title) { blog.title } let(:content) { blog.content } let(:file) { blog.file } ... end After: put 'Creates a blog with thumbnail' do consumes 'multipart/form-data' parameter name: :blog, in: :formData, schema: { '$ref' => '#/definitions/blog' } let(:blog) { FactoryBot.attributes_for(:blog) } ... end Your mileage may vary but you can always choose the best option. --- .../lib/rswag/specs/request_factory.rb | 16 ++++++- .../spec/rswag/specs/request_factory_spec.rb | 14 ++++++ test-app/app/controllers/blogs_controller.rb | 6 +++ test-app/config/routes.rb | 1 + test-app/spec/features/swagger_ui_spec.rb | 1 + test-app/spec/integration/blogs_spec.rb | 27 +++++++++++ test-app/swagger/v1/swagger.json | 46 +++++++++++++++++++ 7 files changed, 110 insertions(+), 1 deletion(-) diff --git a/rswag-specs/lib/rswag/specs/request_factory.rb b/rswag-specs/lib/rswag/specs/request_factory.rb index 2e8f3f1da..e3158362b 100644 --- a/rswag-specs/lib/rswag/specs/request_factory.rb +++ b/rswag-specs/lib/rswag/specs/request_factory.rb @@ -185,8 +185,22 @@ def build_form_payload(parameters, example) # Rather that serializing with the appropriate encoding (e.g. multipart/form-data), # Rails test infrastructure allows us to send the values directly as a hash # PROS: simple to implement, CONS: serialization/deserialization is bypassed in test + smart_payload = build_smart_form_payload(parameters, example) + raw_payload = build_raw_form_payload(parameters, example) + + smart_payload.merge(raw_payload) + end + + def build_smart_form_payload(parameters, example) + smart_tuples = parameters + .select { |p| p[:in] == :formData && p[:schema] } + .map { |p| example.send(p[:name]) } + .reduce({}, :merge) + end + + def build_raw_form_payload(parameters, example) tuples = parameters - .select { |p| p[:in] == :formData } + .select { |p| p[:in] == :formData && !p[:schema] } .map { |p| [p[:name], example.send(p[:name])] } Hash[tuples] end diff --git a/rswag-specs/spec/rswag/specs/request_factory_spec.rb b/rswag-specs/spec/rswag/specs/request_factory_spec.rb index aff5fb4eb..0269bd82d 100644 --- a/rswag-specs/spec/rswag/specs/request_factory_spec.rb +++ b/rswag-specs/spec/rswag/specs/request_factory_spec.rb @@ -178,6 +178,20 @@ module Specs ) end end + + context 'smart form payload' do + before do + metadata[:operation][:consumes] = ['multipart/form-data'] + metadata[:operation][:parameters] = [{ name: 'comment', in: :formData, schema: { type: 'object' } }] + allow(example).to receive(:comment).and_return(text: 'Some comment') + end + + it 'sets payload to hash of names and example values' do + expect(request[:payload]).to eq( + :text => 'Some comment' + ) + end + end end context 'produces content' do diff --git a/test-app/app/controllers/blogs_controller.rb b/test-app/app/controllers/blogs_controller.rb index 75c5ca735..5307baf1d 100644 --- a/test-app/app/controllers/blogs_controller.rb +++ b/test-app/app/controllers/blogs_controller.rb @@ -8,6 +8,12 @@ def create respond_with @blog end + # POST /blogs/multipart + def multipart_create + @blog = Blog.create(params.require(:blog).permit(:title, :content)) + respond_with @blog + end + # POST /blogs/flexible def flexible_create diff --git a/test-app/config/routes.rb b/test-app/config/routes.rb index 038f728eb..e9b611f1d 100644 --- a/test-app/config/routes.rb +++ b/test-app/config/routes.rb @@ -1,5 +1,6 @@ TestApp::Application.routes.draw do + post '/blogs/multipart', to: 'blogs#multipart_create' post '/blogs/flexible', to: 'blogs#flexible_create' post '/blogs/alternate', to: 'blogs#alternate_create' resources :blogs diff --git a/test-app/spec/features/swagger_ui_spec.rb b/test-app/spec/features/swagger_ui_spec.rb index 484ad657a..e09df2120 100644 --- a/test-app/spec/features/swagger_ui_spec.rb +++ b/test-app/spec/features/swagger_ui_spec.rb @@ -7,6 +7,7 @@ expect(page).to have_content('GET /blogs Searches blogs', normalize_ws: true) expect(page).to have_content('POST /blogs Creates a blog', normalize_ws: true) + expect(page).to have_content('POST /blogs/multipart Creates a blog using multipart', normalize_ws: true) expect(page).to have_content('GET /blogs/{id} Retrieves a blog', normalize_ws: true) end end diff --git a/test-app/spec/integration/blogs_spec.rb b/test-app/spec/integration/blogs_spec.rb index ee4a67ef9..d19270a32 100644 --- a/test-app/spec/integration/blogs_spec.rb +++ b/test-app/spec/integration/blogs_spec.rb @@ -49,6 +49,33 @@ end end + path '/blogs/multipart' do + post 'Creates a blog using multipart' do + tags 'Blogs' + description 'Creates a new blog from provided data' + operationId 'createBlogWithMultipart' + consumes 'multipart/form-data' + produces 'application/json' + parameter name: :blog, in: :formData, schema: { type: :object, properties: { name: :blog, type: :object, properties: { '$ref' => '#/definitions/blog' } } } + + let(:blog) { { blog: { title: 'foo', content: 'bar' } } } + + response '201', 'blog created' do + # schema '$ref' => '#/definitions/blog' + run_test! + end + + response '422', 'invalid request' do + schema '$ref' => '#/definitions/errors_object' + + let(:blog) { { blog: { title: 'foo' } } } + run_test! do |response| + expect(response.body).to include("can't be blank") + end + end + end + end + path '/blogs/flexible' do post 'Creates a blog flexible body' do tags 'Blogs' diff --git a/test-app/swagger/v1/swagger.json b/test-app/swagger/v1/swagger.json index 957206cd9..32674b50a 100644 --- a/test-app/swagger/v1/swagger.json +++ b/test-app/swagger/v1/swagger.json @@ -155,6 +155,52 @@ } } }, + "/blogs/multipart": { + "post": { + "summary": "Creates a blog using multipart", + "tags": [ + "Blogs" + ], + "description": "Creates a new blog from provided data", + "operationId": "createBlogWithMultipart", + "parameters": [ + + ], + "responses": { + "201": { + "description": "blog created", + "content": { + } + }, + "422": { + "description": "invalid request", + "content": { + "application/json": { + "schema": { + "$ref": "#/definitions/errors_object" + } + } + } + } + }, + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "name": "blog", + "type": "object", + "properties": { + "$ref": "#/definitions/blog" + } + } + } + } + } + } + } + }, "/blogs/flexible": { "post": { "summary": "Creates a blog flexible body", From 9c297317b2eeb4b4ebaca76c85210fddd56d97f2 Mon Sep 17 00:00:00 2001 From: Oleg Yakovenko Date: Thu, 4 Jun 2020 16:21:43 +0300 Subject: [PATCH 04/61] keep examples content --- README.md | 43 ++++++++++--------- .../lib/generators/rspec/templates/spec.rb | 6 ++- .../lib/rswag/specs/swagger_formatter.rb | 4 +- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index fd3f05c93..a0c5024ca 100644 --- a/README.md +++ b/README.md @@ -363,8 +363,8 @@ you should use the folowing syntax, making sure there are no whitespaces at the ### Specifying/Testing API Security ### -Swagger allows for the specification of different security schemes and their applicability to operations in an API. -To leverage this in rswag, you define the schemes globally in _swagger_helper.rb_ and then use the "security" attribute at the operation level to specify which schemes, if any, are applicable to that operation. +Swagger allows for the specification of different security schemes and their applicability to operations in an API. +To leverage this in rswag, you define the schemes globally in _swagger_helper.rb_ and then use the "security" attribute at the operation level to specify which schemes, if any, are applicable to that operation. Swagger supports :basic, :bearer, :apiKey and :oauth2 and :openIdConnect scheme types. See [the spec](https://swagger.io/docs/specification/authentication/) for more info, as this underwent major changes between Swagger 2.0 and Open API 3.0 ```ruby @@ -416,7 +416,7 @@ describe 'Blogs API' do end # example of documenting an endpoint that handles basic auth and api key based security -describe 'Auth examples API' do +describe 'Auth examples API' do path '/auth-tests/basic-and-api-key' do post 'Authenticates with basic auth and api key' do tags 'Auth Tests' @@ -437,11 +437,11 @@ describe 'Auth examples API' do end end end - + ``` -__NOTE:__ Depending on the scheme types, you'll be required to assign a corresponding parameter value with each example. +__NOTE:__ Depending on the scheme types, you'll be required to assign a corresponding parameter value with each example. For example, :basic auth is required above and so the :Authorization (header) parameter must be set accordingly ## Configuration & Customization ## @@ -479,9 +479,9 @@ rake rswag:specs:swaggerize PATTERN="spec/swagger/**/*_spec.rb" ### Referenced Parameters and Schema Definitions ### -Swagger allows you to describe JSON structures inline with your operation descriptions OR as referenced globals. +Swagger allows you to describe JSON structures inline with your operation descriptions OR as referenced globals. For example, you might have a standard response structure for all failed operations. -Again, this is a structure that changed since swagger 2.0. Notice the new "schemas" section for these. +Again, this is a structure that changed since swagger 2.0. Notice the new "schemas" section for these. Rather than repeating the schema in every operation spec, you can define it globally and provide a reference to it in each spec: ```ruby @@ -549,7 +549,7 @@ end ### Response headers ### -In Rswag, you could use `header` method inside the response block to specify header objects for this response. +In Rswag, you could use `header` method inside the response block to specify header objects for this response. Rswag will validate your response headers with those header objects and inject them into the generated swagger file: ```ruby @@ -597,16 +597,20 @@ To enable examples generation from responses add callback above run_test! like: ``` after do |example| - example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) } + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } end ``` You need to disable --dry-run option for Rspec > 3 - Add to config/environments/test.rb: @@ -645,8 +649,8 @@ describe 'Blogs API', document: false do ``` ##### rswag helper methods ##### -