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

View File

@ -1,7 +1,7 @@
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 ...

View File

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

View File

@ -6,7 +6,7 @@ module SwaggerRails
source_root File.expand_path('../templates', __FILE__)
def add_swagger_dir
empty_directory('config/swagger/v1')
empty_directory('swagger/v1')
end
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|
# Define your swagger documents and provide global metadata
# Describe actual operations in your spec/test files
c.swagger_doc 'v1/swagger.json' do
# Define your swagger documents and provide any global metadata here
# (Individual operations are generated from your spec/test files)
c.swagger_doc 'v1/swagger.json',
{
swagger: '2.0',
info: {
title: 'API V1',
version: 'v1'
}
}
end
# Specify a location to output generated swagger files
c.swagger_dir File.expand_path('../../../swagger', __FILE__)
end

View File

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

View File

@ -6,7 +6,7 @@ module SwaggerRails
isolate_namespace SwaggerRails
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"
end
end

View File

@ -49,12 +49,20 @@ module SwaggerRails
end
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|
SwaggerRails::TestVisitor.instance.submit_request!(self, example.metadata)
test_visitor.submit_request!(self, example.metadata)
end
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

View File

@ -9,9 +9,10 @@ module SwaggerRails
:example_group_finished,
:stop
def initialize(output)
def initialize(output, config=SwaggerRails.config)
@output = output
@swagger_docs = SwaggerRails.swagger_docs
@swagger_docs = config.swagger_docs
@swagger_dir_string = config.swagger_dir_string
@output.puts 'Generating Swagger Docs ...'
end
@ -20,14 +21,14 @@ module SwaggerRails
metadata = notification.group.metadata
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_doc = @swagger_docs[metadata[:docs_path]] || @swagger_docs.values.first
swagger_doc.deep_merge!(swagger_data)
end
def stop(notification)
@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.write(JSON.pretty_generate(doc))
@ -50,11 +51,19 @@ module SwaggerRails
end
def operation_from(metadata)
metadata.slice(:summary, :consumes, :produces, :parameters).tap do |operation|
operation[:responses] = {
metadata[:response_code] => metadata[:response]
}
end
{
tags: [ find_root_of(metadata)[:description] ] ,
summary: metadata[:summary],
consumes: metadata[:consumes],
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

View File

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

View File

@ -4,9 +4,10 @@
# end
require 'rspec/core/rake_task'
require 'swagger_rails'
desc 'Generate Swagger JSON files from integration specs'
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' ]
end

View File

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

View File

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

View File

@ -1,6 +1,6 @@
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

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
it 'creates a default swagger directory' do
assert_directory('config/swagger/v1')
assert_directory('swagger/v1')
end
it 'creates a swagger_rails initializer' do

View File

@ -5,7 +5,8 @@ module SwaggerRails
describe TestVisitor do
let(:test) { spy('test') }
subject { described_class.instance }
let(:swagger_doc) { {} }
subject { described_class.new(swagger_doc) }
describe '#submit_request!' do
before { subject.submit_request!(test, metadata) }
@ -93,6 +94,21 @@ module SwaggerRails
)
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
describe '#assert_response' do