mirror of
https://github.com/ditkrg/rswag.git
synced 2026-01-25 23:32:58 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7025ec0063 | ||
|
|
23f4120fe3 | ||
|
|
312f68ae72 | ||
|
|
63e0e53104 | ||
|
|
4d675056c1 | ||
|
|
de09df59e1 | ||
|
|
b81b2927be | ||
|
|
f1850bc6d0 |
@@ -1,13 +1,13 @@
|
|||||||
PATH
|
PATH
|
||||||
remote: ./rswag-api
|
remote: ./rswag-api
|
||||||
specs:
|
specs:
|
||||||
rswag-api (1.0.0)
|
rswag-api (1.0.1)
|
||||||
rails (>= 3.1, < 5.1)
|
rails (>= 3.1, < 5.1)
|
||||||
|
|
||||||
PATH
|
PATH
|
||||||
remote: ./rswag-specs
|
remote: ./rswag-specs
|
||||||
specs:
|
specs:
|
||||||
rswag-specs (1.0.0)
|
rswag-specs (1.0.1)
|
||||||
json-schema (~> 2.2)
|
json-schema (~> 2.2)
|
||||||
rails (>= 3.1, < 5.1)
|
rails (>= 3.1, < 5.1)
|
||||||
rspec-rails (>= 2.14, < 4)
|
rspec-rails (>= 2.14, < 4)
|
||||||
@@ -15,7 +15,7 @@ PATH
|
|||||||
PATH
|
PATH
|
||||||
remote: ./rswag-ui
|
remote: ./rswag-ui
|
||||||
specs:
|
specs:
|
||||||
rswag-ui (1.0.0)
|
rswag-ui (1.0.1)
|
||||||
rails (>= 3.1, < 5.1)
|
rails (>= 3.1, < 5.1)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
module Rswag
|
module Rswag
|
||||||
module Api
|
module Api
|
||||||
VERSION = '1.0.1'
|
VERSION = '1.0.2'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ require 'rspec/core'
|
|||||||
require 'rswag/specs/version'
|
require 'rswag/specs/version'
|
||||||
require 'rswag/specs/example_group_helpers'
|
require 'rswag/specs/example_group_helpers'
|
||||||
require 'rswag/specs/example_helpers'
|
require 'rswag/specs/example_helpers'
|
||||||
|
require 'rswag/specs/configuration'
|
||||||
require 'rswag/specs/railtie' if defined?(Rails::Railtie)
|
require 'rswag/specs/railtie' if defined?(Rails::Railtie)
|
||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
@@ -15,6 +16,10 @@ module Rswag
|
|||||||
c.include ExampleHelpers, type: :request
|
c.include ExampleHelpers, type: :request
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.config
|
||||||
|
@config ||= Configuration.new(RSpec.configuration)
|
||||||
|
end
|
||||||
|
|
||||||
# Support Rails 3+ and RSpec 2+ (sigh!)
|
# Support Rails 3+ and RSpec 2+ (sigh!)
|
||||||
RAILS_VERSION = Rails::VERSION::MAJOR
|
RAILS_VERSION = Rails::VERSION::MAJOR
|
||||||
RSPEC_VERSION = RSpec::Core::Version::STRING.split('.').first.to_i
|
RSPEC_VERSION = RSpec::Core::Version::STRING.split('.').first.to_i
|
||||||
|
|||||||
37
rswag-specs/lib/rswag/specs/configuration.rb
Normal file
37
rswag-specs/lib/rswag/specs/configuration.rb
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
module Rswag
|
||||||
|
module Specs
|
||||||
|
|
||||||
|
class Configuration
|
||||||
|
|
||||||
|
def initialize(rspec_config)
|
||||||
|
@rspec_config = rspec_config
|
||||||
|
end
|
||||||
|
|
||||||
|
def swagger_root
|
||||||
|
@swagger_root ||= begin
|
||||||
|
if @rspec_config.swagger_root.nil?
|
||||||
|
raise ConfigurationError, 'No swagger_root provided. See swagger_helper.rb'
|
||||||
|
end
|
||||||
|
@rspec_config.swagger_root
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def swagger_docs
|
||||||
|
@swagger_docs ||= begin
|
||||||
|
if @rspec_config.swagger_docs.nil? || @rspec_config.swagger_docs.empty?
|
||||||
|
raise ConfigurationError, 'No swagger_docs defined. See swagger_helper.rb'
|
||||||
|
end
|
||||||
|
@rspec_config.swagger_docs
|
||||||
|
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]
|
||||||
|
swagger_docs[name]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ConfigurationError < StandardError; end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -2,9 +2,9 @@ module Rswag
|
|||||||
module Specs
|
module Specs
|
||||||
module ExampleGroupHelpers
|
module ExampleGroupHelpers
|
||||||
|
|
||||||
def path(path, &block)
|
def path(template, &block)
|
||||||
api_metadata = { path: path}
|
api_metadata = { path_item: { template: template } }
|
||||||
describe(path, api_metadata, &block)
|
describe(template, api_metadata, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
[ :get, :post, :patch, :put, :delete, :head ].each do |verb|
|
[ :get, :post, :patch, :put, :delete, :head ].each do |verb|
|
||||||
@@ -37,8 +37,14 @@ module Rswag
|
|||||||
|
|
||||||
def parameter(attributes)
|
def parameter(attributes)
|
||||||
attributes[:required] = true if attributes[:in].to_sym == :path
|
attributes[:required] = true if attributes[:in].to_sym == :path
|
||||||
|
|
||||||
|
if metadata.has_key?(:operation)
|
||||||
metadata[:operation][:parameters] ||= []
|
metadata[:operation][:parameters] ||= []
|
||||||
metadata[:operation][:parameters] << attributes
|
metadata[:operation][:parameters] << attributes
|
||||||
|
else
|
||||||
|
metadata[:path_item][:parameters] ||= []
|
||||||
|
metadata[:path_item][:parameters] << attributes
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def response(code, description, &block)
|
def response(code, description, &block)
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ module Rswag
|
|||||||
module ExampleHelpers
|
module ExampleHelpers
|
||||||
|
|
||||||
def submit_request(api_metadata)
|
def submit_request(api_metadata)
|
||||||
factory = RequestFactory.new(api_metadata, global_metadata(api_metadata[:swagger_doc]))
|
global_metadata = rswag_config.get_swagger_doc(api_metadata[:swagger_doc])
|
||||||
|
factory = RequestFactory.new(api_metadata, global_metadata)
|
||||||
|
|
||||||
if RAILS_VERSION < 5
|
if RAILS_VERSION < 5
|
||||||
send(
|
send(
|
||||||
@@ -28,15 +29,15 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
def assert_response_matches_metadata(api_metadata)
|
def assert_response_matches_metadata(api_metadata)
|
||||||
validator = ResponseValidator.new(api_metadata, global_metadata(api_metadata[:swagger_doc]))
|
global_metadata = rswag_config.get_swagger_doc(api_metadata[:swagger_doc])
|
||||||
|
validator = ResponseValidator.new(api_metadata, global_metadata)
|
||||||
validator.validate!(response)
|
validator.validate!(response)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def global_metadata(swagger_doc)
|
def rswag_config
|
||||||
swagger_docs = ::RSpec.configuration.swagger_docs
|
::Rswag::Specs.config
|
||||||
swagger_doc.nil? ? swagger_docs.values.first : swagger_docs[swagger_doc]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
def build_fullpath(example)
|
def build_fullpath(example)
|
||||||
@api_metadata[:path].dup.tap do |t|
|
@api_metadata[:path_item][:template].dup.tap do |t|
|
||||||
t.prepend(@global_metadata[:basePath] || '')
|
t.prepend(@global_metadata[:basePath] || '')
|
||||||
parameters_in(:path).each { |p| t.gsub!("{#{p[:name]}}", example.send(p[:name]).to_s) }
|
parameters_in(:path).each { |p| t.gsub!("{#{p[:name]}}", example.send(p[:name]).to_s) }
|
||||||
t.concat(build_query_string(example))
|
t.concat(build_query_string(example))
|
||||||
@@ -44,7 +44,13 @@ module Rswag
|
|||||||
private
|
private
|
||||||
|
|
||||||
def parameters_in(location)
|
def parameters_in(location)
|
||||||
(@api_metadata[:operation][:parameters] || [])
|
path_item_params = @api_metadata[:path_item][:parameters] || []
|
||||||
|
operation_params = @api_metadata[:operation][:parameters] || []
|
||||||
|
applicable_params = operation_params
|
||||||
|
.concat(path_item_params)
|
||||||
|
.uniq { |p| p[:name] } # operation params should override path_item params
|
||||||
|
|
||||||
|
applicable_params
|
||||||
.map { |p| p['$ref'] ? resolve_parameter(p['$ref']) : p } # resolve any references
|
.map { |p| p['$ref'] ? resolve_parameter(p['$ref']) : p } # resolve any references
|
||||||
.concat(resolve_api_key_parameters)
|
.concat(resolve_api_key_parameters)
|
||||||
.select { |p| p[:in] == location }
|
.select { |p| p[:in] == location }
|
||||||
@@ -59,9 +65,13 @@ module Rswag
|
|||||||
|
|
||||||
def resolve_api_key_parameters
|
def resolve_api_key_parameters
|
||||||
@api_key_params ||= begin
|
@api_key_params ||= begin
|
||||||
global_requirements = (@global_metadata[:security] || {})
|
# First figure out the security requirement applicable to the operation
|
||||||
requirements = global_requirements.merge(@api_metadata[:operation][:security] || {})
|
global_requirements = (@global_metadata[:security] || [] ).map { |r| r.keys.first }
|
||||||
definitions = (@global_metadata[:securityDefinitions] || {}).slice(*requirements.keys)
|
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 }
|
definitions.values.select { |d| d[:type] == :apiKey }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
require 'active_support/core_ext/hash/deep_merge'
|
require 'active_support/core_ext/hash/deep_merge'
|
||||||
require 'rspec/core/formatters/base_text_formatter'
|
|
||||||
require 'swagger_helper'
|
require 'swagger_helper'
|
||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
@@ -11,12 +10,9 @@ module Rswag
|
|||||||
::RSpec::Core::Formatters.register self, :example_group_finished, :stop
|
::RSpec::Core::Formatters.register self, :example_group_finished, :stop
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(output)
|
def initialize(output, config = Rswag::Specs.config)
|
||||||
@output = output
|
@output = output
|
||||||
@swagger_root = ::RSpec.configuration.swagger_root
|
@config = config
|
||||||
raise ConfigurationError, 'Missing swagger_root. See swagger_helper.rb' if @swagger_root.nil?
|
|
||||||
@swagger_docs = ::RSpec.configuration.swagger_docs || []
|
|
||||||
raise ConfigurationError, 'Missing swagger_docs. See swagger_helper.rb' if @swagger_docs.empty?
|
|
||||||
|
|
||||||
@output.puts 'Generating Swagger docs ...'
|
@output.puts 'Generating Swagger docs ...'
|
||||||
end
|
end
|
||||||
@@ -30,13 +26,13 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
return unless metadata.has_key?(:response)
|
return unless metadata.has_key?(:response)
|
||||||
swagger_doc = get_swagger_doc(metadata[:swagger_doc])
|
swagger_doc = @config.get_swagger_doc(metadata[:swagger_doc])
|
||||||
swagger_doc.deep_merge!(metadata_to_swagger(metadata))
|
swagger_doc.deep_merge!(metadata_to_swagger(metadata))
|
||||||
end
|
end
|
||||||
|
|
||||||
def stop(notification=nil)
|
def stop(notification=nil)
|
||||||
@swagger_docs.each do |url_path, doc|
|
@config.swagger_docs.each do |url_path, doc|
|
||||||
file_path = File.join(@swagger_root, url_path)
|
file_path = File.join(@config.swagger_root, url_path)
|
||||||
dirname = File.dirname(file_path)
|
dirname = File.dirname(file_path)
|
||||||
FileUtils.mkdir_p dirname unless File.exists?(dirname)
|
FileUtils.mkdir_p dirname unless File.exists?(dirname)
|
||||||
|
|
||||||
@@ -50,30 +46,22 @@ module Rswag
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def get_swagger_doc(tag)
|
|
||||||
return @swagger_docs.values.first if tag.nil?
|
|
||||||
raise ConfigurationError, "Unknown swagger_doc '#{tag}'" unless @swagger_docs.has_key?(tag)
|
|
||||||
@swagger_docs[tag]
|
|
||||||
end
|
|
||||||
|
|
||||||
def metadata_to_swagger(metadata)
|
def metadata_to_swagger(metadata)
|
||||||
response_code = metadata[:response][:code]
|
response_code = metadata[:response][:code]
|
||||||
response = metadata[:response].reject { |k,v| k == :code }
|
response = metadata[:response].reject { |k,v| k == :code }
|
||||||
|
|
||||||
verb = metadata[:operation][:verb]
|
verb = metadata[:operation][:verb]
|
||||||
operation = metadata[:operation]
|
operation = metadata[:operation]
|
||||||
.reject { |k,v| k == :verb }
|
.reject { |k,v| k == :verb }
|
||||||
.merge(responses: { response_code => response })
|
.merge(responses: { response_code => response })
|
||||||
|
|
||||||
{
|
path_template = metadata[:path_item][:template]
|
||||||
paths: {
|
path_item = metadata[:path_item]
|
||||||
metadata[:path] => {
|
.reject { |k,v| k == :template }
|
||||||
verb => operation
|
.merge(verb => operation)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class ConfigurationError < StandardError; end
|
{ paths: { path_template => path_item } }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
module Rswag
|
module Rswag
|
||||||
module Specs
|
module Specs
|
||||||
VERSION = '1.0.1'
|
VERSION = '1.0.2'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
77
rswag-specs/spec/rswag/specs/configuration_spec.rb
Normal file
77
rswag-specs/spec/rswag/specs/configuration_spec.rb
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
require 'rswag/specs/configuration'
|
||||||
|
|
||||||
|
module Rswag
|
||||||
|
module Specs
|
||||||
|
|
||||||
|
describe Configuration do
|
||||||
|
subject { described_class.new(rspec_config) }
|
||||||
|
|
||||||
|
let(:rspec_config) { OpenStruct.new(swagger_root: swagger_root, swagger_docs: swagger_docs) }
|
||||||
|
let(:swagger_root) { 'foobar' }
|
||||||
|
let(:swagger_docs) do
|
||||||
|
{
|
||||||
|
'v1/swagger.json' => { info: { title: 'v1' } },
|
||||||
|
'v2/swagger.json' => { info: { title: 'v2' } }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#swagger_root' do
|
||||||
|
let(:response) { subject.swagger_root }
|
||||||
|
|
||||||
|
context 'provided in rspec config' do
|
||||||
|
it { expect(response).to eq('foobar') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'not provided' do
|
||||||
|
let(:swagger_root) { nil }
|
||||||
|
it { expect { response }.to raise_error ConfigurationError }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#swagger_docs' do
|
||||||
|
let(:response) { subject.swagger_docs }
|
||||||
|
|
||||||
|
context 'provided in rspec config' do
|
||||||
|
it { expect(response).to be_an_instance_of(Hash) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'not provided' do
|
||||||
|
let(:swagger_docs) { nil }
|
||||||
|
it { expect { response }.to raise_error ConfigurationError }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'provided but empty' do
|
||||||
|
let(:swagger_docs) { {} }
|
||||||
|
it { expect { response }.to raise_error ConfigurationError }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#get_swagger_doc(tag=nil)' do
|
||||||
|
let(:swagger_doc) { subject.get_swagger_doc(tag) }
|
||||||
|
|
||||||
|
context 'no tag provided' do
|
||||||
|
let(:tag) { nil }
|
||||||
|
|
||||||
|
it 'returns the first doc in rspec config' do
|
||||||
|
expect(swagger_doc).to eq(info: { title: 'v1' })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'tag provided' do
|
||||||
|
context 'matching doc' do
|
||||||
|
let(:tag) { 'v2/swagger.json' }
|
||||||
|
|
||||||
|
it 'returns the matching doc in rspec config' do
|
||||||
|
expect(swagger_doc).to eq(info: { title: 'v2' })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'no matching doc' do
|
||||||
|
let(:tag) { 'foobar' }
|
||||||
|
it { expect { swagger_doc }.to raise_error ConfigurationError }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -5,20 +5,21 @@ module Rswag
|
|||||||
|
|
||||||
describe ExampleGroupHelpers do
|
describe ExampleGroupHelpers do
|
||||||
subject { double('example_group') }
|
subject { double('example_group') }
|
||||||
let(:api_metadata) { {} }
|
|
||||||
before do
|
before do
|
||||||
subject.extend ExampleGroupHelpers
|
subject.extend ExampleGroupHelpers
|
||||||
allow(subject).to receive(:describe)
|
allow(subject).to receive(:describe)
|
||||||
allow(subject).to receive(:context)
|
allow(subject).to receive(:context)
|
||||||
allow(subject).to receive(:metadata).and_return(api_metadata)
|
allow(subject).to receive(:metadata).and_return(api_metadata)
|
||||||
end
|
end
|
||||||
|
let(:api_metadata) { {} }
|
||||||
|
|
||||||
describe '#path(path)' do
|
describe '#path(path)' do
|
||||||
before { subject.path('/blogs') }
|
before { subject.path('/blogs') }
|
||||||
|
|
||||||
it "delegates to 'describe' with 'path' metadata" do
|
it "delegates to 'describe' with 'path' metadata" do
|
||||||
expect(subject).to have_received(:describe).with(
|
expect(subject).to have_received(:describe).with(
|
||||||
'/blogs', path: '/blogs'
|
'/blogs', path_item: { template: '/blogs' }
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -34,7 +35,6 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe '#tags|description|operationId|consumes|produces|schemes|deprecated(value)' do
|
describe '#tags|description|operationId|consumes|produces|schemes|deprecated(value)' do
|
||||||
let(:api_metadata) { { operation: {} } }
|
|
||||||
before do
|
before do
|
||||||
subject.tags('Blogs', 'Admin')
|
subject.tags('Blogs', 'Admin')
|
||||||
subject.description('Some description')
|
subject.description('Some description')
|
||||||
@@ -44,6 +44,7 @@ module Rswag
|
|||||||
subject.schemes('http', 'https')
|
subject.schemes('http', 'https')
|
||||||
subject.deprecated(true)
|
subject.deprecated(true)
|
||||||
end
|
end
|
||||||
|
let(:api_metadata) { { operation: {} } }
|
||||||
|
|
||||||
it "adds to the 'operation' metadata" do
|
it "adds to the 'operation' metadata" do
|
||||||
expect(api_metadata[:operation]).to match(
|
expect(api_metadata[:operation]).to match(
|
||||||
@@ -59,7 +60,6 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe '#tags|description|operationId|consumes|produces|schemes|deprecated|security(value)' do
|
describe '#tags|description|operationId|consumes|produces|schemes|deprecated|security(value)' do
|
||||||
let(:api_metadata) { { operation: {} } }
|
|
||||||
before do
|
before do
|
||||||
subject.tags('Blogs', 'Admin')
|
subject.tags('Blogs', 'Admin')
|
||||||
subject.description('Some description')
|
subject.description('Some description')
|
||||||
@@ -70,6 +70,7 @@ module Rswag
|
|||||||
subject.deprecated(true)
|
subject.deprecated(true)
|
||||||
subject.security(api_key: [])
|
subject.security(api_key: [])
|
||||||
end
|
end
|
||||||
|
let(:api_metadata) { { operation: {} } }
|
||||||
|
|
||||||
it "adds to the 'operation' metadata" do
|
it "adds to the 'operation' metadata" do
|
||||||
expect(api_metadata[:operation]).to match(
|
expect(api_metadata[:operation]).to match(
|
||||||
@@ -86,10 +87,21 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe '#parameter(attributes)' do
|
describe '#parameter(attributes)' do
|
||||||
let(:api_metadata) { { operation: {} } }
|
|
||||||
|
|
||||||
context 'always' do
|
context "when called at the 'path' level" do
|
||||||
before { subject.parameter(name: :blog, in: :body, schema: { type: 'object' }) }
|
before { subject.parameter(name: :blog, in: :body, schema: { type: 'object' }) }
|
||||||
|
let(:api_metadata) { { path_item: {} } } # i.e. operation not defined yet
|
||||||
|
|
||||||
|
it "adds to the 'path_item parameters' metadata" do
|
||||||
|
expect(api_metadata[:path_item][:parameters]).to match(
|
||||||
|
[ name: :blog, in: :body, schema: { type: 'object' } ]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when called at the 'operation' level" do
|
||||||
|
before { subject.parameter(name: :blog, in: :body, schema: { type: 'object' }) }
|
||||||
|
let(:api_metadata) { { path_item: {}, operation: {} } } # i.e. operation defined
|
||||||
|
|
||||||
it "adds to the 'operation parameters' metadata" do
|
it "adds to the 'operation parameters' metadata" do
|
||||||
expect(api_metadata[:operation][:parameters]).to match(
|
expect(api_metadata[:operation][:parameters]).to match(
|
||||||
@@ -100,6 +112,7 @@ module Rswag
|
|||||||
|
|
||||||
context "'path' parameter" do
|
context "'path' parameter" do
|
||||||
before { subject.parameter(name: :id, in: :path) }
|
before { subject.parameter(name: :id, in: :path) }
|
||||||
|
let(:api_metadata) { { operation: {} } }
|
||||||
|
|
||||||
it "automatically sets the 'required' flag" do
|
it "automatically sets the 'required' flag" do
|
||||||
expect(api_metadata[:operation][:parameters]).to match(
|
expect(api_metadata[:operation][:parameters]).to match(
|
||||||
@@ -120,8 +133,8 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe '#schema(value)' do
|
describe '#schema(value)' do
|
||||||
let(:api_metadata) { { response: {} } }
|
|
||||||
before { subject.schema(type: 'object') }
|
before { subject.schema(type: 'object') }
|
||||||
|
let(:api_metadata) { { response: {} } }
|
||||||
|
|
||||||
it "adds to the 'response' metadata" do
|
it "adds to the 'response' metadata" do
|
||||||
expect(api_metadata[:response][:schema]).to match(type: 'object')
|
expect(api_metadata[:response][:schema]).to match(type: 'object')
|
||||||
@@ -129,8 +142,8 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe '#header(name, attributes)' do
|
describe '#header(name, attributes)' do
|
||||||
let(:api_metadata) { { response: {} } }
|
|
||||||
before { subject.header('Date', type: 'string') }
|
before { subject.header('Date', type: 'string') }
|
||||||
|
let(:api_metadata) { { response: {} } }
|
||||||
|
|
||||||
it "adds to the 'response headers' metadata" do
|
it "adds to the 'response headers' metadata" do
|
||||||
expect(api_metadata[:response][:headers]).to match(
|
expect(api_metadata[:response][:headers]).to match(
|
||||||
|
|||||||
@@ -4,9 +4,19 @@ module Rswag
|
|||||||
module Specs
|
module Specs
|
||||||
|
|
||||||
describe ExampleHelpers do
|
describe ExampleHelpers do
|
||||||
|
subject { double('example') }
|
||||||
|
|
||||||
|
before do
|
||||||
|
subject.extend ExampleHelpers
|
||||||
|
# Mock out some infrastructure
|
||||||
|
stub_const('Rails::VERSION::MAJOR', 3)
|
||||||
|
rswag_config = double('rswag_config')
|
||||||
|
allow(rswag_config).to receive(:get_swagger_doc).and_return(global_metadata)
|
||||||
|
allow(subject).to receive(:rswag_config).and_return(rswag_config)
|
||||||
|
end
|
||||||
let(:api_metadata) do
|
let(:api_metadata) do
|
||||||
{
|
{
|
||||||
path: '/blogs/{blog_id}/comments/{id}',
|
path_item: { template: '/blogs/{blog_id}/comments/{id}' },
|
||||||
operation: {
|
operation: {
|
||||||
verb: :put,
|
verb: :put,
|
||||||
summary: 'Updates a blog',
|
summary: 'Updates a blog',
|
||||||
@@ -16,9 +26,9 @@ module Rswag
|
|||||||
{ name: 'q1', in: :query, type: 'string' },
|
{ name: 'q1', in: :query, type: 'string' },
|
||||||
{ name: :blog, in: :body, schema: { type: 'object' } }
|
{ name: :blog, in: :body, schema: { type: 'object' } }
|
||||||
],
|
],
|
||||||
security: {
|
security: [
|
||||||
api_key: []
|
{ api_key: [] }
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@@ -34,22 +44,14 @@ module Rswag
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { double('example') }
|
describe '#submit_request(api_metadata)' do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
subject.extend ExampleHelpers
|
|
||||||
allow(subject).to receive(:blog_id).and_return(1)
|
allow(subject).to receive(:blog_id).and_return(1)
|
||||||
allow(subject).to receive(:id).and_return(2)
|
allow(subject).to receive(:id).and_return(2)
|
||||||
allow(subject).to receive(:q1).and_return('foo')
|
allow(subject).to receive(:q1).and_return('foo')
|
||||||
allow(subject).to receive(:api_key).and_return('fookey')
|
allow(subject).to receive(:api_key).and_return('fookey')
|
||||||
allow(subject).to receive(:blog).and_return(text: 'Some comment')
|
allow(subject).to receive(:blog).and_return(text: 'Some comment')
|
||||||
allow(subject).to receive(:global_metadata).and_return(global_metadata)
|
|
||||||
allow(subject).to receive(:put)
|
allow(subject).to receive(:put)
|
||||||
end
|
|
||||||
|
|
||||||
describe '#submit_request(api_metadata)' do
|
|
||||||
before do
|
|
||||||
stub_const('Rails::VERSION::MAJOR', 3)
|
|
||||||
subject.submit_request(api_metadata)
|
subject.submit_request(api_metadata)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,15 @@ module Rswag
|
|||||||
module Specs
|
module Specs
|
||||||
|
|
||||||
describe RequestFactory do
|
describe RequestFactory do
|
||||||
|
subject { RequestFactory.new(api_metadata, global_metadata) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(example).to receive(:blog_id).and_return(1)
|
||||||
|
allow(example).to receive(:id).and_return('2')
|
||||||
|
end
|
||||||
let(:api_metadata) do
|
let(:api_metadata) do
|
||||||
{
|
{
|
||||||
path: '/blogs/{blog_id}/comments/{id}',
|
path_item: { template: '/blogs/{blog_id}/comments/{id}' },
|
||||||
operation: {
|
operation: {
|
||||||
verb: :put,
|
verb: :put,
|
||||||
summary: 'Updates a blog',
|
summary: 'Updates a blog',
|
||||||
@@ -18,14 +24,7 @@ module Rswag
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
let(:global_metadata) { {} }
|
let(:global_metadata) { {} }
|
||||||
|
|
||||||
subject { RequestFactory.new(api_metadata, global_metadata) }
|
|
||||||
|
|
||||||
let(:example) { double('example') }
|
let(:example) { double('example') }
|
||||||
before do
|
|
||||||
allow(example).to receive(:blog_id).and_return(1)
|
|
||||||
allow(example).to receive(:id).and_return('2')
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#build_fullpath(example)' do
|
describe '#build_fullpath(example)' do
|
||||||
let(:path) { subject.build_fullpath(example) }
|
let(:path) { subject.build_fullpath(example) }
|
||||||
@@ -103,7 +102,7 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
context 'global requirement' do
|
context 'global requirement' do
|
||||||
before { global_metadata[:security] = { api_key: [] } }
|
before { global_metadata[:security] = [ { api_key: [] } ] }
|
||||||
|
|
||||||
it "appends the api_key using metadata and example value" do
|
it "appends the api_key using metadata and example value" do
|
||||||
expect(path).to eq('/blogs/1/comments/2?api_key=fookey')
|
expect(path).to eq('/blogs/1/comments/2?api_key=fookey')
|
||||||
@@ -111,7 +110,7 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
context 'operation-specific requirement' do
|
context 'operation-specific requirement' do
|
||||||
before { api_metadata[:operation][:security] = { api_key: [] } }
|
before { api_metadata[:operation][:security] = [ { api_key: [] } ] }
|
||||||
|
|
||||||
it "appends the api_key using metadata and example value" do
|
it "appends the api_key using metadata and example value" do
|
||||||
expect(path).to eq('/blogs/1/comments/2?api_key=fookey')
|
expect(path).to eq('/blogs/1/comments/2?api_key=fookey')
|
||||||
@@ -126,6 +125,17 @@ module Rswag
|
|||||||
expect(path).to eq('/foobar/blogs/1/comments/2')
|
expect(path).to eq('/foobar/blogs/1/comments/2')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "defined at the 'path' level" do
|
||||||
|
before do
|
||||||
|
api_metadata[:path_item][:parameters] = [ { name: :blog_id, in: :path } ]
|
||||||
|
api_metadata[:operation][:parameters] = [ { name: :id, in: :path } ]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "builds path from parameters defined at path and operation levels" do
|
||||||
|
expect(path).to eq('/blogs/1/comments/2')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#build_body(example)' do
|
describe '#build_body(example)' do
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ module Rswag
|
|||||||
module Specs
|
module Specs
|
||||||
|
|
||||||
describe ResponseValidator do
|
describe ResponseValidator do
|
||||||
|
subject { ResponseValidator.new(api_metadata, global_metadata) }
|
||||||
|
|
||||||
let(:api_metadata) { { response: { code: 200 } } }
|
let(:api_metadata) { { response: { code: 200 } } }
|
||||||
let(:global_metadata) { {} }
|
let(:global_metadata) { {} }
|
||||||
|
|
||||||
subject { ResponseValidator.new(api_metadata, global_metadata) }
|
|
||||||
|
|
||||||
describe '#validate!(response)' do
|
describe '#validate!(response)' do
|
||||||
let(:call) { subject.validate!(response) }
|
let(:call) { subject.validate!(response) }
|
||||||
|
|
||||||
|
|||||||
@@ -5,50 +5,33 @@ module Rswag
|
|||||||
module Specs
|
module Specs
|
||||||
|
|
||||||
describe SwaggerFormatter do
|
describe SwaggerFormatter do
|
||||||
# Mock infrastructure - output, RSpec.configuration etc.
|
subject { described_class.new(output, config) }
|
||||||
|
|
||||||
|
# Mock out some infrastructure
|
||||||
|
before do
|
||||||
|
allow(config).to receive(:swagger_root).and_return(swagger_root)
|
||||||
|
end
|
||||||
|
let(:config) { double('config') }
|
||||||
let(:output) { double('output').as_null_object }
|
let(:output) { double('output').as_null_object }
|
||||||
let(:swagger_root) { File.expand_path('../tmp', __FILE__) }
|
let(:swagger_root) { File.expand_path('../tmp/swagger', __FILE__) }
|
||||||
let(:swagger_docs) do
|
|
||||||
{
|
|
||||||
'v1/swagger.json' => { info: { version: 'v1' } }
|
|
||||||
}
|
|
||||||
end
|
|
||||||
let(:config) { OpenStruct.new(swagger_root: swagger_root, swagger_docs: swagger_docs) }
|
|
||||||
before { allow(RSpec).to receive(:configuration).and_return(config) }
|
|
||||||
|
|
||||||
subject { described_class.new(output) }
|
|
||||||
|
|
||||||
describe '::new(output)' do
|
|
||||||
context 'swagger_root not configured' do
|
|
||||||
let(:swagger_root) { nil }
|
|
||||||
it { expect { subject }.to raise_error ConfigurationError }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'swagger_docs not configured' do
|
|
||||||
let(:swagger_docs) { nil }
|
|
||||||
it { expect { subject }.to raise_error ConfigurationError }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#example_group_finished(notification)' do
|
describe '#example_group_finished(notification)' do
|
||||||
# Mock notification parameter
|
before do
|
||||||
|
allow(config).to receive(:get_swagger_doc).and_return(swagger_doc)
|
||||||
|
subject.example_group_finished(notification)
|
||||||
|
end
|
||||||
|
let(:swagger_doc) { {} }
|
||||||
|
let(:notification) { OpenStruct.new(group: OpenStruct.new(metadata: api_metadata)) }
|
||||||
let(:api_metadata) do
|
let(:api_metadata) do
|
||||||
{
|
{
|
||||||
path: '/blogs',
|
path_item: { template: '/blogs' },
|
||||||
operation: { verb: :post, summary: 'Creates a blog' },
|
operation: { verb: :post, summary: 'Creates a blog' },
|
||||||
response: { code: '201', description: 'blog created' }
|
response: { code: '201', description: 'blog created' }
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
let(:notification) { OpenStruct.new(group: OpenStruct.new(metadata: api_metadata)) }
|
|
||||||
|
|
||||||
let(:call) { subject.example_group_finished(notification) }
|
it 'converts to swagger and merges into the corresponding swagger doc' do
|
||||||
|
expect(swagger_doc).to match(
|
||||||
context 'single swagger_doc' do
|
|
||||||
before { call }
|
|
||||||
|
|
||||||
it 'converts metadata to swagger and merges into the doc' do
|
|
||||||
expect(swagger_docs.values.first).to match(
|
|
||||||
info: { version: 'v1' },
|
|
||||||
paths: {
|
paths: {
|
||||||
'/blogs' => {
|
'/blogs' => {
|
||||||
post: {
|
post: {
|
||||||
@@ -63,54 +46,18 @@ module Rswag
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'multiple swagger_docs' do
|
|
||||||
let(:swagger_docs) do
|
|
||||||
{
|
|
||||||
'v1/swagger.json' => {},
|
|
||||||
'v2/swagger.json' => {}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
context "no 'swagger_doc' tag" do
|
|
||||||
before { call }
|
|
||||||
|
|
||||||
it 'merges into the first doc' do
|
|
||||||
expect(swagger_docs.values.first).to have_key(:paths)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "matching 'swagger_doc' tag" do
|
|
||||||
before do
|
|
||||||
api_metadata[:swagger_doc] = 'v2/swagger.json'
|
|
||||||
call
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'merges into the matched doc' do
|
|
||||||
expect(swagger_docs.values.last).to have_key(:paths)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "non matching 'swagger_doc' tag" do
|
|
||||||
before { api_metadata[:swagger_doc] = 'foobar' }
|
|
||||||
it { expect { call }.to raise_error ConfigurationError }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#stop' do
|
describe '#stop' do
|
||||||
let(:notification) { double('notification') }
|
|
||||||
let(:swagger_docs) do
|
|
||||||
{
|
|
||||||
'v1/swagger.json' => { info: { version: 'v1' } },
|
|
||||||
'v2/swagger.json' => { info: { version: 'v2' } },
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
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(
|
||||||
|
'v1/swagger.json' => { info: { version: 'v1' } },
|
||||||
|
'v2/swagger.json' => { info: { version: 'v2' } }
|
||||||
|
)
|
||||||
subject.stop(notification)
|
subject.stop(notification)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
let(:notification) { double('notification') }
|
||||||
|
|
||||||
it 'writes the swagger_doc(s) to file' do
|
it 'writes the swagger_doc(s) to file' do
|
||||||
expect(File).to exist("#{swagger_root}/v1/swagger.json")
|
expect(File).to exist("#{swagger_root}/v1/swagger.json")
|
||||||
expect(File).to exist("#{swagger_root}/v2/swagger.json")
|
expect(File).to exist("#{swagger_root}/v2/swagger.json")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
module Rswag
|
module Rswag
|
||||||
module Ui
|
module Ui
|
||||||
VERSION = '1.0.1'
|
VERSION = '1.0.2'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
module Rswag
|
module Rswag
|
||||||
VERSION = '1.0.1'
|
VERSION = '1.0.2'
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -41,12 +41,13 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
|
|||||||
end
|
end
|
||||||
|
|
||||||
path '/blogs/{id}' do
|
path '/blogs/{id}' do
|
||||||
|
parameter name: :id, :in => :path, :type => :string
|
||||||
|
|
||||||
get 'Retrieves a blog' do
|
get 'Retrieves a blog' do
|
||||||
tags 'Blogs'
|
tags 'Blogs'
|
||||||
description 'Retrieves a specific blog by id'
|
description 'Retrieves a specific blog by id'
|
||||||
operationId 'getBlog'
|
operationId 'getBlog'
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
parameter name: :id, :in => :path, :type => :string
|
|
||||||
|
|
||||||
response '200', 'blog found' do
|
response '200', 'blog found' do
|
||||||
schema '$ref' => '#/definitions/blog'
|
schema '$ref' => '#/definitions/blog'
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ RSpec.configure do |config|
|
|||||||
in: :query
|
in: :query
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
security: {
|
security: [
|
||||||
api_key: []
|
{ api_key: [] }
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -68,6 +68,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/blogs/{id}": {
|
"/blogs/{id}": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "Retrieves a blog",
|
"summary": "Retrieves a blog",
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -78,14 +86,6 @@
|
|||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"in": "path",
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "blog found",
|
"description": "blog found",
|
||||||
@@ -145,9 +145,11 @@
|
|||||||
"in": "query"
|
"in": "query"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"security": {
|
"security": [
|
||||||
|
{
|
||||||
"api_key": [
|
"api_key": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user