2 Commits

Author SHA1 Message Date
Greg Myers
e130e41843 Update changelog 2019-10-18 23:25:18 +01:00
Greg Myers
5946b3c88b Merge pull request #250 from BookOfGreg/fix/swagger-generator
Point the railtie to the correct file
2019-10-18 23:22:29 +01:00
27 changed files with 51 additions and 237 deletions

View File

@@ -1,17 +0,0 @@
## Describe the bug
A clear and concise description of what the bug is.
## Steps to Test or Reproduce
Please provide an example repo or the steps to reproduce the behavior.
## Expected behavior
A clear and concise description of what you expected to happen.
## Screenshots
If applicable, add screenshots to help explain your problem.
## Additional context
Add any other context about the problem here.
## Rswag Version
The version of rswag are you using.

View File

@@ -1,11 +0,0 @@
## Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is.
## Describe the solution you'd like
A clear and concise description of what you want to happen.
## Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
## Additional context
Add any other context or screenshots about the feature request here.

View File

@@ -1,15 +0,0 @@
## Problem
A clear and concise description of what the problem is.
## Solution
A clear and concise description of what the solution is.
### Related Issues
Links to any related issues.
### Checklist
- [ ] Added tests
- [ ] Changelog updated
### Steps to Test or Reproduce
Outline the steps to test or reproduce the PR here.

View File

@@ -1 +1 @@
2.6.3
2.6.4

View File

@@ -1,11 +1,11 @@
language: ruby
dist: bionic
dist: xenial
services:
- xvfb
rvm:
- 2.6.3
- 2.6.4
env:
- RAILS_VERSION=6.0.0
@@ -19,7 +19,7 @@ addons:
cache:
directories:
- /home/travis/.rvm/gems/ruby-2.6.3
- /home/travis/.rvm/gems/ruby-2.6.4
install: ./ci/build.sh
@@ -29,7 +29,6 @@ jobs:
include:
- stage: publish components
script: 'cd rswag-api'
if: tag IS present
deploy:
gemspec: rswag-api.gemspec
provider: rubygems
@@ -40,7 +39,6 @@ jobs:
- stage: publish components
script: 'cd rswag-specs'
if: tag IS present
deploy:
gemspec: rswag-specs.gemspec
provider: rubygems
@@ -51,7 +49,6 @@ jobs:
- stage: publish components
script: 'cd rswag-ui'
if: tag IS present
deploy:
gemspec: rswag-ui.gemspec
provider: rubygems
@@ -63,7 +60,6 @@ jobs:
- stage: publish rswag
script: 'cd rswag'
if: tag IS present
deploy:
gemspec: rswag.gemspec
provider: rubygems

View File

