Honor basePath, output tags & other cleanup

This commit is contained in:
richie 2016-05-13 00:03:23 -07:00
parent 452917e0d9
commit 9a327e84cf
18 changed files with 190 additions and 91 deletions

View File

@ -1,7 +1,7 @@
PATH PATH
remote: . remote: .
specs: specs:
swagger_rails (0.0.1) swagger_rails (1.0.0.pre.beta)
rails (>= 3.1, < 5) rails (>= 3.1, < 5)
GEM GEM
@ -111,7 +111,7 @@ GEM
treetop (1.4.15) treetop (1.4.15)
polyglot polyglot
polyglot (>= 0.3.1) polyglot (>= 0.3.1)
tzinfo (0.3.46) tzinfo (0.3.49)
PLATFORMS PLATFORMS
ruby ruby
@ -119,6 +119,9 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
generator_spec generator_spec
pry pry
rspec-rails rspec-rails (~> 3.0)
sqlite3 sqlite3
swagger_rails! swagger_rails!
BUNDLED WITH
1.10.6

View File

@ -1,7 +1,7 @@
swagger_rails swagger_rails
========= =========
Leverage your api/integration test specs to generate [swagger](http://swagger.io/) descriptions for Rails-based API's. Use the provided DSL to accurately test and describe API operations in your spec files. Then, you can easily generate corresponding swagger.json files and serve them up with an embedded version of [swagger-ui](https://github.com/swagger-api/swagger-ui). This means you can complement your API with a slick discovery UI to assist consumers with their integration efforts. Best of all, it requires minimal coding and maintenance, allowing you to focus on building an awesome API! Generate API documentation, including a slick discovery UI and playground, directly from your rspec integration specs. Use the provided DSL to describe and test API operations in your spec files. Then, you can easily generate corresponding swagger.json files and serve them up with an embedded version of [swagger-ui](https://github.com/swagger-api/swagger-ui). Best of all, it requires minimal coding and maintenance, allowing you to focus on building an awesome API!
And that's not all ... And that's not all ...

View File

@ -3,7 +3,7 @@ module SwaggerRails
def index def index
@discovery_paths = Hash[ @discovery_paths = Hash[
SwaggerRails.swagger_docs.map do |path, doc| SwaggerRails.config.swagger_docs.map do |path, doc|
[ "#{root_path}#{path}", doc[:info][:title] ] [ "#{root_path}#{path}", doc[:info][:title] ]
end end
] ]

View File

@ -6,7 +6,7 @@ module SwaggerRails
source_root File.expand_path('../templates', __FILE__) source_root File.expand_path('../templates', __FILE__)
def add_swagger_dir def add_swagger_dir
empty_directory('config/swagger/v1') empty_directory('swagger/v1')
end end
def add_initializer def add_initializer

View File

@ -1,45 +0,0 @@
{
"swagger": "2.0",
"info": {
"version": "0.0.0",
"title": "[Enter a description for your API here]",
"description": "The docs below are powered by the default swagger.json that gets installed with swagger_rails. You'll need to update it to describe your API. See here for the complete swagger spec - https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md"
},
"paths": {
"/a/sample/resource": {
"post": {
"tags": [
"a/sample/resource"
],
"description": "Create a new sample resource",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/CreateSampleResource"
}
}
],
"responses": {
"200": {
"description": "Ok"
}
}
}
}
},
"definitions": {
"CreateSampleResource": {
"properties": {
"name": {
"type": "string"
},
"date_time": {
"type": "string",
"format": "date-time"
}
}
}
}
}

View File

@ -1,13 +1,16 @@
SwaggerRails.configure do |c| SwaggerRails.configure do |c|
# Define your swagger documents and provide global metadata # Define your swagger documents and provide any global metadata here
# Describe actual operations in your spec/test files # (Individual operations are generated from your spec/test files)
c.swagger_doc 'v1/swagger.json' do c.swagger_doc 'v1/swagger.json',
{ {
swagger: '2.0',
info: { info: {
title: 'API V1', title: 'API V1',
version: 'v1' version: 'v1'
} }
} }
end
# Specify a location to output generated swagger files
c.swagger_dir File.expand_path('../../../swagger', __FILE__)
end end

View File

@ -2,23 +2,32 @@ require "swagger_rails/engine"
module SwaggerRails module SwaggerRails
def self.configure class Configuration
yield self attr_reader :swagger_docs, :swagger_dir_string
def initialize
@swagger_docs = {}
@swagger_dir_string = nil
end
def swagger_doc(path, doc)
@swagger_docs[path] = doc
end
def swagger_dir(dir_string)
@swagger_dir_string = dir_string
end
end end
class << self class << self
@@swagger_docs = {} attr_reader :config
def swagger_doc(path, &block) def configure
@@swagger_docs[path] = block yield config
end end
def swagger_docs def config
Hash[ @config ||= Configuration.new
@@swagger_docs.map do |path, factory|
[ path, factory.call.merge(swagger: '2.0') ]
end
]
end end
end end
end end

