Coder Social home page Coder Social logo

rswag / rswag Goto Github PK

View Code? Open in Web Editor NEW
1.9K 27.0 411.0 8.79 MB

Seamlessly adds a Swagger to Rails-based API's

License: MIT License

Ruby 95.81% HTML 3.15% Shell 0.48% JavaScript 0.11% CSS 0.06% Dockerfile 0.35% Makefile 0.04%
swagger-ui rswag openapi

rswag's Introduction

rswag

Build Status Maintainability

OpenApi 3.0 and Swagger 2.0 compatible!

Seeking maintainers! Got a pet-bug that needs fixing? Just let us know in your issue/pr that you'd like to step up to help.

Rswag extends rspec-rails "request specs" with a Swagger-based DSL for describing and testing API operations. You describe your API operations with a succinct, intuitive syntax, and it automatically runs the tests. Once you have green tests, run a rake task to auto-generate corresponding Swagger files and expose them as YAML or JSON endpoints. Rswag also provides an embedded version of the awesome swagger-ui that's powered by the exposed file. This toolchain makes it seamless to go from integration specs, which you're probably doing in some form already, to living documentation for your API consumers.

Api Rswag creates Swagger tooling for Rails API's. Generate beautiful API documentation, including a UI to explore and test operations, directly from your rspec integration tests.

And that's not all ...

Once you have an API that can describe itself in Swagger, you've opened the treasure chest of Swagger-based tools including a client generator that can be targeted to a wide range of popular platforms. See swagger-codegen for more details.

Table of Contents

Getting Started

  1. Add this line to your applications Gemfile:

    gem 'rswag'

    or if you like to avoid loading rspec in other bundler groups load the rswag-specs component separately. Note: Adding it to the :development group is not strictly necessary, but without it, generators and rake tasks must be preceded by RAILS_ENV=test.

    # Gemfile
    gem 'rswag-api'
    gem 'rswag-ui'
    
    group :development, :test do
      gem 'rspec-rails'
      gem 'rswag-specs'
    end
  2. Run the install generator

    rails g rswag:install

    Or run the install generators for each package separately if you installed Rswag as separate gems, as indicated above:

    rails g rswag:api:install
    rails g rswag:ui:install
    RAILS_ENV=test rails g rswag:specs:install
  3. Create an integration spec to describe and test your API. There is also a generator which can help get you started rails generate rspec:swagger API::MyController

    # spec/requests/blogs_spec.rb
    require 'swagger_helper'
    
    describe 'Blogs API' do
    
      path '/blogs' do
    
        post 'Creates a blog' do
          tags 'Blogs'
          consumes 'application/json'
          parameter name: :blog, in: :body, schema: {
            type: :object,
            properties: {
              title: { type: :string },
              content: { type: :string }
            },
            required: [ 'title', 'content' ]
          }
    
          response '201', 'blog created' do
            let(:blog) { { title: 'foo', content: 'bar' } }
            run_test!
          end
    
          response '422', 'invalid request' do
            let(:blog) { { title: 'foo' } }
            run_test!
          end
        end
      end
    
      path '/blogs/{id}' do
    
        get 'Retrieves a blog' do
          tags 'Blogs', 'Another Tag'
          produces 'application/json', 'application/xml'
          parameter name: :id, in: :path, type: :string
          request_body_example value: { some_field: 'Foo' }, name: 'basic', summary: 'Request example description'
    
          response '200', 'blog found' do
            schema type: :object,
              properties: {
                id: { type: :integer },
                title: { type: :string },
                content: { type: :string }
              },
              required: [ 'id', 'title', 'content' ]
    
            let(:id) { Blog.create(title: 'foo', content: 'bar').id }
            run_test!
          end
    
          response '404', 'blog not found' do
            let(:id) { 'invalid' }
            run_test!
          end
    
          response '406', 'unsupported accept header' do
            let(:'Accept') { 'application/foo' }
            run_test!
          end
        end
      end
    end

By default, the above command will create spec under spec/requests folder. You can pass an option to change this default path as in rails generate rspec:swagger API::BlogsController --spec_path integration. This will create the spec file spec/integration/blogs_spec.rb

  1. Generate the Swagger JSON file(s)

    rake rswag:specs:swaggerize

    This common command is also aliased as rake rswag.

    Or if you installed your gems separately:

    RAILS_ENV=test rails rswag
    
  2. Spin up your app and check out the awesome, auto-generated docs at /api-docs!

The rspec DSL

Paths, Operations and Responses

If you've used Swagger before, then the syntax should be very familiar. To describe your API operations, start by specifying a path and then list the supported operations (i.e. HTTP verbs) for that path. Path parameters must be surrounded by curly braces ({}). Within an operation block (see "post" or "get" in the example above), most of the fields supported by the Swagger "Operation" object are available as methods on the example group. To list (and test) the various responses for an operation, create one or more response blocks. Again, you can reference the Swagger "Response" object for available fields.

Take special note of the run_test! method that's called within each response block. This tells rswag to create and execute a corresponding example. It builds and submits a request based on parameter descriptions and corresponding values that have been provided using the rspec "let" syntax. For example, the "post" description in the example above specifies a "body" parameter called "blog". It also lists 2 different responses. For the success case (i.e. the 201 response), notice how "let" is used to set the blog parameter to a value that matches the provided schema. For the failure case (i.e. the 422 response), notice how it's set to a value that does not match the provided schema. When the test is executed, rswag also validates the actual response code and, where applicable, the response body against the provided JSON Schema.

If you want to add metadata to the example, you can pass keyword arguments to the run_test! method:

# to run particular test case
response '201', 'blog created' do
  run_test! focus: true
end

# to write vcr cassette
response '201', 'blog created' do
  run_test! vcr: true
end

If you want to customize the description of the generated specification, a description can be passed to run_test!

response '201', 'blog created' do
  run_test! "custom spec description"
end

If you want to do additional validation on the response, pass a block to the run_test! method:

response '201', 'blog created' do
  run_test! do |response|
    data = JSON.parse(response.body)
    expect(data['title']).to eq('foo')
  end
end

If you'd like your specs to be a little more explicit about what's going on here, you can replace the call to run_test! with equivalent "before" and "it" blocks:

response '201', 'blog created' do
  let(:blog) { { title: 'foo', content: 'bar' } }

  before do |example|
    submit_request(example.metadata)
  end

  it 'returns a valid 201 response' do |example|
    assert_response_matches_metadata(example.metadata)
  end
end

Also note that the examples generated with run_test! are tagged with the :rswag so they can easily be filtered. E.g. rspec --tag rswag

date-time in query parameters

Input sent in queries of Rspec tests is HTML safe, including date-time strings.

parameter name: :date_time, in: :query, type: :string

response '200', 'blog found' do
  let(:date_time) { DateTime.new(2001, 2, 3, 4, 5, 6, '-7').to_s }

  run_test! do
    expect(request[:path]).to eq('/blogs?date_time=2001-02-03T04%3A05%3A06-07%3A00')
  end
end

Strict schema validation

By default, if response body contains undocumented properties tests will pass. To keep your responses clean and validate against a strict schema definition you can set the global config option:

# spec/swagger_helper.rb
RSpec.configure do |config|
  config.openapi_strict_schema_validation = true
end

or set the option per individual example:

# using in run_test!
describe 'Blogs API' do
  path '/blogs' do
    post 'Creates a blog' do
      ...
      response '201', 'blog created' do
        let(:blog) { { title: 'foo', content: 'bar' } }

        run_test!(openapi_strict_schema_validation: true)
      end
    end
  end
end

# using in response block
describe 'Blogs API' do
  path '/blogs' do
    post 'Creates a blog' do
      ...

      response '201', 'blog created', openapi_strict_schema_validation: true do
        let(:blog) { { title: 'foo', content: 'bar' } }

        run_test!
      end
    end
  end
end

# using in an explicit example
describe 'Blogs API' do
  path '/blogs' do
    post 'Creates a blog' do
      ...
      response '201', 'blog created' do
        let(:blog) { { title: 'foo', content: 'bar' } }

        before do |example|
          submit_request(example.metadata)
        end

        it 'returns a valid 201 response', openapi_strict_schema_validation: true do |example|
          assert_response_matches_metadata(example.metadata)
        end
      end
    end
  end
end

Null Values

This library is currently using JSON::Draft4 for validation of response models. Nullable properties can be supported with the non-standard property 'x-nullable' to a definition to allow null/nil values to pass. Or you can add the new standard nullable property to a definition.

describe 'Blogs API' do
  path '/blogs' do
    post 'Creates a blog' do
      ...

      response '200', 'blog found' do
        schema type: :object,
          properties: {
            id: { type: :integer },
            title: { type: :string, nullable: true }, # preferred syntax
            content: { type: :string, 'x-nullable': true } # legacy syntax, but still works
          }
        ....
      end
    end
  end
end

Support for oneOf, anyOf or AllOf schemas

Open API 3.0 now supports more flexible schema validation with the oneOf, anyOf and allOf directives. rswag will handle these definitions and validate them properly.

Notice the schema inside the response section. Placing a schema method inside the response will validate (and fail the tests) if during the integration test run the endpoint response does not match the response schema. This test validation can handle anyOf and allOf as well. See below:

  path '/blogs/flexible' do
    post 'Creates a blog flexible body' do
      tags 'Blogs'
      description 'Creates a flexible blog from provided data'
      operationId 'createFlexibleBlog'
      consumes 'application/json'
      produces 'application/json'

      parameter name: :blog, in: :body, schema: {
          oneOf: [
            { '$ref' => '#/components/schemas/blog' },
            { '$ref' => '#/components/schemas/flexible_blog' }
          ]
        }

      response '201', 'flexible blog created' do
        schema oneOf: [{ '$ref' => '#/components/schemas/blog' }, { '$ref' => '#/components/schemas/flexible_blog' }]
        run_test!
      end
    end
  end