@@ -12,12 +12,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
### Security
## [2.2.0] - 2019-11-01
### Added
- New swagger_format config option for setting YAML output [#251](https://github.com/rswag/rswag/pull/251)
### Changed
- rswag-api will serve yaml files as yaml [#251](https://github.com/rswag/rswag/pull/251)
## [2.1.1] - 2019-10-18
### Fixed
- Fix incorrect require reference for swagger_generator [#248](https://github.com/rswag/rswag/issues/248)

View File

@@ -1,11 +1,10 @@
rswag
=========
[![Build Status](https://travis-ci.org/rswag/rswag.svg?branch=master)](https://travis-ci.org/rswag/rswag)
[![Maintainability](https://api.codeclimate.com/v1/badges/1175b984edc4610f82ab/maintainability)](https://codeclimate.com/github/rswag/rswag/maintainability)
[Swagger](http://swagger.io) tooling for Rails API's. Generate beautiful API documentation, including a UI to explore and test operations, directly from your rspec integration tests.
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 automaticaly 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](https://github.com/swagger-api/swagger-ui) that's powered by the exposed file. This toolchain makes it seamless to go from integration specs, which youre probably doing in some form already, to living documentation for your API consumers.
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 automaticaly runs the tests. Once you have green tests, run a rake task to auto-generate corresponding Swagger files and expose them as JSON endpoints. Rswag also provides an embedded version of the awesome [swagger-ui](https://github.com/swagger-api/swagger-ui) that's powered by the exposed JSON. This toolchain makes it seamless to go from integration specs, which youre probably doing in some form already, to living documentation for your API consumers.
And that's not all ...
@@ -16,7 +15,7 @@ Once you have an API that can describe itself in Swagger, you've opened the trea
|Rswag Version|Swagger (OpenAPI) Spec.|swagger-ui|
|----------|----------|----------|
|[master](https://github.com/rswag/rswag/tree/master)|2.0|3.18.2|
|[2.2.0](https://github.com/rswag/rswag/tree/2.2.0)|2.0|3.18.2|
|[2.0.6](https://github.com/rswag/rswag/tree/2.0.6)|2.0|3.17.3|
|[1.6.0](https://github.com/rswag/rswag/tree/1.6.0)|2.0|2.2.5|
## Getting Started ##
@@ -27,15 +26,14 @@ Once you have an API that can describe itself in Swagger, you've opened the trea
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.
or if you like to avoid loading rspec in other bundler groups.
```ruby
# Gemfile
gem 'rswag-api'
gem 'rswag-ui'
group :development, :test do
group :test do
gem 'rspec-rails'
gem 'rswag-specs'
end
@@ -46,11 +44,11 @@ Once you have an API that can describe itself in Swagger, you've opened the trea
```ruby
rails g rswag:install
```
Or run the install generators for each package separately if you installed Rswag as separate gems, as indicated above:
```ruby
rails g rswag:api:install
rails g rswag:api:install
rails g rswag:ui:install
RAILS_ENV=test rails g rswag:specs:install
```
@@ -123,17 +121,12 @@ Once you have an API that can describe itself in Swagger, you've opened the trea
end
```
There is also a generator which can help get you started `rails generate rspec:swagger API::MyController`
4. Generate the Swagger JSON file(s)
```ruby
rake rswag:specs:swaggerize
```
This common command is also aliased as `rake rswag`.
5. Spin up your app and check out the awesome, auto-generated docs at _/api-docs_!
## The rspec DSL ##
@@ -193,7 +186,7 @@ describe 'Blogs API' do
end
end
```
*Note:* OAI v3 has a nullable property. Rswag will work to support this soon. This may have an effect on the need/use of custom extension to the draft. Do not use this property if you don't understand the implications.
*Note:* the OAI v3 may be released soon(ish?) and include a nullable property. This may have an effect on the need/use of custom extension to the draft. Do not use this property if you don't understand the implications.
<https://github.com/OAI/OpenAPI-Specification/issues/229#issuecomment-280376087>
### Global Metadata ###
@@ -216,8 +209,8 @@ RSpec.configure do |config|
basePath: '/api/v1'
},
'v2/swagger.yaml' => {
openapi: '3.0.0',
'v2/swagger.json' => {
swagger: '2.0',
info: {
title: 'API V2',
version: 'v2',
@@ -229,12 +222,12 @@ RSpec.configure do |config|
end
```
#### Supporting multiple versions of API ####
#### 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 ```swagger_doc``` tag to each spec specifying its target document name:
```ruby
# spec/integration/v2/blogs_spec.rb
describe 'Blogs API', swagger_doc: 'v2/swagger.yaml' do
describe 'Blogs API', swagger_doc: 'v2/swagger.json' do
path '/blogs' do
...
@@ -244,14 +237,14 @@ describe 'Blogs API', swagger_doc: 'v2/swagger.yaml' do
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](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) for reference.
#### 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](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) for reference.
__NOTE:__ There is one difference between the official Markdown syntax and Swagger interpretation, namely tables. To create a table like this:
| Column1 | Collumn2 |
| ------- | -------- |
| cell1 | cell2 |
| cell1 | cell2 |
you should use the folowing syntax, making sure there are no whitespaces at the start of any of the lines:
@@ -451,17 +444,6 @@ RSpec.configure do |config|
config.swagger_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 simply nullify the response metadata after the test run.
```ruby
after do |example|
example.metadata[:response] = null
end
```
### 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 it's mount prefix in _routes.rb_:

View File

@@ -1,10 +1,8 @@
require 'json'
require 'yaml'
require 'rack/mime'
module Rswag
module Api
class Middleware
class Middleware
def initialize(app, config)
@app = app
@@ -16,46 +14,24 @@ module Rswag
filename = "#{@config.resolve_swagger_root(env)}/#{path}"
if env['REQUEST_METHOD'] == 'GET' && File.file?(filename)
swagger = parse_file(filename)
swagger = load_json(filename)
@config.swagger_filter.call(swagger, env) unless @config.swagger_filter.nil?
mime = Rack::Mime.mime_type(::File.extname(path), 'text/plain')
body = unload_swagger(filename, swagger)
return [
'200',
{ 'Content-Type' => mime },
[ body ]
{ 'Content-Type' => 'application/json' },
[ JSON.dump(swagger) ]
]
end
return @app.call(env)
end
private
def parse_file(filename)
if /\.ya?ml$/ === filename
load_yaml(filename)
else
load_json(filename)
end
end
def load_yaml(filename)
YAML.safe_load(File.read(filename))
end
def load_json(filename)
JSON.parse(File.read(filename))
end
def unload_swagger(filename, swagger)
if /\.ya?ml$/ === filename
YAML.dump(swagger)
else
JSON.dump(swagger)
end
end
end
end
end

View File

@@ -1,5 +0,0 @@
swagger: '2.0'
info:
title: API V1
version: v1
paths: {}

View File

@@ -76,21 +76,6 @@ module Rswag
expect(response[2].join).to include('"host":"tempuri.org"')
end
end
context 'when a path maps to a yaml swagger file' do
let(:env) { env_defaults.merge('PATH_INFO' => 'v1/swagger.yml') }
it 'returns a 200 status' do
expect(response.length).to eql(3)
expect(response.first).to eql('200')
end
it 'returns contents of the swagger file' do
expect(response.length).to eql(3)
expect(response[1]).to include( 'Content-Type' => 'text/yaml')
expect(response[2].join).to include('title: API V1')
end
end
end
end
end

View File

@@ -13,8 +13,8 @@ RSpec.configure do |config|
# document below. You can override this behavior by adding a swagger_doc tag to the
# the root example_group in your specs, e.g. describe '...', swagger_doc: 'v2/swagger.json'
config.swagger_docs = {
'v1/swagger.yaml' => {
openapi: '3.0.1',
'v1/swagger.json' => {
swagger: '2.0',
info: {
title: 'API V1',
version: 'v1'
@@ -22,10 +22,4 @@ RSpec.configure do |config|
paths: {}
}
}
# Specify the format of the output Swagger file when running 'rswag:specs:swaggerize'.
# The swagger_docs configuration option has the filename including format in
# the key, this may want to be changed to avoid putting yaml in json files.
# Defaults to json. Accepts ':json' and ':yaml'.
config.swagger_format = :yaml
end

View File

@@ -12,7 +12,6 @@ module Rswag
c.add_setting :swagger_root
c.add_setting :swagger_docs
c.add_setting :swagger_dry_run
c.add_setting :swagger_format
c.extend ExampleGroupHelpers, type: :request
c.include ExampleHelpers, type: :request
end

View File

@@ -31,14 +31,6 @@ module Rswag
end
end
def swagger_format
@swagger_format ||= begin
@rspec_config.swagger_format = :json if @rspec_config.swagger_format.nil? || @rspec_config.swagger_format.empty?
raise ConfigurationError, "Unknown swagger_format '#{@rspec_config.swagger_format}'" unless [:json, :yaml].include?(@rspec_config.swagger_format)
@rspec_config.swagger_format
end
end
def get_swagger_doc(name)
return swagger_docs.values.first if name.nil?
raise ConfigurationError, "Unknown swagger_doc '#{name}'" unless swagger_docs[name]

View File

@@ -37,7 +37,7 @@ module Rswag
FileUtils.mkdir_p dirname unless File.exists?(dirname)
File.open(file_path, 'w') do |file|
file.write(pretty_generate(doc))
file.write(JSON.pretty_generate(doc))
end
@output.puts "Swagger doc generated at #{file_path}"
@@ -46,20 +46,6 @@ module Rswag
private
def pretty_generate(doc)
if @config.swagger_format == :yaml
clean_doc = yaml_prepare(doc)
YAML.dump(clean_doc)
else # config errors are thrown in 'def swagger_format', no throw needed here
JSON.pretty_generate(doc)
end
end
def yaml_prepare(doc)
json_doc = JSON.pretty_generate(doc)
clean_doc = JSON.parse(json_doc)
end
def metadata_to_swagger(metadata)
response_code = metadata[:response][:code]
response = metadata[:response].reject { |k,v| k == :code }

View File

@@ -16,6 +16,3 @@ namespace :rswag do
end
end
end
task :rswag => ['rswag:specs:swaggerize']

View File

@@ -4,7 +4,7 @@ require 'generators/rswag/specs/install/install_generator'
module Rswag
module Specs
RSpec.describe InstallGenerator do
describe InstallGenerator do
include GeneratorSpec::TestCase
destination File.expand_path('../tmp', __FILE__)

View File

@@ -3,12 +3,10 @@ require 'rswag/specs/configuration'
module Rswag
module Specs
RSpec.describe Configuration do
describe Configuration do
subject { described_class.new(rspec_config) }
let(:rspec_config) do
OpenStruct.new(swagger_root: swagger_root, swagger_docs: swagger_docs, swagger_format: swagger_format)
end
let(:rspec_config) { OpenStruct.new(swagger_root: swagger_root, swagger_docs: swagger_docs) }
let(:swagger_root) { 'foobar' }
let(:swagger_docs) do
{
@@ -16,7 +14,6 @@ module Rswag
'v2/swagger.json' => { info: { title: 'v2' } }
}
end
let(:swagger_format) { :yaml }
describe '#swagger_root' do
let(:response) { subject.swagger_root }
@@ -49,26 +46,6 @@ module Rswag
end
end
describe '#swagger_format' do
let(:response) { subject.swagger_format }
context 'provided in rspec config' do
it { expect(response).to be_an_instance_of(Symbol) }
end
context 'unsupported format provided' do
let(:swagger_format) { :xml }
it { expect { response }.to raise_error ConfigurationError }
end
context 'not provided' do
let(:swagger_format) { nil }
it { expect(response).to eq(:json) }
end
end
describe '#get_swagger_doc(tag=nil)' do
let(:swagger_doc) { subject.get_swagger_doc(tag) }

View File

@@ -3,7 +3,7 @@ require 'rswag/specs/example_group_helpers'
module Rswag
module Specs
RSpec.describe ExampleGroupHelpers do
describe ExampleGroupHelpers do
subject { double('example_group') }
before do

View File

@@ -3,7 +3,7 @@ require 'rswag/specs/example_helpers'
module Rswag
module Specs
RSpec.describe ExampleHelpers do
describe ExampleHelpers do
subject { double('example') }
before do
@@ -12,7 +12,7 @@ module Rswag
allow(config).to receive(:get_swagger_doc).and_return(swagger_doc)
stub_const('Rswag::Specs::RAILS_VERSION', 3)
end
let(:config) { double('config') }
let(:config) { double('config') }
let(:swagger_doc) do
{
securityDefinitions: {

View File

@@ -3,13 +3,13 @@ require 'rswag/specs/request_factory'
module Rswag
module Specs
RSpec.describe RequestFactory do
describe RequestFactory do
subject { RequestFactory.new(config) }
before do
allow(config).to receive(:get_swagger_doc).and_return(swagger_doc)
end
let(:config) { double('config') }
let(:config) { double('config') }
let(:swagger_doc) { {} }
let(:example) { double('example') }
let(:metadata) do

View File

@@ -3,13 +3,13 @@ require 'rswag/specs/response_validator'
module Rswag
module Specs
RSpec.describe ResponseValidator do
describe ResponseValidator do
subject { ResponseValidator.new(config) }
before do
allow(config).to receive(:get_swagger_doc).and_return(swagger_doc)
end
let(:config) { double('config') }
let(:config) { double('config') }
let(:swagger_doc) { {} }
let(:example) { double('example') }
let(:metadata) do

View File

@@ -3,8 +3,8 @@ require 'ostruct'
module Rswag
module Specs
RSpec.describe SwaggerFormatter do
describe SwaggerFormatter do
subject { described_class.new(output, config) }
# Mock out some infrastructure
@@ -47,36 +47,20 @@ module Rswag
end
describe '#stop' do
before do
before do
FileUtils.rm_r(swagger_root) if File.exists?(swagger_root)
allow(config).to receive(:swagger_docs).and_return(
'v1/swagger.json' => { info: { version: 'v1' } },
'v2/swagger.json' => { info: { version: 'v2' } }
)
allow(config).to receive(:swagger_format).and_return(swagger_format)
subject.stop(notification)
end
let(:notification) { double('notification') }
context 'with default format' do
let(:swagger_format) { :json }
it 'writes the swagger_doc(s) to file' do
expect(File).to exist("#{swagger_root}/v1/swagger.json")
expect(File).to exist("#{swagger_root}/v2/swagger.json")
expect { JSON.parse(File.read("#{swagger_root}/v2/swagger.json")) }.not_to raise_error
end
end
context 'with yaml format' do
let(:swagger_format) { :yaml }
it 'writes the swagger_doc(s) as yaml' do
expect(File).to exist("#{swagger_root}/v1/swagger.json")
expect { JSON.parse(File.read("#{swagger_root}/v1/swagger.json")) }.to raise_error(JSON::ParserError)
# Psych::DisallowedClass would be raised if we do not pre-process ruby symbols
expect { YAML.safe_load(File.read("#{swagger_root}/v1/swagger.json")) }.not_to raise_error
end
it 'writes the swagger_doc(s) to file' do
expect(File).to exist("#{swagger_root}/v1/swagger.json")
expect(File).to exist("#{swagger_root}/v2/swagger.json")
end
after do

View File

@@ -2,9 +2,9 @@ Rswag::Ui.configure do |c|
# List the Swagger endpoints that you want to be documented through the swagger-ui
# The first parameter is the path (absolute or relative to the UI host) to the corresponding
# endpoint and the second is a title that will be displayed in the document selector
# NOTE: If you're using rspec-api to expose Swagger files (under swagger_root) as JSON or YAML endpoints,
# JSON endpoint and the second is a title that will be displayed in the document selector
# NOTE: If you're using rspec-api to expose Swagger files (under swagger_root) as JSON endpoints,
# then the list below should correspond to the relative paths for those endpoints
c.swagger_endpoint '/api-docs/v1/swagger.yaml', 'API V1 Docs'
c.swagger_endpoint '/api-docs/v1/swagger.json', 'API V1 Docs'
end

View File

@@ -1,6 +1,6 @@
require 'rails_helper'
RSpec.feature 'swagger-ui', js: true do
feature 'swagger-ui', js: true do
scenario 'browsing api-docs' do
visit '/api-docs'

View File

@@ -1,6 +1,6 @@
require 'swagger_helper'
RSpec.describe 'Auth Tests API', type: :request, swagger_doc: 'v1/swagger.json' do
describe 'Auth Tests API', type: :request, swagger_doc: 'v1/swagger.json' do
path '/auth-tests/basic' do
post 'Authenticates with basic auth' do

View File

@@ -1,6 +1,6 @@
require 'swagger_helper'
RSpec.describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
let(:api_key) { 'fake_key' }
path '/blogs' do

View File

@@ -1,9 +1,9 @@
require 'spec_helper'
require 'rake'
RSpec.describe 'rswag:specs:swaggerize' do
describe 'rswag:specs:swaggerize' do
let(:swagger_root) { Rails.root.to_s + '/swagger' }
before do
before do
TestApp::Application.load_tasks
FileUtils.rm_r(swagger_root) if File.exists?(swagger_root)
end