View File

@ -6,7 +6,7 @@ module SwaggerRails
isolate_namespace SwaggerRails isolate_namespace SwaggerRails
initializer 'swagger_rails.initialize' do |app| initializer 'swagger_rails.initialize' do |app|
middleware.use SwaggerDocs, File.join(app.root, 'config', 'swagger') middleware.use SwaggerDocs, SwaggerRails.config.swagger_dir_string
middleware.use SwaggerUi, "#{root}/bower_components/swagger-ui/dist" middleware.use SwaggerUi, "#{root}/bower_components/swagger-ui/dist"
end end
end end

View File

@ -49,12 +49,20 @@ module SwaggerRails
end end
def run_test! def run_test!
if metadata.has_key?(:swagger_doc)
swagger_doc = SwaggerRails.config.swagger_docs[metadata[:swagger_doc]]
else
swagger_doc = SwaggerRails.config.swagger_docs.values.first
end
test_visitor = SwaggerRails::TestVisitor.new(swagger_doc)
before do |example| before do |example|
SwaggerRails::TestVisitor.instance.submit_request!(self, example.metadata) test_visitor.submit_request!(self, example.metadata)
end end
it "returns a #{metadata[:response_code]} status" do |example| it "returns a #{metadata[:response_code]} status" do |example|
SwaggerRails::TestVisitor.instance.assert_response!(self, example.metadata) test_visitor.assert_response!(self, example.metadata)
end end
end end
end end

View File

@ -9,9 +9,10 @@ module SwaggerRails
:example_group_finished, :example_group_finished,
:stop :stop
def initialize(output) def initialize(output, config=SwaggerRails.config)
@output = output @output = output
@swagger_docs = SwaggerRails.swagger_docs @swagger_docs = config.swagger_docs
@swagger_dir_string = config.swagger_dir_string
@output.puts 'Generating Swagger Docs ...' @output.puts 'Generating Swagger Docs ...'
end end
@ -20,14 +21,14 @@ module SwaggerRails
metadata = notification.group.metadata metadata = notification.group.metadata
return unless metadata.has_key?(:response_code) return unless metadata.has_key?(:response_code)
swagger_doc = @swagger_docs[metadata[:swagger_doc]] || @swagger_docs.values.first
swagger_data = swagger_data_from(metadata) swagger_data = swagger_data_from(metadata)
swagger_doc = @swagger_docs[metadata[:docs_path]] || @swagger_docs.values.first
swagger_doc.deep_merge!(swagger_data) swagger_doc.deep_merge!(swagger_data)
end end
def stop(notification) def stop(notification)
@swagger_docs.each do |path, doc| @swagger_docs.each do |path, doc|
file_path = File.join(Rails.root, 'config/swagger', path) file_path = File.join(@swagger_dir_string, path)
File.open(file_path, 'w') do |file| File.open(file_path, 'w') do |file|
file.write(JSON.pretty_generate(doc)) file.write(JSON.pretty_generate(doc))
@ -50,11 +51,19 @@ module SwaggerRails
end end
def operation_from(metadata) def operation_from(metadata)
metadata.slice(:summary, :consumes, :produces, :parameters).tap do |operation| {
operation[:responses] = { tags: [ find_root_of(metadata)[:description] ] ,
metadata[:response_code] => metadata[:response] summary: metadata[:summary],
} consumes: metadata[:consumes],
end produces: metadata[:produces],
parameters: metadata[:parameters],
responses: { metadata[:response_code] => metadata[:response] }
}
end
def find_root_of(metadata)
parent = metadata[:parent_example_group]
parent.nil? ? metadata : find_root_of(parent)
end end
end end
end end

View File

@ -1,8 +1,9 @@
require 'singleton'
module SwaggerRails module SwaggerRails
class TestVisitor class TestVisitor
include Singleton
def initialize(swagger_doc)
@swagger_doc = swagger_doc
end
def submit_request!(test, metadata) def submit_request!(test, metadata)
params_data = params_data_for(test, metadata[:parameters]) params_data = params_data_for(test, metadata[:parameters])
@ -35,6 +36,7 @@ module SwaggerRails
path_params_data.each do |param_data| path_params_data.each do |param_data|
path.sub!("\{#{param_data[:name]}\}", param_data[:value].to_s) path.sub!("\{#{param_data[:name]}\}", param_data[:value].to_s)
end end
path.prepend(@swagger_doc[:basePath] || '')
end end
end end