This automatic schema validation is a powerful feature of rswag.

Global Metadata

In addition to paths, operations and responses, Swagger also supports global API metadata. When you install rswag, a file called swagger_helper.rb is added to your spec folder. This is where you define one or more Swagger documents and provide global metadata. Again, the format is based on Swagger so most of the global fields supported by the top level "Swagger" object can be provided with each document definition. As an example, you could define a Swagger document for each version of your API and in each case specify a title, version string. In Open API 3.0 the pathing and server definitions have changed a bit Swagger host/basePath:

# spec/swagger_helper.rb
RSpec.configure do |config|
  config.openapi_root = Rails.root.to_s + '/swagger'

  config.openapi_specs = {
    'v1/swagger.json' => {
      openapi: '3.0.1',
      info: {
        title: 'API V1',
        version: 'v1',
        description: 'This is the first version of my API'
      },
      servers: [
        {
          url: 'https://{defaultHost}',
          variables: {
            defaultHost: {
                default: 'www.example.com'
            }
          }
        }
      ]
    },

    'v2/swagger.json' => {
      openapi: '3.0.1',
      info: {
        title: 'API V2',
        version: 'v2',
        description: 'This is the second version of my API'
      },
      servers: [
        {
          url: '{protocol}://{defaultHost}',
          variables: {
            protocol: {
              default: :https
            },
            defaultHost: {
                default: 'www.example.com'
            }
          }
        }
      ]
    }
  }
end

Supporting multiple versions of API

By default, the paths, operations and responses defined in your spec files will be associated with the first Swagger document in swagger_helper.rb. If your API has multiple versions, you should be using separate documents to describe each of them. In order to assign a file with a given version of API, you'll need to add the openapi_spec tag to each spec specifying its target document name:

# spec/requests/v2/blogs_spec.rb
describe 'Blogs API', openapi_spec: 'v2/swagger.yaml' do

  path '/blogs' do
  ...

  path '/blogs/{id}' do
  ...
end

Supporting YAML format

By default, the swagger docs are generated in JSON format. If you want to generate them in YAML format, you can specify the swagger format in the swagger_helper.rb file:

# spec/swagger_helper.rb
RSpec.configure do |config|
  config.openapi_root = Rails.root.to_s + '/swagger'
  
  # Use if you want to see which test is running
  # config.formatter = :documentation

  # Generate swagger docs in YAML format
  config.openapi_format = :yaml

  config.openapi_specs = {
    'v1/swagger.yaml' => {
      openapi: '3.0.1',
      info: {
        title: 'API V1',
        version: 'v1',
        description: 'This is the first version of my API'
      },
      servers: [
        {
          url: 'https://{defaultHost}',
          variables: {
            defaultHost: {
                default: 'www.example.com'
            }
          }
        }
      ]
    },
  }
end

Formatting the description literals:

Swagger supports the Markdown syntax to format strings. This can be especially handy if you were to provide a long description of a given API version or endpoint. Use this guide for reference.

NOTE: There is one difference between the official Markdown syntax and Swagger interpretation, namely tables. To create a table like this:

Column1 Column2
cell1 cell2

you should use the following syntax, making sure there is no whitespace at the start of any of the lines:


| Column1 | Column2 | 
 |
| ------- | ------- |
| cell1   | cell2    |


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 supports :basic, :bearer, :apiKey and :oauth2 and :openIdConnect scheme types. See the spec for more info, as this underwent major changes between Swagger 2.0 and Open API 3.0

# spec/swagger_helper.rb
RSpec.configure do |config|
  config.openapi_root = Rails.root.to_s + '/swagger'

  config.openapi_specs = {
    'v1/swagger.json' => {
      ...  # note the new Open API 3.0 compliant security structure here, under "components"
      components: {
        securitySchemes: {
          basic_auth: {
            type: :http,
            scheme: :basic
          },
          api_key: {
            type: :apiKey,
            name: 'api_key',
            in: :query
          }
        }
      }
    }
  }
end

# spec/requests/blogs_spec.rb
describe 'Blogs API' do

  path '/blogs' do

    post 'Creates a blog' do
      tags 'Blogs'
      security [ basic_auth: [] ]
      ...

      response '201', 'blog created' do
        let(:Authorization) { "Basic #{::Base64.strict_encode64('jsmith:jspass')}" }
        run_test!
      end

      response '401', 'authentication failed' do
        let(:Authorization) { "Basic #{::Base64.strict_encode64('bogus:bogus')}" }
        run_test!
      end
    end
  end
end

# example of documenting an endpoint that handles basic auth and api key based security
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'
      operationId 'testBasicAndApiKey'
      security [{ basic_auth: [], api_key: [] }]

      response '204', 'Valid credentials' do
        let(:Authorization) { "Basic #{::Base64.strict_encode64('jsmith:jspass')}" }
        let(:api_key) { 'foobar' }
        run_test!
      end

      response '401', 'Invalid credentials' do
        let(:Authorization) { "Basic #{::Base64.strict_encode64('jsmith:jspass')}" }
        let(:api_key) { 'bar-foo' }
        run_test!
      end
    end
  end
end

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

The steps described above will get you up and running with minimal setup. However, rswag offers a lot of flexibility to customize as you see fit. Before exploring the various options, you'll need to be aware of its different components. The following table lists each of them and the files that get added/updated as part of a standard install.

Gem Description Added/Updated
rswag-specs Swagger-based DSL for rspec & accompanying rake task for generating Swagger files spec/swagger_helper.rb
rswag-api Rails Engine that exposes your Swagger files as JSON endpoints config/initializers/rswag_api.rb, config/routes.rb
rswag-ui Rails Engine that includes swagger-ui and powers it from your Swagger endpoints config/initializers/rswag-ui.rb, config/routes.rb

Output Location for Generated Swagger Files

You can adjust this in the swagger_helper.rb that's installed with rswag-specs:

# spec/swagger_helper.rb
RSpec.configure do |config|
  config.openapi_root = Rails.root.to_s + '/your-custom-folder-name'
  ...
end

NOTE: If you do change this, you'll also need to update the rswag_api.rb initializer (assuming you're using rswag-api). More on this later.

Input Location for Rspec Tests

By default, rswag will search for integration tests in spec/requests, spec/api and spec/integration. If you want to use tests from other locations, provide the PATTERN argument to rake:

# search for tests in spec/swagger
rake rswag:specs:swaggerize PATTERN="spec/swagger/**/*_spec.rb"

Additional rspec options

You can add additional rspec parameters using the ADDITIONAL_RSPEC_OPTS env variable:

# Only include tests tagged "rswag"
rake rswag:specs:swaggerize ADDITIONAL_RSPEC_OPTS="--tag rswag"

Referenced Parameters and Schema Definitions

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. Rather than repeating the schema in every operation spec, you can define it globally and provide a reference to it in each spec:

# spec/swagger_helper.rb
config.openapi_specs = {
  'v1/swagger.json' => {
    openapi: '3.0.0',
    info: {
      title: 'API V1'
    },
    components: {
      schemas: {
        errors_object: {
          type: 'object',
          properties: {
            errors: { '$ref' => '#/components/schemas/errors_map' }
          }
        },
        errors_map: {
          type: 'object',
          additionalProperties: {
            type: 'array',
            items: { type: 'string' }
          }
        },
        blog: {
          type: 'object',
          properties: {
            id: { type: 'integer' },
            title: { type: 'string' },
            content: { type: 'string', nullable: true },
            thumbnail: { type: 'string', nullable: true }
          },
          required: %w[id title]
        },
        new_blog: {
          type: 'object',
          properties: {
            title: { type: 'string' },
            content: { type: 'string', nullable: true },
            thumbnail: { type: 'string', format: 'binary', nullable: true }
          },
          required: %w[title]
        }
      }
    }
  }
}

# spec/requests/blogs_spec.rb
describe 'Blogs API' do

  path '/blogs' do

    post 'Creates a blog' do

      parameter name: :new_blog, in: :body, schema: { '$ref' => '#/components/schemas/new_blog' }

      response 422, 'invalid request' do
        schema '$ref' => '#/components/schemas/errors_object'
  ...
end

# spec/requests/comments_spec.rb
describe 'Blogs API' do

  path '/blogs/{blog_id}/comments' do

    post 'Creates a comment' do

      response 422, 'invalid request' do
        schema '$ref' => '#/components/schemas/errors_object'
  ...
end

Request examples

# spec/integration/blogs_spec.rb
describe 'Blogs API' do

  path '/blogs/{blog_id}' do

    get 'Retrieves a blog' do

      request_body_example value: { some_field: 'Foo' }, name: 'request_example_1', summary: 'A request example'

      response 200, 'blog found' do
        ...

to use the actual request from the spec as the example:

config.after(:each, operation: true, use_as_request_example: true) do |spec|
  spec.metadata[:operation][:request_examples] ||= []

  example = {
    value: JSON.parse(request.body.string, symbolize_names: true),
    name: 'request_example_1',
    summary: 'A request example'
  }

  spec.metadata[:operation][:request_examples] << example
end

Response headers

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:

