Merge branch 'master' of github.com:rswag/rswag

This commit is contained in:
Laura Watson 2019-10-17 13:34:54 +01:00
commit 71423cffb6
27 changed files with 211 additions and 38 deletions

View File

@ -12,15 +12,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed ### Fixed
### Security ### Security
## [2.1.0] - 2019-10-17
### Added
- New Spec Generator [#75](https://github.com/rswag/rswag/pull/75)
- Support for Options and Trace verbs; You must use a framework that supports this, for Options Rails 6.1+ Rails 6 does not support Trace. [#237](https://github.com/rswag/rswag/pull/75)
### Changed
- Update swagger-ui to 3.18.2 [#240](https://github.com/rswag/rswag/pull/240)
## [2.0.6] - 2019-10-03 ## [2.0.6] - 2019-10-03
### Added ### Added
- Support for Rails 6 [#228](https://github.com/rswag/rswag/pull/228) - Support for Rails 6 [#228](https://github.com/rswag/rswag/pull/228)
- Support for Windows paths [#176](https://github.com/rswag/rswag/pull/176) - Support for Windows paths [#176](https://github.com/rswag/rswag/pull/176)
### Changed ### Changed
- Show response body when error code is not expected [#117](https://github.com/rswag/rswag/pull/177) - Show response body when error code is not expected [#117](https://github.com/rswag/rswag/pull/177)
### Deprecated
### Removed
### Fixed
### Security
## [2.0.5] - 2018-07-10 ## [2.0.5] - 2018-07-10

View File

@ -14,8 +14,8 @@ Once you have an API that can describe itself in Swagger, you've opened the trea
|Rswag Version|Swagger (OpenAPI) Spec.|swagger-ui| |Rswag Version|Swagger (OpenAPI) Spec.|swagger-ui|
|----------|----------|----------| |----------|----------|----------|
|[master](https://github.com/rswag/rswag/tree/master)|2.0|3.17.3| |[master](https://github.com/rswag/rswag/tree/master)|2.0|3.18.2|
|[2.0.6](https://github.com/rswag/rswag/tree/2.0.6)|2.0|3.17.3| |[2.1.0](https://github.com/rswag/rswag/tree/2.1.0)|2.0|3.18.2|
|[1.6.0](https://github.com/rswag/rswag/tree/1.6.0)|2.0|2.2.5| |[1.6.0](https://github.com/rswag/rswag/tree/1.6.0)|2.0|2.2.5|
## Getting Started ## ## Getting Started ##
@ -48,7 +48,8 @@ Once you have an API that can describe itself in Swagger, you've opened the trea
Or run the install generators for each package separately if you installed Rswag as separate gems, as indicated above: Or run the install generators for each package separately if you installed Rswag as separate gems, as indicated above:
```ruby ```ruby
rails g rswag:api:install rswag:ui:install rails g rswag:api:install
rails g rswag:ui:install
RAILS_ENV=test rails g rswag:specs:install RAILS_ENV=test rails g rswag:specs:install
``` ```

View File

@ -0,0 +1,9 @@
Description:
This creates an RSpec request spec to define Swagger documentation for a
controller. It will create a test for each of the controller's methods.
Example:
rails generate rspec:swagger V3::AccountsController
This will create:
spec/requests/v3/accounts_spec.rb

View File

@ -0,0 +1,22 @@
require 'rswag/route_parser'
require 'rails/generators'
module Rspec
class SwaggerGenerator < ::Rails::Generators::NamedBase
source_root File.expand_path('../templates', __FILE__)
def setup
@routes = Rswag::RouteParser.new(controller_path).routes
end
def create_spec_file
template 'spec.rb', File.join('spec', 'requests', "#{controller_path}_spec.rb")
end
private
def controller_path
file_path.chomp('_controller')
end
end
end

View File

@ -0,0 +1,30 @@
require 'swagger_helper'
RSpec.describe '<%= controller_path %>', type: :request do
<% @routes.each do | template, path_item | %>
path '<%= template %>' do
<% unless path_item[:params].empty? -%>
# You'll want to customize the parameter types...
<% path_item[:params].each do |param| -%>
parameter name: '<%= param %>', in: :path, type: :string, description: '<%= param %>'
<% end -%>
<% end -%>
<% path_item[:actions].each do | action, details | %>
<%= action %>('<%= details[:summary] %>') do
response(200, 'successful') do
<% unless path_item[:params].empty? -%>
<% path_item[:params].each do |param| -%>
let(:<%= param %>) { '123' }
<% end -%>
<% end -%>
after do |example|
example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) }
end
run_test!
end
end
<% end -%>
end
<% end -%>
end

View File

@ -0,0 +1,58 @@
module Rswag
class RouteParser
attr_reader :controller
def initialize(controller)
@controller = controller
end
def routes
::Rails.application.routes.routes.select do |route|
route.defaults[:controller] == controller
end.reduce({}) do |tree, route|
path = path_from(route)
verb = verb_from(route)
tree[path] ||= { params: params_from(route), actions: {} }
tree[path][:actions][verb] = { summary: summary_from(route) }
tree
end
end
private
def path_from(route)
route.path.spec.to_s
.chomp('(.:format)') # Ignore any format suffix
.gsub(/:([^\/.?]+)/, '{\1}') # Convert :id to {id}
end
def verb_from(route)
verb = route.verb
if verb.kind_of? String
verb.downcase
else
verb.source.gsub(/[$^]/, '').downcase
end
end
def summary_from(route)
verb = route.requirements[:action]
noun = route.requirements[:controller].split('/').last.singularize
# Apply a few customizations to make things more readable
case verb
when 'index'
verb = 'list'
noun = noun.pluralize
when 'destroy'
verb = 'delete'
end
"#{verb} #{noun}"
end
def params_from(route)
route.segments - ['format']
end
end
end

View File

@ -3,7 +3,7 @@ require 'json-schema'
module Rswag module Rswag
module Specs module Specs
class ExtendedSchema < JSON::Schema::Draft4 class ExtendedSchema < JSON::Schema::Draft4
def initialize def initialize
super super
@attributes['type'] = ExtendedTypeAttribute @attributes['type'] = ExtendedTypeAttribute
@ -13,7 +13,7 @@ module Rswag
end end
class ExtendedTypeAttribute < JSON::Schema::TypeV4Attribute class ExtendedTypeAttribute < JSON::Schema::TypeV4Attribute
def self.validate(current_schema, data, fragments, processor, validator, options={}) def self.validate(current_schema, data, fragments, processor, validator, options={})
return if data.nil? && current_schema.schema['x-nullable'] == true return if data.nil? && current_schema.schema['x-nullable'] == true
super super

View File

@ -5,6 +5,10 @@ module Rswag
rake_tasks do rake_tasks do
load File.expand_path('../../../tasks/rswag-specs_tasks.rake', __FILE__) load File.expand_path('../../../tasks/rswag-specs_tasks.rake', __FILE__)
end end
generators do
require 'generators/rspec/swagger/swagger_generator.rb'
end
end end
end end
end end

View File

@ -54,7 +54,7 @@ module Rswag
definitions[key] definitions[key]
end end
def add_verb(request, metadata) def add_verb(request, metadata)
request[:verb] = metadata[:operation][:verb] request[:verb] = metadata[:operation][:verb]
end end
@ -104,7 +104,7 @@ module Rswag
end end
# Content-Type header # Content-Type header
consumes = metadata[:operation][:consumes] || swagger_doc[:consumes] consumes = metadata[:operation][:consumes] || swagger_doc[:consumes]
if consumes if consumes
content_type = example.respond_to?(:'Content-Type') ? example.send(:'Content-Type') : consumes.first content_type = example.respond_to?(:'Content-Type') ? example.send(:'Content-Type') : consumes.first
tuples << [ 'Content-Type', content_type ] tuples << [ 'Content-Type', content_type ]

View File

@ -0,0 +1,44 @@
require 'generator_spec'
require 'generators/rspec/swagger_generator'
require 'tmpdir'
module Rspec
describe SwaggerGenerator do
include GeneratorSpec::TestCase
destination Dir.mktmpdir
before(:all) do
prepare_destination
fixtures_dir = File.expand_path('../fixtures', __FILE__)
FileUtils.cp_r("#{fixtures_dir}/spec", destination_root)
end
after(:all) do
end
it 'installs the swagger_helper for rspec' do
allow_any_instance_of(Rswag::RouteParser).to receive(:routes).and_return(fake_routes)
run_generator ['Posts::CommentsController']
assert_file('spec/requests/posts/comments_spec.rb') do |content|
assert_match(/parameter name: 'post_id', in: :path, type: :string/, content)
assert_match(/patch\('update_comments comment'\)/, content)
end
end
private
def fake_routes
{
"/posts/{post_id}/comments/{id}" => {
:params => ["post_id", "id"],
:actions => {
"get" => { :summary=>"show comment" },
"patch" => { :summary=>"update_comments comment" }
}
}
}
end
end
end

View File

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

View File

@ -3,7 +3,7 @@ require 'rswag/specs/configuration'
module Rswag module Rswag
module Specs module Specs
describe Configuration do RSpec.describe Configuration do
subject { described_class.new(rspec_config) } subject { described_class.new(rspec_config) }
let(:rspec_config) { OpenStruct.new(swagger_root: swagger_root, swagger_docs: swagger_docs) } let(:rspec_config) { OpenStruct.new(swagger_root: swagger_root, swagger_docs: swagger_docs) }

View File

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

View File

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

View File

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

View File

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

View File

@ -3,8 +3,8 @@ require 'ostruct'
module Rswag module Rswag
module Specs module Specs
describe SwaggerFormatter do RSpec.describe SwaggerFormatter do
subject { described_class.new(output, config) } subject { described_class.new(output, config) }
# Mock out some infrastructure # Mock out some infrastructure
@ -47,7 +47,7 @@ module Rswag
end end
describe '#stop' do describe '#stop' do
before do before do
FileUtils.rm_r(swagger_root) if File.exists?(swagger_root) FileUtils.rm_r(swagger_root) if File.exists?(swagger_root)
allow(config).to receive(:swagger_docs).and_return( allow(config).to receive(:swagger_docs).and_return(
'v1/swagger.json' => { info: { version: 'v1' } }, 'v1/swagger.json' => { info: { version: 'v1' } },

View File

@ -5,9 +5,9 @@
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"swagger-ui-dist": { "swagger-ui-dist": {
"version": "3.17.3", "version": "3.18.2",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.17.3.tgz", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.18.2.tgz",
"integrity": "sha1-37lkCMzEZ3UVX3NpGQxdSyAW/lw=" "integrity": "sha512-pWAEiKkgWUJvjmLW9AojudnutJ+NTn5g6OdNLj1iIJWwCkoy40K3Upwa24DqFbmIE4vLX4XplND61hp2L+s5vg=="
} }
} }
} }

View File

@ -3,6 +3,6 @@
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"swagger-ui-dist": "3.17.3" "swagger-ui-dist": "3.18.2"
} }
} }

View File

@ -0,0 +1,2 @@
//= link_tree ../images
//= link_directory ../stylesheets .css

View File

View File

@ -2,15 +2,15 @@
# of editing this file, please use the migrations feature of Active Record to # of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition. # incrementally modify your database, and then regenerate this schema definition.
# #
# This file is the source Rails uses to define your schema when running `rails # Note that this schema.rb definition is the authoritative source for your
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to # database schema. If you need to create the application database on another
# be faster and is potentially less error prone than running all of your # system, you should be using db:schema:load, not running all the migrations
# migrations from scratch. Old migrations may fail to apply correctly if those # from scratch. The latter is a flawed and unsustainable approach (the more migrations
# migrations use external dependencies or application code. # you'll amass, the slower it'll run and the greater likelihood for issues).
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2016_02_18_212104) do ActiveRecord::Schema.define(version: 20160218212104) do
create_table "blogs", force: :cascade do |t| create_table "blogs", force: :cascade do |t|
t.string "title" t.string "title"

View File

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

View File

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

View File

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

View File

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