View File

@ -4,9 +4,10 @@
# end # end
require 'rspec/core/rake_task' require 'rspec/core/rake_task'
require 'swagger_rails'
desc 'Generate Swagger JSON files from integration specs' desc 'Generate Swagger JSON files from integration specs'
RSpec::Core::RakeTask.new('swaggerize') do |t| RSpec::Core::RakeTask.new('swaggerize') do |t|
t.pattern = 'spec/integration/**/*_spec.rb' t.pattern = 'spec/requests/**/*_spec.rb, spec/api/**/*_spec.rb, spec/integration/**/*_spec.rb'
t.rspec_opts = [ '--format SwaggerRails::RSpec::Formatter', '--dry-run' ] t.rspec_opts = [ '--format SwaggerRails::RSpec::Formatter', '--dry-run' ]
end end

View File

@ -1,13 +1,16 @@
SwaggerRails.configure do |c| SwaggerRails.configure do |c|
# Define your swagger documents and provide global metadata # Define your swagger documents and provide any global metadata here
# Describe actual operations in your spec/test files # (Individual operations are generated from your spec/test files)
c.swagger_doc 'v1/swagger.json' do c.swagger_doc 'v1/swagger.json',
{ {
swagger: '2.0',
info: { info: {
title: 'API V1', title: 'API V1',
version: 'v1' version: 'v1'
} }
} }
end
# Specify a location to output generated swagger files
c.swagger_dir File.expand_path('../../../swagger', __FILE__)
end end

View File

@ -1,5 +1,5 @@
Rails.application.routes.draw do Rails.application.routes.draw do
resources :blogs, defaults: { :format => :json }
mount SwaggerRails::Engine => '/api-docs' mount SwaggerRails::Engine => '/api-docs'
resources :blogs, defaults: { :format => :json }
end end

View File

@ -1,6 +1,6 @@
require 'rails_helper' require 'rails_helper'
describe 'Blogs API', swagger_doc: 'blogs/v1/swagger.json' do describe 'Blogs API', swagger_doc: 'v1/swagger.json' do
path '/blogs' do path '/blogs' do

View File

@ -0,0 +1,90 @@
{
"swagger": "2.0",
"info": {
"title": "API V1",
"version": "v1"
},
"paths": {
"/blogs": {
"post": {
"tags": [
"Blogs API"
],
"summary": "creates a new blog",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"parameters": [
{
"name": "blog",
"in": "body",
"schema": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"content": {
"type": "string"
}
}
}
}
],
"responses": {
"201": {
"description": "valid request"
},
"422": {
"description": "invalid request"
}
}
},
"get": {
"tags": [
"Blogs API"
],
"summary": "searches existing blogs",
"consumes": null,
"produces": [
"application/json"
],
"parameters": [
],
"responses": {
"200": {
"description": "valid request"
}
}
}
},
"/blogs/{id}": {
"get": {
"tags": [
"Blogs API"
],
"summary": "retreives a specific blog",
"consumes": null,
"produces": [
"application/json"
],
"parameters": [
{
"name": "id",
"in": "path",
"type": "string"
}
],
"responses": {
"200": {
"description": "blog found"
}
}
}
}
}
}

View File

@ -14,7 +14,7 @@ describe SwaggerRails::InstallGenerator do
end end
it 'creates a default swagger directory' do it 'creates a default swagger directory' do
assert_directory('config/swagger/v1') assert_directory('swagger/v1')
end end
it 'creates a swagger_rails initializer' do it 'creates a swagger_rails initializer' do

View File

@ -5,7 +5,8 @@ module SwaggerRails
describe TestVisitor do describe TestVisitor do
let(:test) { spy('test') } let(:test) { spy('test') }
subject { described_class.instance } let(:swagger_doc) { {} }
subject { described_class.new(swagger_doc) }
describe '#submit_request!' do describe '#submit_request!' do
before { subject.submit_request!(test, metadata) } before { subject.submit_request!(test, metadata) }
@ -93,6 +94,21 @@ module SwaggerRails
) )
end end
end end
context 'when a basePath is provided' do
let(:swagger_doc) { { basePath: '/api' } }
let(:metadata) do
{
path_template: '/resource',
http_verb: :get,
parameters: []
}
end
it 'prepends the basePath to the request path' do
expect(test).to have_received(:get).with('/api/resource', {}, {})
end
end
end end
describe '#assert_response' do describe '#assert_response' do