# spec/requests/comments_spec.rb
describe 'Blogs API' do

  path '/blogs/{blog_id}/comments' do

    post 'Creates a comment' do

      response 422, 'invalid request' do
        header 'X-Rate-Limit-Limit', schema: { type: :integer }, description: 'The number of allowed requests in the current period'
        header 'X-Rate-Limit-Remaining', schema: { type: :integer }, description: 'The number of remaining requests in the current period'
  ...
end

Nullable or Optional Response Headers

You can include nullable or required to specify whether a response header must be present or may be null. When nullable is not included, the headers validation validates that the header response is non-null. When required is not included, the headers validation validates the the header response is passed.

# spec/integration/comments_spec.rb
describe 'Blogs API' do

  path '/blogs/{blog_id}/comments' do

    get 'Gets a list of comments' do

      response 200, 'blog found' do
        header 'X-Cursor', schema: { type: :string, nullable: true }, description: 'The cursor to get the next page of comments.'
        header 'X-Per-Page', schema: { type: :integer }, required: false, description: 'The number of comments per page.'
  ...
end

Response examples

You can provide custom response examples to the generated swagger file by calling the method examples inside the response block: However, auto generated example responses are now enabled by default in rswag. See below.

# spec/requests/blogs_spec.rb
describe 'Blogs API' do

  path '/blogs/{blog_id}' do

    get 'Retrieves a blog' do

      response 200, 'blog found' do
        example 'application/json', :example_key, {
            id: 1,
            title: 'Hello world!',
            content: '...'
          }
        example 'application/json', :example_key_2, {
            id: 1,
            title: 'Hello world!',
            content: '...'
          }, "Summary of the example", "Longer description of the example"
  ...
end

Enable auto generation examples from responses

To enable examples generation from responses add callback above run_test! like:

after do |example|
  content = example.metadata[:response][:content] || {}
  example_spec = {
    "application/json"=>{
      examples: {
        test_example: {
          value: JSON.parse(response.body, symbolize_names: true)
        }
      }
    }
  }
  example.metadata[:response][:content] = content.deep_merge(example_spec)
end

Dry Run Option

The --dry-run option is enabled by default for Rspec 3, but if you need to disable it you can use the environment variable RSWAG_DRY_RUN=0 during the generation command or add the following to your config/environments/test.rb:

RSpec.configure do |config|
  config.rswag_dry_run = false
end

Running tests without documenting

If you want to use Rswag for testing without adding it to you swagger docs, you can provide the document tag:

describe 'Blogs API' do
  path '/blogs/{blog_id}' do
    get 'Retrieves a blog' do
      # documentation is now disabled for this response only
      response 200, 'blog found', document: false do
        ...

You can also reenable documentation for specific responses only:

# documentation is now disabled
describe 'Blogs API', document: false do
  path '/blogs/{blog_id}' do
    get 'Retrieves a blog' do
      # documentation is reenabled for this response only
      response 200, 'blog found', document: true do
        ...
      end

      response 401, 'special case' do
        ...
      end
rswag helper methods

Route Prefix for Swagger JSON Endpoints

The functionality to expose Swagger files, such as those generated by rswag-specs, as JSON endpoints is implemented as a Rails Engine. As with any Engine, you can change its mount prefix in routes.rb:

TestApp::Application.routes.draw do
  ...

  mount Rswag::Api::Engine => 'your-custom-prefix'
end

Assuming a Swagger file exists at <openapi_root>/v1/swagger.json, this configuration would expose the file as the following JSON endpoint:

GET http://<hostname>/your-custom-prefix/v1/swagger.json

Root Location for Swagger Files

You can adjust this in the rswag_api.rb initializer that's installed with rspec-api:

Rswag::Api.configure do |c|
  c.openapi_root = Rails.root.to_s + '/your-custom-folder-name'
  ...
end

NOTE: If you're using rswag-specs to generate Swagger files, you'll want to ensure they both use the same <openapi_root>. The reason for separate settings is to maintain independence between the two gems. For example, you could install rswag-api independently and create your Swagger files manually.

Dynamic Values for Swagger JSON

There may be cases where you need to add dynamic values to the Swagger JSON that's returned by rswag-api. For example, you may want to provide an explicit host name. Rather than hardcoding it, you can configure a filter that's executed prior to serializing every Swagger document:

Rswag::Api.configure do |c|
  ...

  c.swagger_filter = lambda { |swagger, env| swagger['host'] = env['HTTP_HOST'] }
end

Note how the filter is passed the rack env for the current request. This provides a lot of flexibility. For example, you can assign the "host" property (as shown) or you could inspect session information or an Authorization header and remove operations based on user permissions.

Custom Headers for Swagger Files

You can specify custom headers for serving your generated Swagger JSON. For example you may want to force a specific charset for the 'Content-Type' header. You can configure a hash of headers to be sent with the request:

Rswag::Api.configure do |c|
  ...

  c.swagger_headers = { 'Content-Type' => 'application/json; charset=UTF-8' }
end

Take care when overriding Content-Type if you serve both YAML and JSON files as it will no longer switch the Content-Type header correctly.

Enable Swagger Endpoints for swagger-ui

You can update the rswag_ui.rb initializer, installed with rswag-ui, to specify which Swagger endpoints should be available to power the documentation UI. If you're using rswag-api, these should correspond to the Swagger endpoints it exposes. When the UI is rendered, you'll see these listed in a drop-down to the top right of the page:

Rswag::Ui.configure do |c|
  c.openapi_endpoint '/api-docs/v1/swagger.json', 'API V1 Docs'
  c.openapi_endpoint '/api-docs/v2/swagger.json', 'API V2 Docs'
end

Enable Simple Basic Auth for swagger-ui

You can also update the rswag_ui.rb initializer, installed with rswag-ui to specify a username and password should you want to keep your documentation private.

Rswag::Ui.configure do |c|
  c.basic_auth_enabled = true
  c.basic_auth_credentials 'username', 'password'
end

Route Prefix for the swagger-ui

Similar to rswag-api, you can customize the swagger-ui path by changing its mount prefix in routes.rb:

TestApp::Application.routes.draw do
  ...

  mount Rswag::Api::Engine => 'api-docs'
  mount Rswag::Ui::Engine => 'your-custom-prefix'
end

Customizing the swagger-ui

The swagger-ui provides several options for customizing its behavior, all of which are documented here https://github.com/swagger-api/swagger-ui/tree/2.x#swaggerui. If you need to tweak these or customize the overall look and feel of your swagger-ui, then you'll need to provide your own version of index.html. You can do this with the following generator.

rails g rswag:ui:custom

This will add a local version that you can modify at app/views/rswag/ui/home/index.html.erb. For example, it will let you to add your own <title> and favicon.

To replace the "Swagger sponsored by" brand image, you can add the following script to the generated file:

<script>
  (function () {
  window.addEventListener("load", function () {
      setTimeout(function () {

          var logo = document.getElementsByClassName('link');

          logo[0].children[0].alt = "My API";
          logo[0].children[0].src = "/favicon.png";
      });
  }); })();
</script>

The above script would expect to find an image named favicon.png in the public folder.

Serve UI Assets Directly from your Web Server

Rswag ships with an embedded version of the swagger-ui, which is a static collection of JavaScript and CSS files. These assets are served by the rswag-ui middleware. However, for optimal performance you may want to serve them directly from your web server (e.g. Apache or NGINX). To do this, you'll need to copy them to the web server root. This is the "public" folder in a typical Rails application.

bundle exec rake rswag:ui:copy_assets[public/api-docs]

NOTE:: The provided subfolder MUST correspond to the UI mount prefix - "api-docs" by default.

Notes to test swagger output locally with swagger editor

docker pull swaggerapi/swagger-editor
docker run -d -p 80:8080 swaggerapi/swagger-editor

This will run the swagger editor in the docker daemon and can be accessed at http://localhost. From here, you can use the UI to load the generated swagger.json to validate the output.

Custom :getter option for parameter

To avoid conflicts with Rspec include matcher and other possible intersections like status method:

...
parameter name: :status,
          getter: :filter_status,
          in: :query,
          schema: {
            type: :string,
            enum: %w[one two three],
          }, required: false

let(:status) { nil } # will not be used in query string
let(:filter_status) { 'one' } # `&status=one` will be provided in final query

Linting with RuboCop RSpec

When you lint your RSpec spec files with rubocop-rspec, it will fail to detect RSpec aliases that Rswag defines. Make sure to use rubocop-rspec 2.0 or newer and add the following to your .rubocop.yml:

inherit_gem:
  rswag-specs: .rubocop_rspec_alias_config.yml

rswag's People

Contributors

a-lavis avatar akabiru avatar alexcastrodev avatar asavageiv avatar bookofgreg avatar dependabot[bot] avatar domaindrivendev avatar drewish avatar gobijan avatar hdpuk86 avatar iainbryson avatar ilkhamgaysin avatar jamie avatar jdanielian avatar johnnykei avatar jtannas avatar ksm125 avatar lukecivantos avatar mohamed-karam avatar mynnx avatar navigatingcancer-scripts avatar nebojsastrbac997 avatar oblakeerickson avatar olegykz avatar romanblanco avatar saturnflyer avatar sergioisidoro avatar singhgarima avatar vinh0604 avatar ydah avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rswag's Issues

Pattern change

I know that you can change the pattern when running the specs but it does not look like you can change the pattern when you want run the swaggerize rake task.

IE: I want to store the swagger specs in /spec/swagger/

rake swaggerize doesn't create directories

