mirror of
https://github.com/ditkrg/rswag.git
synced 2026-01-25 07:16:40 +00:00
Honor basePath, output tags & other cleanup
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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 ...
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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],
|
||||||
|
produces: metadata[:produces],
|
||||||
|
parameters: metadata[:parameters],
|
||||||
|
responses: { metadata[:response_code] => metadata[:response] }
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def find_root_of(metadata)
|
||||||
|
parent = metadata[:parent_example_group]
|
||||||
|
parent.nil? ? metadata : find_root_of(parent)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
90
spec/dummy/swagger/v1/swagger.json
Normal file
90
spec/dummy/swagger/v1/swagger.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user