I edited spec/swagger_helper.rb to change the directory name to reflect a date based versioning scheme:

  config.swagger_docs = {
    'v2016-09-01/swagger.json' => {
      swagger: '2.0',
      info: {
        title: 'Blah API',
        version: 'v2016-09-01'
      }
    }
  }

And when I ran the rake task it died because the directory didn't exist:

% bundle exec rake swaggerize
/Users/andrew/.rbenv/versions/2.3.1/bin/ruby -I/Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/lib:/Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-support-3.5.0/lib /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/exe/rspec --pattern spec/requests/\*\*/\*_spec.rb,\ spec/api/\*\*/\*_spec.rb,\ spec/integration/\*\*/\*_spec.rb --format SwaggerRails::RSpec::Formatter --dry-run --order defined
Generating Swagger Docs ...
/Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/swagger_rails-5003b7ccd9ff/lib/swagger_rails/rspec/formatter.rb:32:in `initialize': No such file or directory @ rb_sysopen - /Users/andrew/projects/accounting_integration/swagger/v2016-09-01/swagger.json (Errno::ENOENT)
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/swagger_rails-5003b7ccd9ff/lib/swagger_rails/rspec/formatter.rb:32:in `open'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/swagger_rails-5003b7ccd9ff/lib/swagger_rails/rspec/formatter.rb:32:in `block in stop'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/swagger_rails-5003b7ccd9ff/lib/swagger_rails/rspec/formatter.rb:29:in `each'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/swagger_rails-5003b7ccd9ff/lib/swagger_rails/rspec/formatter.rb:29:in `stop'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/lib/rspec/core/reporter.rb:189:in `block in notify'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/lib/rspec/core/reporter.rb:188:in `each'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/lib/rspec/core/reporter.rb:188:in `notify'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/lib/rspec/core/reporter.rb:183:in `stop'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/lib/rspec/core/reporter.rb:157:in `block in finish'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/lib/rspec/core/reporter.rb:175:in `close_after'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/lib/rspec/core/reporter.rb:156:in `finish'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/lib/rspec/core/reporter.rb:79:in `report'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/lib/rspec/core/runner.rb:111:in `run_specs'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/lib/rspec/core/runner.rb:87:in `run'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/lib/rspec/core/runner.rb:71:in `run'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/lib/rspec/core/runner.rb:45:in `invoke'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rspec-core-3.5.2/exe/rspec:4:in `<main>'

Running mkdir swagger/v2016-09-01 and then rake swaggerize again fixed it.

It seems like it should just create any directories it needs or at least handle it a bit more gracefully.

Feature Request - Ordering

I'd like a way to specify what order the specs will be inserted in swagger.json. The tags are helpful to a degree, but part of my documentation is contingent on steps to setting up, and this seems to insert by operation.

parameters don't support references

According to the Swagger spec Path Item Object's parameters can be either a Parameter or a Reference

Field Name Type Description
parameters [Parameter Object Reference Object]

http://swagger.io/specification/#pathItemObject

But in the DSL the parameter method prevents the use of references by requiring the name:

     def parameter(name, attributes={})
        metadata[:parameters] << { name: name.to_s }.merge(attributes)
      end

https://github.com/domaindrivendev/swagger_rails/blob/master/lib/swagger_rails/rspec/dsl.rb#L41-L43

The most literal translation of the syntax for references would look something like:

parameter $ref: '#/parameters/Pet'

But the $ is a no go.

Something like this isn't too bad:

parameter '$ref' => '#/parameters/Pet'

If we loose the $ we can get terser:

parameter ref: '#/parameters/Pet'

I wasn't quite sure how the specs for the DSL worked so I didn't bother with a full PR but I think we could get this working with something simple like:

      # Accepts parameter objects:
      #    parameter :petId, in: :path, type: :integer, required: true
      # Or references:
      #    parameter ref: '#/parameters/Pet'
      def parameter(name, attributes={})
        metadata[:parameters] << if name.respond_to?(:has_key?)
          { '$ref': name.delete(:ref) || attributes.delete('ref') }
        else
          { name: name.to_s }.merge(attributes)
        end
      end

`require': cannot load such file -- swagger_helper

Hi,

I'm trying to use this but I get this error when doing rake swaggerize

/Users/my-name/.rvm/gems/ruby-2.3.0/gems/activesupport-4.2.7/lib/active_support/dependencies.rb:274:in `require': cannot load such file -- swagger_helper (LoadError)

Everything out of the box doing rails g swagger_rails:install

just added a file spec/api/controllers/posts_controller_spec.rb

require 'swagger_helper'

describe "posts api" do

  path "/posts/{id}" do

  ...

Rails 4.2.7
ruby 2.3.0

Is there any definitive guide for this gem?
Thank you in advanced. ๐Ÿ˜„

List of all object models in UI

Hi there!

Is it possible to add a section at the bottom of the UI website with all object models used in the API?
I would be looking for something like this:

screenshot from 2017-07-25 10 48 26

Currently, one can only see the models where they are actually being used,
screenshot from 2017-07-25 11 04 10

but there is no single place with all the models. Or did I miss something?

Thanks

Issue with precompilation

I tried to deploy rswag on production and caught this issue:

Error
    at new JS_Parse_Error (/tmp/execjs20170619-19339-1oeg4ujs:2659:11936)
    at js_error (/tmp/execjs20170619-19339-1oeg4ujs:2659:12155)
    at croak (/tmp/execjs20170619-19339-1oeg4ujs:2659:20622)
    at token_error (/tmp/execjs20170619-19339-1oeg4ujs:2659:20759)
    at unexpected (/tmp/execjs20170619-19339-1oeg4ujs:2659:20847)
    at expr_atom (/tmp/execjs20170619-19339-1oeg4ujs:2659:29141)
    at maybe_unary (/tmp/execjs20170619-19339-1oeg4ujs:2659:31652)
    at expr_ops (/tmp/execjs20170619-19339-1oeg4ujs:2660:404)
    at maybe_conditional (/tmp/execjs20170619-19339-1oeg4ujs:2660:496)
    at maybe_assign (/tmp/execjs20170619-19339-1oeg4ujs:2660:939)
  (in /home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/rswag-ui-1.3.0/vendor/assets/components/swagger-ui/o2c.html)
new JS_Parse_Error ((execjs):2659:11936)
js_error ((execjs):2659:12155)
croak ((execjs):2659:20622)
token_error ((execjs):2659:20759)
unexpected ((execjs):2659:20847)
expr_atom ((execjs):2659:29141)
maybe_unary ((execjs):2659:31652)
expr_ops ((execjs):2660:404)
maybe_conditional ((execjs):2660:496)
maybe_assign ((execjs):2660:939)
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/execjs-2.6.0/lib/execjs/external_runtime.rb:39:in `exec'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/execjs-2.6.0/lib/execjs/external_runtime.rb:21:in `eval'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/execjs-2.6.0/lib/execjs/external_runtime.rb:46:in `call'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/uglifier-2.7.2/lib/uglifier.rb:212:in `run_uglifyjs'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/uglifier-2.7.2/lib/uglifier.rb:179:in `compile'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/uglifier_compressor.rb:25:in `evaluate'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/tilt-1.4.1/lib/tilt/template.rb:103:in `render'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/context.rb:197:in `block in evaluate'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/context.rb:194:in `each'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/context.rb:194:in `evaluate'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/bundled_asset.rb:25:in `initialize'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/base.rb:377:in `new'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/base.rb:377:in `build_asset'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/index.rb:94:in `block in build_asset'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/caching.rb:58:in `cache_asset'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/index.rb:93:in `build_asset'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/base.rb:287:in `find_asset'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/index.rb:61:in `find_asset'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/manifest.rb:211:in `block in find_asset'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/manifest.rb:257:in `benchmark'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/manifest.rb:210:in `find_asset'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/manifest.rb:119:in `block in compile'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/manifest.rb:118:in `each'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/sprockets/manifest.rb:118:in `compile'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-rails-2.3.3/lib/sprockets/rails/task.rb:70:in `block (3 levels) in define'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-2.12.4/lib/rake/sprocketstask.rb:146:in `with_logger'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/sprockets-rails-2.3.3/lib/sprockets/rails/task.rb:69:in `block (2 levels) in define'
/home/deploy/.bundler/exposure_web/ruby/2.1.0/gems/rake-11.2.2/exe/rake:27:in `<top (required)>'
  • rails-4.2.4
  • rswag-1.3.0

How to include body prams for put api method?

I've got a test that has the following:

 require 'swagger_helper'

describe 'Port API' do

  path '/port/admin_state' do
    put 'Sets the admin state for a port' do
      security [ Token: [] ]
      produces 'application/json'
      consumes 'application/json'

      let(:api_key) { FactoryGirl.create(:api_key) }
      let(:Authorization) { "Token token=\"#{api_key.access_token}\"" }

      parameter name: :device_id,
        in: :body,
        type: 'string',
        required: true,
        description: 'Numeric device identifier'
      
      before do |example|
        @device = FactoryGirl.create(:device)
        @port = FactoryGirl.create(:port, device: @device)
        submit_request(example.metadata)
      end

      response '200', 'A port was toggled successfully' do
        let(:device_id) { @device.id }

        schema type: :object,
          properties: {
            status: { type: :string },
            error: { type: :string }
          }

        it do
          data = JSON.parse(response.body)
          p data
          expect(data.class).to be(Hash)
          expect(response.status).to eq(200)
        end
      end
    end

  end
end

For my get operations, all of the parameters have been in the query string. For this particular method, I'm looking to use body parameters in the request. I see when executing code against the endpoint, that the params object does indeed get populated with the params I've sent in the body. However, when testing with rswag, I don't see those parameters submitted in the request. Furthermore, I don't see anything related to my body parameters in the example.metadata object. How do I get the body params to get submitted as json to the endpoint?

Dependency Issue

rswag depends on rswag-specs
rswag-specs requires ''rspec/core' without defining the dependency in gemspec
rswag seems to inject itself into default Rakefile
when run in non test enviroment where rspec-core is not present this causes an error

Step 10/11 : RUN RAILS_ENV=docker bundle exec rake assets:precompile
 ---> Running in 7e0706635cc5
rake aborted!
LoadError: cannot load such file -- rspec/core
/var/www/org-api/current/vendor/bundle/ruby/2.4.0/gems/activesupport-5.1.2/lib/active_support/dependencies.rb:292:in `require'
/var/www/org-api/current/vendor/bundle/ruby/2.4.0/gems/activesupport-5.1.2/lib/active_support/dependencies.rb:292:in `block in require'
/var/www/org-api/current/vendor/bundle/ruby/2.4.0/gems/activesupport-5.1.2/lib/active_support/dependencies.rb:258:in `load_dependency'
/var/www/org-api/current/vendor/bundle/ruby/2.4.0/gems/activesupport-5.1.2/lib/active_support/dependencies.rb:292:in `require'
/var/www/org-api/current/vendor/bundle/ruby/2.4.0/gems/rswag-specs-1.4.0/lib/rswag/specs.rb:1:in `<top (required)>'
/var/www/org-api/current/vendor/bundle/ruby/2.4.0/gems/activesupport-5.1.2/lib/active_support/dependencies.rb:292:in `require'
/var/www/org-api/current/vendor/bundle/ruby/2.4.0/gems/activesupport-5.1.2/lib/active_support/dependencies.rb:292:in `block in require'
/var/www/org-api/current/vendor/bundle/ruby/2.4.0/gems/activesupport-5.1.2/lib/active_support/dependencies.rb:258:in `load_dependency'
/var/www/org-api/current/vendor/bundle/ruby/2.4.0/gems/activesupport-5.1.2/lib/active_support/dependencies.rb:292:in `require'
/var/www/org-api/current/vendor/bundle/ruby/2.4.0/gems/rswag-1.4.0/lib/rswag.rb:1:in `<top (required)>'
/var/lib/gems/2.4.0/gems/bundler-1.15.1/lib/bundler/runtime.rb:82:in `require'
/var/lib/gems/2.4.0/gems/bundler-1.15.1/lib/bundler/runtime.rb:82:in `block (2 levels) in require'
/var/lib/gems/2.4.0/gems/bundler-1.15.1/lib/bundler/runtime.rb:77:in `each'
/var/lib/gems/2.4.0/gems/bundler-1.15.1/lib/bundler/runtime.rb:77:in `block in require'
/var/lib/gems/2.4.0/gems/bundler-1.15.1/lib/bundler/runtime.rb:66:in `each'
/var/lib/gems/2.4.0/gems/bundler-1.15.1/lib/bundler/runtime.rb:66:in `require'
/var/lib/gems/2.4.0/gems/bundler-1.15.1/lib/bundler.rb:108:in `require'
/var/www/org-api/current/config/application.rb:13:in `<top (required)>'
/var/www/org-api/current/Rakefile:4:in `require_relative'
/var/www/org-api/current/Rakefile:4:in `<top (required)>'
/var/www/org-api/current/vendor/bundle/ruby/2.4.0/gems/rake-12.0.0/exe/rake:27:in `<top (required)>'
(See full trace by running task with --trace)

Schema: How to represent 'string' or 'null'

In JSONSchema, you can represent that an object as a certain type, such as 'string' as well as 'null'.

Example:
"image-url": { "type" : ["string", "null"] }

How can I do this in rswag schema definitions?

Try it out

Is there a way to disable to try it out feature?

Malformed params creation on POST when params added to body

Hello,

Thanks a lot for that gem, it's great! I have a small issue however, and cannot figure out what is going on; I have the following spec code to test a sessions controller:

  path '/api/sign_in' do
    post 'Sign In' do
      tags 'Sessions'
      produces 'application/json'
      parameter name: :credentials, in: :body, description: 'The credentials of the user', schema: {
        type: :object,
        properties: {
          email: { type: :string },
          password: { type: :string }
        },
        required: ['email', 'password']
      }
      response '200', 'User successfully logged in' do
        let(:user) { create(:user, password: 'password') }
        let(:credentials) { { email: user.email, password: 'password' } }
        run_test!
      end
    end
  end

it works as expected, however the params arrive to my app in this form:

<ActionController::Parameters {"{\"email\":\"[email protected]\",\"password\":\"password\"}"=>nil, "format"=>:json, "controller"=>"api/v1/sessions", "action"=>"create"} permitted: false>

As you can see, I end up with params in a weird form, i.e

{\"email\":\"america.braun@hanewisoky.name\",\"password\":\"password\"}"=>nil 

which makes me unable to extract them from the params to perform my sign up mechanism - what is weird is that with the generated documentation, when using the "try it" feature, the params are actually sent properly. Would you have an idea about how I could get this sorted? Thanks a lot in advance!!

This is how the params are formed when I use the "try it" feature:

<ActionController::Parameters {"email"=>"string", "password"=>"string", "format"=>:json, "controller"=>"api/v1/sessions", "action"=>"create", "session"=>{"email"=>"string", "password"=>"string"}} permitted: false>

As you can see, it's all hunky dory in this case :\

Per-response RSpec metadata

Is it possible to define per-response RSpec metadata, e.g. for using VCR? It's possible by wrapping each response into its own describe block, but that creates an unnecessary level of nesting:

require 'swagger_helper'

RSpec.describe 'My API' do
  path '/v1/{x}' do
    get 'Resolve an X' do
      tags 'X'
      produces 'application/json'
      parameter name: :x, in: :path, schema: {
        type: :string
      }

      describe "Success", vcr: { cassette_name: "rswag_resolve_x_success" } do
        response '200', 'Successful result' do
          schema type: :object,
                 properties: {
                   x: { type: :string }
                 }
          let(:x) { "xxx" }

          run_test!
        end
      end

      describe "Fails", vcr: { cassette_name: "rswag_resolve_x_all_fails" } do
        response '503', 'Service unavailable' do
          let(:x) { "xxx" }
          run_test!
        end
      end
    end
  end
end

Question: multiple response for multiple parameters

I have the following spec test:

require 'swagger_helper'

describe 'Devices API' do

  path '/api/v1/devices' do

    get 'Retrieves devices, optionally mating criteria' do
      security [ Token: [] ]
      produces 'application/json'

      let(:api_key) { FactoryGirl.create(:api_key) }
      let(:Authorization) { "Token token=\"#{api_key.access_token}\"" }

      before do |example|
        loc = FactoryGirl.create(:location, name: 'Earth')
        FactoryGirl.create(:device, location: loc)
        FactoryGirl.create(:device, location: loc)
        FactoryGirl.create(:device, location: loc)
        submit_request(example.metadata)
      end

      response '200', 'Returns all Earth devices' do
        parameter name: :location, :in => :query, :type => :string
        let(:location) { 'Earth' }

        it 'returns a valid 201 response' do |example|
          assert_response_matches_metadata(example.metadata)
        end

        it 'returns an array with 3 entries' do |example|
          data = JSON.parse(response.body)
          p data
          expect(data.class).to be(Array)
          expect(data.size).to eq(3)
        end
      end

      response '200', 'no devices found' do
        parameter name: :role, :in => :query, :type => :string
        let(:role) { 'DoesNotExist' }

        before do |example|
          submit_request(example.metadata)
        end

        examples 'application/json' => {}

        it 'returns an array with 0 entries' do |example|
          data = JSON.parse(response.body)
          expect(data.class).to be(Array)
          expect(data.size).to eq(0)
        end

      end
    end
  end

end

I'm failing to understand how parameters work in this case. My api method call takes three optional parameters that I would like to test independently, but in each response block, if I add a parameter to it, that parameter seems to be required in all other response blocks. So if a parameter shows up in any response block, its appended to the call for every response block., which is not what I want.

I'm looking for a way that I can independently test a parameter in isolation, but have all parameters noted in the API docs (and not duplicated). If anyone has advice on the subject, I would appreciate it.

Error on specs with multiple responses with same return code defined

Hi,

while testing APIs it can happen you'll have the same HTTP response code for different reasons, like for example running the following spec:

require 'swagger_helper'

describe 'Test my API'

  path '/api/document' do

    post 'Creates a new document' do
      
      parameter name: :document, in: :body, required: true, schema: {
        title:  {  type: :string  },
        text:  {  type: :string  },
        required: ['title','text']
      }

      response '200', 'Login OK' do        
        let!(:document) {
          {
          	"title" => "My document",
            "text" => "Lorem ipsum dolor"            
          }
        }
        run_test!
      end

      response '422', 'No title provided' do
        let!(:document) {
          {          
            "text" => "Lorem ipsum dolor"            
          }
        }
        run_test!
      end

      response '422', 'No text provided' do
        let!(:document) {
          {          
            "title" => "My document"
          }
        }
        run_test!
      end                              

    end

  end

end

yields the following error:

<ruby_root>.rvm/gems/ruby-2.2.2/gems/rswag-specs-1.2.1/lib/rswag/specs/example_group_helpers.rb:50:in `response': wrong number of arguments (1 for 2) (ArgumentError)
	from <ruby_root>workspaces/mobile_server/spec/integration/social_login_spec.rb:80:in `block (3 levels) in <top (required)>'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `module_exec'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `subclass'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:258:in `block in define_example_group_method'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rswag-specs-1.2.1/lib/rswag/specs/example_group_helpers.rb:13:in `block (2 levels) in <module:ExampleGroupHelpers>'
	from <ruby_root>workspaces/mobile_server/spec/integration/social_login_spec.rb:40:in `block (2 levels) in <top (required)>'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `module_exec'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `subclass'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:258:in `block in define_example_group_method'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rswag-specs-1.2.1/lib/rswag/specs/example_group_helpers.rb:7:in `path'
	from <ruby_root>workspaces/mobile_server/spec/integration/social_login_spec.rb:38:in `block in <top (required)>'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `module_exec'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `subclass'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:258:in `block in define_example_group_method'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/dsl.rb:43:in `block in expose_example_group_alias'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/dsl.rb:84:in `block (2 levels) in expose_example_group_alias_globally'
	from <ruby_root>workspaces/mobile_server/spec/integration/social_login_spec.rb:36:in `<top (required)>'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1435:in `load'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1435:in `block in load_spec_files'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1433:in `each'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1433:in `load_spec_files'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:100:in `setup'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:86:in `run'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:71:in `run'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:45:in `invoke'
	from <ruby_root>.rvm/gems/ruby-2.2.2/gems/rspec-core-3.5.4/exe/rspec:4:in `<top (required)>'
	from <ruby_root>.rvm/gems/ruby-2.2.2/bin/rspec:23:in `load'
	from <ruby_root>.rvm/gems/ruby-2.2.2/bin/rspec:23:in `<main>'
	from <ruby_root>.rvm/gems/ruby-2.2.2/bin/ruby_executable_hooks:15:in `eval'
	from <ruby_root>.rvm/gems/ruby-2.2.2/bin/ruby_executable_hooks:15:in `<main>'

I'm using version 1.2.1 and would love to keep generating swagger files with rswag!

If this can help, version 1.2.0 was not throwing exception, but the generated swagger only had one section per response with the same HTTP code (the last defined one).

fail to build_path when specifiying swagger_doc

Hello

I have

->swagger $ ls **/* 
mobile/swagger.json v1/swagger.json

mobile:
swagger.json

v1:
swagger.json
  config.swagger_docs = {
    'v1/swagger.json' => {
      swagger: '2.0',
      info: {
        title: 'API V1',
        version: 'v1'
      }
    },
    'mobile/swagger.json' => {
      swagger: '2.0',
      info: {
        title: 'mobile API V1',
        version: 'v1'
      }
    }
  }
describe 'store mobile API', swagger_doc: 'mobile/swagger.json' do

But it returns

     Failure/Error: test_visitor.submit_request!(self, example.metadata)
     NoMethodError:
       undefined method `[]' for nil:NilClass
     # /Users/bti/.rvm/gems/ruby-2.3.0@appaloosa/gems/swagger_rails-1.0.0.pre.beta2/lib/swagger_rails/test_visitor.rb:39:in `block in build_path'
     # /Users/bti/.rvm/gems/ruby-2.3.0@appaloosa/gems/swagger_rails-1.0.0.pre.beta2/lib/swagger_rails/test_visitor.rb:35:in `tap'
     # /Users/bti/.rvm/gems/ruby-2.3.0@appaloosa/gems/swagger_rails-1.0.0.pre.beta2/lib/swagger_rails/test_visitor.rb:35:in `build_path'
     # /Users/bti/.rvm/gems/ruby-2.3.0@appaloosa/gems/swagger_rails-1.0.0.pre.beta2/lib/swagger_rails/test_visitor.rb:11:in `submit_request!'
     # /Users/bti/.rvm/gems/ruby-2.3.0@appaloosa/gems/swagger_rails-1.0.0.pre.beta2/lib/swagger_rails/rspec/dsl.rb:61:in `block in run_test!'
     # ./spec/spec_helper.rb:30:in `block (3 levels) in <top (required)>'

It seems that SwaggerRails.config load a default but not the one set in swagger_helper.rb.

[1] pry(#<Class>)> SwaggerRails.config
=> #<SwaggerRails::Configuration:0x007fbfdc2bed98
 @swagger_dir_string="/Users/bti/code/appaloosa/swagger",
 @swagger_docs={"v1/swagger.json"=>{:swagger=>"2.0", :info=>{:title=>"API V1", :version=>"v1"}}}>

I had to add manually swagger_helper.rb at the beginning maybe it's part of the problem. I keep digging.

Can't use query params

Hello

When writting test like :

  path '/mobile/shops/{shop_id}/users/{user_id}/shops?key={key}&per={per}&page={page}' do
    get 'user shops' do
      operation_description 'Get user\'s shops list for shop switcher'
      consumes 'application/json'
      produces 'application/json'
      parameter :shop_id, in: :path, type: :string, required: true
      parameter :user_id, in: :path, type: :string, required: true
      parameter :key, in: :path, type: :string, required: true
      parameter :per, in: :path, type: :string, required: false
      parameter :page, in: :path, type: :string, required: false

The test to call

Started GET "/mobile/shops/1/users/1/shops?&headers[ACCEPT]=application%2Fjson&headers[CONTENT_TYPE]=application%2Fjson&key=gkoq9ucxswu21yaxb1iehrsx8z5bm88eopa2skzpxa601sz1h4&per=2&page=1" for 127.0.0.1 at 2016-09-26 09:46:47 +0200
Processing by Mobile::shopsController#user_shops as HTML
  Parameters: {"headers"=>{"ACCEPT"=>"application/json", "CONTENT_TYPE"=>"application/json"}, "key"=>"gkoq9ucxswu21yaxb1iehrsx8z5bm88eopa2skzpxa601sz1h4", "per"=>"2", "page"=>"1", "shop_id"=>"1", "user_id"=>"1"}

If I turn key, per and page as "query" I get

Started GET "/mobile/shops/1/users/1/shops?params[key]=d3q1feao22rrcswsu4qre5m6m51r6wlfb9nio4q21ytzr2mdkd&params[per]=2&params[page]=1&headers[ACCEPT]=application%2Fjson&headers[CONTENT_TYPE]=application%2Fjson&key=%7Bkey%7D&per=%7Bper%7D&page=%7Bpage%7D" for 127.0.0.1 at 2016-09-26 10:01:02 +0200
Processing by Mobile::shopsController#user_shops as HTML
  Parameters: {"params"=>{"key"=>"d3q1feao22rrcswsu4qre5m6m51r6wlfb9nio4q21ytzr2mdkd", "per"=>"2", "page"=>"1"}, "headers"=>{"ACCEPT"=>"application/json", "CONTENT_TYPE"=>"application/json"}, "key"=>"{key}", "per"=>"{per}", "page"=>"{page}", "shop_id"=>"1", "user_id"=>"1"}

Parameters => params? Is a normal behavior?

how make upload request

I tried this code to document an upload via put method, but it simply breaks. am I doing something wrong?

     put 'update a puzzle details' do
      consumes: 'multipart/form-data'
      parameter name: 'Authorization', in: :header, type: :string, required: true
      parameter name: 'Content-Type', in: :header, type: :string, required: true
      parameter name: 'puzzle_id', in: :path, type: :string, required: true
      parameter name: 'puzzle[kind]', in: :formData, type: :string, enum: %w(freezed motioning), required: false
      parameter name: 'puzzle[picture]', in: :formData, type: :file, required: false
      response 200, 'successful' do
        let(:puzzle) { create :puzzle }
        let(:Authorization) { "Bearer #{get_token(puzzle.user)}"}
        let(:'Content-Type') { 'multipart/form-data' }
        let(:puzzle_id) { puzzle.id }
        let(:'puzzle[kind]') { 'freezed' }
        let(:'puzzle[picture]') { uploaded_file }
        run_test!
      end
    end

Don't require sprockets

On applications generated using Rails 5's API mode (rails new backend --api) swagger_rails' generator blows up:

% rails g --help
/Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-5.0.0/lib/rails/railtie/configuration.rb:95:in `method_missing': undefined method `assets' for #<Rails::Application::Configuration:0x007faa62ed0940> (NoMethodError)
Did you mean?  asset_host
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/swagger_rails-5003b7ccd9ff/lib/swagger_rails/engine.rb:10:in `block in <class:Engine>'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-5.0.0/lib/rails/initializable.rb:30:in `instance_exec'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-5.0.0/lib/rails/initializable.rb:30:in `run'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-5.0.0/lib/rails/initializable.rb:55:in `block in run_initializers'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/2.3.0/tsort.rb:228:in `block in tsort_each'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/2.3.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/2.3.0/tsort.rb:431:in `each_strongly_connected_component_from'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/2.3.0/tsort.rb:349:in `block in each_strongly_connected_component'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/2.3.0/tsort.rb:347:in `each'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/2.3.0/tsort.rb:347:in `call'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/2.3.0/tsort.rb:347:in `each_strongly_connected_component'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/2.3.0/tsort.rb:226:in `tsort_each'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/2.3.0/tsort.rb:205:in `tsort_each'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-5.0.0/lib/rails/initializable.rb:54:in `run_initializers'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-5.0.0/lib/rails/application.rb:352:in `initialize!'
    from /Users/andrew/projects/accounting_integration/config/environment.rb:5:in `<top (required)>'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-5.0.0/lib/rails/application.rb:328:in `require'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-5.0.0/lib/rails/application.rb:328:in `require_environment!'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-5.0.0/lib/rails/commands/commands_tasks.rb:157:in `require_application_and_environment!'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-5.0.0/lib/rails/commands/commands_tasks.rb:143:in `generate_or_destroy'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-5.0.0/lib/rails/commands/commands_tasks.rb:60:in `generate'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-5.0.0/lib/rails/commands/commands_tasks.rb:49:in `run_command!'
    from /Users/andrew/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-5.0.0/lib/rails/commands.rb:18:in `<top (required)>'
    from bin/rails:4:in `require'
    from bin/rails:4:in `<main>'

Uncommenting the require "sprockets/railtie" in config/application.rb got past the error.

It would be nice to either document the requirement on Sprockets or just test if the module is enabled before trying to add the path. I think it makes the most sense to make it optional since there's still quite a bit of value to just having the Rails app generate the JSON and consuming it with a copy of swagger-ui that's hosted elsewhere.

This issue was forked from comments on #3

Question: what is the purpose of schema?

I'm seeing the schema in swagger, as well as used in the rswag gem, but I don't understand its purpose. Is it to test the results of a call? When is it required? Is it ever advisable to leave it off?

Problem with "rails g swagger_rails:install"

When I try to run this command, I get this error: "NoMethodError: undefined method `to_time_preserves_timezone=' for ActiveSupport:Module". I'm using Aptana Studio v 3.2.0, and Rails 4.2.7.

Creating tables

Having spent quite a lot of time figuring out how to make tables display in the UI, I'd like to share my solution to hopefully spare some time for someone in the future.

The normal markdown table syntax looks as follows:

| Column1 | Collumn2 |
| ------- | -------- |
| cell1   | cell2    | 

However, in RSwag this displays in quite an interesting fashion:
screenshot from 2017-07-28 10 49 38

In order to get the "real" table output:
screenshot from 2017-07-28 11 52 57

One has to use this syntax:

&#13;
| Column1 | Collumn2 |&#13;
| ------- | -------- |&#13;
| cell1   | cell2    |&#13;
&#13;

Making sure there are no white spaces in front of any lines of the code above.

I hope I was able to help someone!

$ref makes other parameters be ignored

the definition parameters seems to be ignored when I declare them with $ref
it may be because of this implementation
example:

{
  "definitions": {
    "musical_instrument": {
      "type": "object",
      "properties": {
        "kind": { "type": "string" }
      }
    },
    "player": {
      "type": "object",
      "properties": {
        "name": { "type": "string" },
        "instrument": {
          "$ref": "#/definitions/musical_instrument",
          "x-nullable": true
        }
      }
    }
  }
}

then, x-nullable is ignored
I expect to merge these parameters with $ref
if this is specification of swagger, sorry for my misunderstanding

Assigning *_spec.rb files to versions of API

Hi everyone,

The API I am documenting is made of 3 versions, each of them varying in the methods they provide and schema of objects they output. I would like to document this with Rswag but I am stuck :/

I followed the instructions in ReadMe regarding adding multiple versions of API (updating rswag_ui.rb file and swagger_helper.rb). At this point, swaggerizer parses everything without issues and in the UI I can switch between versions.

I want to create different *_spec.rb files for each endpoint in each API version. The problem is that ReadMe does not specify how does swaggerizer know which version should a given _spec.rb file be associated with. I tried putting *_spec.rb files in different folders - /api/v1, /api/v2 etc however it still does not work.

Could you please advise me how can this be done??

Thank you in advance

Unable to use apiKey pairs in requests

As per: https://swagger.io/docs/specification/authentication/api-keys, It should be possible to use multiple apiKey entries together simultaneously. Ie: App Key + API Key.
However in testing we've discovered rswag's spec helpers only add the first entry of type apiKey to the headers / query.

Here is our securityDefinitions / security section showing an expected working use case:

  "securityDefinitions": {
      username: {
          type: :apiKey,
          name: "X-Username",
          in: :header
      },
      key: {
          type: :apiKey,
          name: "X-ApiKey",
          in: :header
      }
  },
  "security": [
      {
          "username": [],
          "key": []
      }
  ],

The swagger.json file is generated correctly. It's only affects the rspec tests.

I've added some tests and fixed the issue and will put in a pull request shortly.

Fail to load swagger-ui assets

Hello @domaindrivendev

First thank you soooo much for the rewrite. I've edited my old request specs and it's work perfectly with query params. The json generated doesn't contain any errors when testing with http://editor.swagger.io/#/.

I have only one issue with swagger-ui.

When trying to go to /api-docs it gets lot's of missing assets like:

ActionController::RoutingError (No route matches [GET] "/assets/swagger-ui/images/favicon-32x32.png")

Page looks like :
capture d ecran 2016-10-12 a 11 25 38

also

$ rails g rswag:ui:custom_ui                                                                             [11:26:20]
Running via Spring preloader in process 19510
Could not find generator 'rswag:ui:custom_ui'. Maybe you meant 'rswag:ui:custom', 'rswag:ui:install' or 'rswag:install'
Run `rails generate --help` for more options.

Description for tags and summary missing from UI

Hi everyone,

In my API docs, I'd like to have a short intro/description of each tag and each method for the users to quickly understand what the sections and methods are responsible for. Swagger supports both features description of tags (https://swagger.io/specification/#tag-object-71), summary for an endpoint (https://swagger.io/specification/#operation-object-36). In the case of tags, Rswag nicely generates something along these lines to swagger.json:

"tags" : { "Users" : { "description" : "These actions relate to user objects" }, "Articles" : { "description" : "These actions relate to articles" } }
But this never shows up in the UI.

However, Swaggerizer seems not to recognize the keyword 'summary'. The following code:

post 'Creates a blog' do tags 'Blogs' consumes 'application/json', 'application/xml' description 'This section displays nicely in the UI' summary 'Swaggerizer does not like this keyword though' parameter name: :blog, in: :body, schema: {

results in the following error:

block (4 levels) in <top (required)>': undefined method summary' for #Class:0x0000000d32c078 (NoMethodError)`

Could you advise me whether I'm doing something wrong or whether Rswag and it's version of UI do not have such features.

Thanks in advance!

Can't use "operation_description"

Hello

Thanks a lot for swagger_rails. It's very great to use.

I have an issue when trying to use operation_description :

get 'user stores' do
      operation_description 'Detailed implementation notes for the create a new blog API endpoint'
      consumes 'application/json'
/Users/bti/.rvm/gems/ruby-2.3.0@appaloosa/gems/rspec-core-3.3.2/lib/rspec/core/example_group.rb:656:in `method_missing': undefined method `operation_description' for #<Class:0x007fdf446a29f0> (NoMethodError)
    from /Users/bti/code/appaloosa/spec/requests/mobile/stores_controller_spec.rb:21:in `block (3 levels) in <top (required)>'
    from /Users/bti/.rvm/gems/ruby-2.3.0@appaloosa/gems/rspec-core-3.3.2/lib/rspec/core/example_group.rb:363:in `module_exec'
    from /Users/bti/.rvm/gems/ruby-2.3.0@appaloosa/gems/rspec-core-3.3.2/lib/rspec/core/example_group.rb:363:in `subclass'
    from /Users/bti/.rvm/gems/ruby-2.3.0@appaloosa/gems/rspec-core-3.3.2/lib/rspec/core/example_group.rb:254:in `block in define_example_group_method'
    from /Users/bti/.rvm/gems/ruby-2.3.0@appaloosa/gems/swagger_rails-1.0.0.pre.beta2/lib/swagger_rails/rspec/dsl.rb:20:in `operation'
    from /Users/bti/.rvm/gems/ruby-2.3.0@appaloosa/gems/swagger_rails-1.0.0.pre.beta2/lib/swagger_rails/rspec/dsl.rb:25:in `block (2 levels) in <module:DSL>'

Any idea?

Send the header values in request

not able to pass the header values in the spec, please help to send the values in the headers
example:

require 'swagger_helper'
describe 'Test API' do
  path '/api/v1/visit/{id}' do
    before { header "X-Token", 'api_key' }
     get "visiting information" do
      tags 'Page'
      produces 'application/json'
      parameter name: :id, :in => :path, :type => :string      
      # before do
      #   header "X-Token", 'api_key'
      # end
      response '200', 'success'  do
        let(:id) { 1 }
        run_test!
      end
  end
end

giving the blow error when i run the above integration spec.

header is not available from within an example (e.g. an it block) or from constructs that run in the scope of an example (e.g. before, let, etc). It is only available on an example group (e.g. a describe or context block).

Unnecessary dependency on activerecord

the rswag gem has an unnecessary transitive dependency on activerecord through rails. We are not using a relational database and thus activerecord is not in our project but now pulled in via rswag. This is not desirable. rswag should only include what's really necessary e.g. activesupport, activemodel etc.

Security definitions

It's something that's missing in the documentation, how can I use security definitions both on docs and when running the tests?

I found this method on rswag library:

def resolve_api_key_parameters
        @api_key_params ||= begin
          # First figure out the security requirement applicable to the operation
          global_requirements = (@global_metadata[:security] || [] ).map { |r| r.keys.first }
          operation_requirements = (@api_metadata[:operation][:security] || [] ).map { |r| r.keys.first }
          requirements = global_requirements | operation_requirements

          # Then obtain the scheme definitions for those requirements
          definitions = (@global_metadata[:securityDefinitions] || {}).slice(*requirements)
          definitions.values.select { |d| d[:type] == :apiKey }
        end
      end

But I'm not sure how to use it.

Fail to load swagger-ui assets

Hi @domaindrivendev,

First of all: I love rswag as it saves me a ton of typing and keeping stuff in sync. Thank for your effort and time spent on this. ๐Ÿ‘

I have one issue with loading the swagger-ui assets in production environment. They get precompiled as I see all of them having digest, but the index.html.erb links them without the digest.

Im using rails 5.0.0.1 and rswag 1.0.3
I generated my own index.html.erb file via 'rails g rswag:ui:custom' and also tried to turn the link and script tags into rails stylesheet_link_tag and javascript_include_tag. This changed nothing.

all asset configs in my environments/production.rb:
config.assets.compile = false
config.assets.js_compressor = :uglifier

Any pointers what I'm missing or an idea how to fix this?

How to Access Original Response

I'm trying to access RSpecs response to ensure the values returned are actually correct for a given requrest, but it doesn't look like I'm able to do so since it's been overridden by rswag's response.

How can I access the original response?

undefined method `path' for RSpec::ExampleGroups::...

Copying the example blog spec from the readme and installing with Ruby 2.4.1, RSpec 3.6.0 and RSwag 1.3.0 gives me the following error...

$ rake rswag:specs:swaggerize
/Users/nick/.rbenv/versions/2.4.1/bin/ruby -I/Users/nick/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/rspec-core-3.6.0/lib:/Users/nick/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/rspec-support-3.6.0/lib /Users/nick/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/rspec-core-3.6.0/exe/rspec --pattern spec/requests/\*\*/\*_spec.rb,\ spec/api/\*\*/\*_spec.rb,\ spec/integration/\*\*/\*_spec.rb --format Rswag::Specs::SwaggerFormatter --dry-run --order defined
Generating Swagger docs ...

An error occurred while loading ./spec/integration/metadata_api_spec.rb.
Failure/Error:
    path '/blogs' do
  
      post 'Creates a blog' do
        tags 'Blogs'
        consumes 'application/json', 'application/xml'
        parameter name: :blog, in: :body, schema: {
          type: :object,
          properties: {
            title: { type: :string },
            content: { type: :string }

NoMethodError:
  undefined method `path' for RSpec::ExampleGroups::BlogsAPI:Class
# ./spec/integration/metadata_api_spec.rb:5:in `block in <top (required)>'
# ./spec/integration/metadata_api_spec.rb:3:in `<top (required)>'
Swagger doc generated at /Users/nick/Documents/.../swagger.json

The custom methods from RSwag don't seem to be present in my spec's context, even though require 'swagger_helper' is present and the file has been generated.

Our rails_helper has some custom RSpec includes, but nothing which should exclude RSwag...

RSpec.configure do |config|
  config.include ....

Anyone seen this behaviour before? Thanks!

Authorization Headers

I'm running into an issue between rswag integration tests & adding swagger securityDefinitions. I can either have rswag integration tests work OR Swagger UI docs work.

My controllers all required an authorization header. I've manually added those as params in each integration test:

parameter({
  :in => :header,
  :type => :string,
  :name => :Authorization,
  :required => true,
  :description => 'Client token'
})
...
let(:Authorization) { 'Bearer ' + generate_token }

run_test!

specs pass!

When I move to the security helper:

security [ { :client => [] } ]
...
let(:Authorization) { 'Bearer ' + generate_token }

The Authorization header is not added added for the integration spec and the spec fails.

I've attached an example:

This passes

RSpec.describe 'Session' do
  path '/sessions' do
    post 'create' do
      tags 'Session'

      consumes 'application/json'
      produces 'application/json'

      # here
      parameter({
        :in => :header,
        :type => :string,
        :name => :Authorization,
        :required => true,
        :description => 'Client token'
      })

      response '201', 'created' do
        # this header is included
        let(:Authorization) { 'Bearer ' + generate_token }

        schema({ '$ref' => '#/definitions/responses/session' })

        run_test!
      end
    end
  end
end

this fails

config.swagger_docs = {
  'v1/swagger.json' => {
    :swagger => '2.0',
    :info => {
      :title => 'My Api',
      :version => 'v1',
      :description => 'our docs'
    },
    :schemes => [ 'http' ],
    :host => 'localhost:3000',
    :basePath => '/v1/',
    :definitions => [],
    :securityDefinitions => {
      # here
      :client => {
        :in => :header,
        :name => :Authorization,
        :type => 'oauth2',
        :require => true,
        :description => 'Client Token',
        :tokenUrl => 'http://localhost:5000/auth/token',
        :flow => 'application',
        :scopes => { }
      }
    }
  }
}

RSpec.describe 'Session' do
  path '/sessions' do
    post 'create' do
      tags 'Session'

      # here
      security [ { :client => [] } ]

      consumes 'application/json'
      produces 'application/json'

      response '201', 'created' do
        # this header won't be included
        let(:Authorization) { 'Bearer ' + generate_token }

        schema({ '$ref' => '#/definitions/responses/session' })

        run_test!
      end
    end
  end
end

Swagger UI works great with the security helper & defined securityDefinitions. But specs won't pass. When I configure rswag for specs to pass, swagger UI doesn't auto fill the Authorization header. When I configure rswag for swagger-ui to work, rswag specs don't include the Authorization header & specs fail.

In this clip, specs are passing, I'm using the security helper, I've manually defined the Authorization header, & I've defined securityDefinitions. (I'm pretty sure I'm not supposed to do all of those). But it's the only way I can get specs to pass & get swagger docs to work --- kinda.

Since I've manually defined the Authorization header, I have to add a value to it. Even though swagger UI will do that globally.

swagger2

Endpoint response not showing in swagger definition

Thanks for the great work! This thing is amazing.

Please correct me if i'm wrong but the endpoint response is not currently saved in the swagger definition file right?

get 'Retreive endpoint specified by id' do
  consumes 'application/vnd.api+json'
  produces 'application/vnd.api+json'
  parameter :id, in: :path, type: :string, required: true, description: "The endpoint id"

  response '200', 'success' do
    let(:endpoint) { Endpoint.create! }
    let(:id) { endpoint.id }

    run_test!
  end
end

produces

"paths": {
  "/api/v1/endpoints/{id}": {
    "get": {
      "tags": [
        "Endpoints API"
      ],
      "summary": "Retreive endpoint specified by id",
      "consumes": [
        "application/vnd.api+json"
      ],
      "produces": [
        "application/vnd.api+json"
      ],
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "type": "string",
          "required": true,
          "description": "The endpoint id"
        }
      ],
      "responses": {
        "200": {
          "description": "success"
        }
      }
    }
  }
}

Any chance to save the response payload as well? Thanks

swagger_rails generate swagger file with errors

Hello

I have a test that return me this json:

{
  "swagger": "2.0",
  "info": {
    "title": "mobile API V1",
    "version": "v1"
  },
  "paths": {
    "/mobile/shops/{shop_id}/users/{user_id}/shops?_token={_token}&per={per}&page={page}": {
      "get": {
        "tags": [
          "shop mobile API"
        ],
        "summary": "user shops",
        "description": "Get user's shops for shop switcher",
        "consumes": [
          "application/json"
        ],
        "produces": null,
        "parameters": [
          {
            "name": "shop_id",
            "in": "path",
            "required": true
          },
          {
            "name": "user_id",
            "in": "path",
            "required": true
          },
          {
            "name": "_token",
            "in": "path",
            "required": true
          },
          {
            "name": "per",
            "in": "path",
            "required": false
          },
          {
            "name": "page",
            "in": "path",
            "required": false
          }
        ],
        "responses": {
          "200": {
            "description": "success"
          },
          "302": {
            "description": "wrong token"
          }
        }
      }
    }
  }
}

But it returns lot's of errors in http://editor.swagger.io/. Some of them I don't understand like Not a valid parameter definition. I check demo json and mine without finding all the issues even with swagger specs.

Rails 5.0 compatibility

Hey,

Thanks for awesome gem! It's working really good with rails 5.0.0.rc2!

However, there seems to be a problem with the recent 5.0 version. The gem depends on rails < 5. Bundler versioning assumes that any 5.0 release candidate is satisfying the < 5 conditional. Is there any barrier for bumping the version in Gemfile.lock to <= 5.0 ? If there are any unresolved urgent issues I may be able to help.

Cheers!

Examples Generation Fails When Body is Gzip Encoded

I'm running the master build locally and was having problems getting a simple response body to parse. Some quick troubleshooting confirmed my theory that the response body was gzip encoded when passed to the metadata handler (not sure that's the right term... sorry, ruby noob).

I've come up with a workaround for now and will probably create a helper method to simplify all this, but I thought I'd submit the issue in case you want to address it. I've added comments to the example provided in the readme as well as my hacky temp workaround.

Thanks for the work you've done -- loving the gem! :)

after do |example|
                    #puts response.header #shows response is gzip compressed
                    #puts response.body #compressed body
                    #puts ActiveSupport::Gzip.decompress(response.body) #proper JSON body
                    #puts JSON.parse(response.body, symbolize_names: true) #blank
                    #example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) } #fails silently
                    if response['Content-Type'].start_with?('application/json') && response['Content-Encoding'] == 'gzip'
                        example.metadata[:response][:examples] = { 'application/json' => JSON.parse(ActiveSupport::Gzip.decompress(response.body), symbolize_names: true) } #success
                    else
                        example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) }
                    end
                end

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.