mirror of
https://github.com/ditkrg/rswag.git
synced 2026-01-23 06:16:42 +00:00
Add yaml support (#7)
This commit is contained in:
parent
79304a1bc1
commit
f331e064fd
@ -11,7 +11,7 @@ Currently, this is still a work in progress. It will output Open API 3.0 compati
|
||||
|
||||
OpenApi Rswag creates [Swagger](http://swagger.io) tooling for Rails API's. Generate beautiful API documentation, including a UI to explore and test operations, directly from your rspec integration tests.
|
||||
|
||||
Rswag extends rspec-rails "request specs" with a Swagger-based DSL for describing and testing API operations. You describe your API operations with a succinct, intuitive syntax, and it automaticaly runs the tests. Once you have green tests, run a rake task to auto-generate corresponding Swagger files and expose them as JSON endpoints. Rswag also provides an embedded version of the awesome [swagger-ui](https://github.com/swagger-api/swagger-ui) that's powered by the exposed JSON. This toolchain makes it seamless to go from integration specs, which you're probably doing in some form already, to living documentation for your API consumers.
|
||||
Rswag extends rspec-rails "request specs" with a Swagger-based DSL for describing and testing API operations. You describe your API operations with a succinct, intuitive syntax, and it automaticaly runs the tests. Once you have green tests, run a rake task to auto-generate corresponding Swagger files and expose them as YAML or JSON endpoints. Rswag also provides an embedded version of the awesome [swagger-ui](https://github.com/swagger-api/swagger-ui) that's powered by the exposed file. This toolchain makes it seamless to go from integration specs, which you're probably doing in some form already, to living documentation for your API consumers.
|
||||
|
||||
And that's not all ...
|
||||
|
||||
@ -266,7 +266,7 @@ By default, the paths, operations and responses defined in your spec files will
|
||||
|
||||
```ruby
|
||||
# spec/integration/v2/blogs_spec.rb
|
||||
describe 'Blogs API', swagger_doc: 'v2/swagger.json' do
|
||||
describe 'Blogs API', swagger_doc: 'v2/swagger.yaml' do
|
||||
|
||||
path '/blogs' do
|
||||
...
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
require 'json'
|
||||
require 'yaml'
|
||||
require 'rack/mime'
|
||||
|
||||
module OpenApi
|
||||
module Rswag
|
||||
@ -15,13 +17,15 @@ module OpenApi
|
||||
filename = "#{@config.resolve_swagger_root(env)}/#{path}"
|
||||
|
||||
if env['REQUEST_METHOD'] == 'GET' && File.file?(filename)
|
||||
swagger = load_json(filename)
|
||||
swagger = parse_file(filename)
|
||||
@config.swagger_filter.call(swagger, env) unless @config.swagger_filter.nil?
|
||||
mime = Rack::Mime.mime_type(::File.extname(path), 'text/plain')
|
||||
body = unload_swagger(filename, swagger)
|
||||
|
||||
return [
|
||||
'200',
|
||||
{ 'Content-Type' => 'application/json' },
|
||||
[ JSON.dump(swagger) ]
|
||||
{ 'Content-Type' => 'mine' },
|
||||
[ body ]
|
||||
]
|
||||
end
|
||||
|
||||
@ -30,9 +34,29 @@ module OpenApi
|
||||
|
||||
private
|
||||
|
||||
def parse_file(filename)
|
||||
if /\.ya?ml$/ === filename
|
||||
load_yaml(filename)
|
||||
else
|
||||
load_json(filename)
|
||||
end
|
||||
end
|
||||
|
||||
def load_yaml(filename)
|
||||
YAML.safe_load(File.read(filename))
|
||||
end
|
||||
|
||||
def load_json(filename)
|
||||
JSON.parse(File.read(filename))
|
||||
end
|
||||
|
||||
def unload_swagger(filename, swagger)
|
||||
if /\.ya?ml$/ === filename
|
||||
YAML.dump(swagger)
|
||||
else
|
||||
JSON.dump(swagger)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: API V1
|
||||
version: v1
|
||||
paths: {}
|
||||
@ -76,6 +76,21 @@ module OpenApi::Rswag
|
||||
expect(response[2].join).to include('"host":"tempuri.org"')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a path maps to a yaml swagger file' do
|
||||
let(:env) { env_defaults.merge('PATH_INFO' => 'v1/swagger.yml') }
|
||||
|
||||
it 'returns a 200 status' do
|
||||
expect(response.length).to eql(3)
|
||||
expect(response.first).to eql('200')
|
||||
end
|
||||
|
||||
it 'returns contents of the swagger file' do
|
||||
expect(response.length).to eql(3)
|
||||
expect(response[1]).to include( 'Content-Type' => 'text/yaml')
|
||||
expect(response[2].join).to include('title: API V1')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -32,4 +32,10 @@ RSpec.configure do |config|
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# Specify the format of the output Swagger file when running 'rswag:specs:swaggerize'.
|
||||
# The swagger_docs configuration option has the filename including format in
|
||||
# the key, this may want to be changed to avoid putting yaml in json files.
|
||||
# Defaults to json. Accepts ':json' and ':yaml'.
|
||||
config.swagger_format = :yaml
|
||||
end
|
||||
|
||||
@ -13,6 +13,7 @@ module OpenApi
|
||||
c.add_setting :swagger_root
|
||||
c.add_setting :swagger_docs
|
||||
c.add_setting :swagger_dry_run
|
||||
c.add_setting :swagger_format
|
||||
c.extend ExampleGroupHelpers, type: :request
|
||||
c.include ExampleHelpers, type: :request
|
||||
end
|
||||
|
||||
@ -34,6 +34,14 @@ module OpenApi
|
||||
end
|
||||
end
|
||||
|
||||
def swagger_format
|
||||
@swagger_format ||= begin
|
||||
@rspec_config.swagger_format = :json if @rspec_config.swagger_format.nil? || @rspec_config.swagger_format.empty?
|
||||
raise ConfigurationError, "Unknown swagger_format '#{@rspec_config.swagger_format}'" unless [:json, :yaml].include?(@rspec_config.swagger_format)
|
||||
@rspec_config.swagger_format
|
||||
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]
|
||||
|
||||
@ -60,7 +60,7 @@ module OpenApi
|
||||
FileUtils.mkdir_p dirname unless File.exist?(dirname)
|
||||
|
||||
File.open(file_path, 'w') do |file|
|
||||
file.write(JSON.pretty_generate(doc))
|
||||
file.write(pretty_generate(doc))
|
||||
end
|
||||
|
||||
@output.puts "Swagger doc generated at #{file_path}"
|
||||
@ -69,6 +69,20 @@ module OpenApi
|
||||
|
||||
private
|
||||
|
||||
def pretty_generate(doc)
|
||||
if @config.swagger_format == :yaml
|
||||
clean_doc = yaml_prepare(doc)
|
||||
YAML.dump(clean_doc)
|
||||
else # config errors are thrown in 'def swagger_format', no throw needed here
|
||||
JSON.pretty_generate(doc)
|
||||
end
|
||||
end
|
||||
|
||||
def yaml_prepare(doc)
|
||||
json_doc = JSON.pretty_generate(doc)
|
||||
JSON.parse(json_doc)
|
||||
end
|
||||
|
||||
def metadata_to_swagger(metadata)
|
||||
response_code = metadata[:response][:code]
|
||||
response = metadata[:response].reject { |k, _v| k == :code }
|
||||
|
||||
@ -8,7 +8,9 @@ module OpenApi
|
||||
describe Configuration do
|
||||
subject { described_class.new(rspec_config) }
|
||||
|
||||
let(:rspec_config) { OpenStruct.new(swagger_root: swagger_root, swagger_docs: swagger_docs) }
|
||||
let(:rspec_config) do
|
||||
OpenStruct.new(swagger_root: swagger_root, swagger_docs: swagger_docs, swagger_format: swagger_format)
|
||||
end
|
||||
let(:swagger_root) { 'foobar' }
|
||||
let(:swagger_docs) do
|
||||
{
|
||||
@ -16,6 +18,7 @@ module OpenApi
|
||||
'v2/swagger.json' => { info: { title: 'v2' } }
|
||||
}
|
||||
end
|
||||
let(:swagger_format) { :yaml }
|
||||
|
||||
describe '#swagger_root' do
|
||||
let(:response) { subject.swagger_root }
|
||||
@ -48,6 +51,26 @@ module OpenApi
|
||||
end
|
||||
end
|
||||
|
||||
describe '#swagger_format' do
|
||||
let(:response) { subject.swagger_format }
|
||||
|
||||
context 'provided in rspec config' do
|
||||
it { expect(response).to be_an_instance_of(Symbol) }
|
||||
end
|
||||
|
||||
context 'unsupported format provided' do
|
||||
let(:swagger_format) { :xml }
|
||||
|
||||
it { expect { response }.to raise_error ConfigurationError }
|
||||
end
|
||||
|
||||
context 'not provided' do
|
||||
let(:swagger_format) { nil }
|
||||
|
||||
it { expect(response).to eq(:json) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#get_swagger_doc(tag=nil)' do
|
||||
let(:swagger_doc) { subject.get_swagger_doc(tag) }
|
||||
|
||||
|
||||
@ -55,14 +55,31 @@ module OpenApi
|
||||
'v1/swagger.json' => { info: { version: 'v1' } },
|
||||
'v2/swagger.json' => { info: { version: 'v2' } }
|
||||
)
|
||||
allow(config).to receive(:swagger_format).and_return(swagger_format)
|
||||
subject.stop(notification)
|
||||
end
|
||||
|
||||
let(:notification) { double('notification') }
|
||||
|
||||
context 'with default format' do
|
||||
let(:swagger_format) { :json }
|
||||
|
||||
it 'writes the swagger_doc(s) to file' do
|
||||
expect(File).to exist("#{swagger_root}/v1/swagger.json")
|
||||
expect(File).to exist("#{swagger_root}/v2/swagger.json")
|
||||
expect { JSON.parse(File.read("#{swagger_root}/v2/swagger.json")) }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'with yaml format' do
|
||||
let(:swagger_format) { :yaml }
|
||||
|
||||
it 'writes the swagger_doc(s) as yaml' do
|
||||
expect(File).to exist("#{swagger_root}/v1/swagger.json")
|
||||
expect { JSON.parse(File.read("#{swagger_root}/v1/swagger.json")) }.to raise_error(JSON::ParserError)
|
||||
# Psych::DisallowedClass would be raised if we do not pre-process ruby symbols
|
||||
expect { YAML.safe_load(File.read("#{swagger_root}/v1/swagger.json")) }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
|
||||
@ -2,9 +2,9 @@ OpenApi::Rswag::Ui.configure do |c|
|
||||
|
||||
# List the Swagger endpoints that you want to be documented through the swagger-ui
|
||||
# The first parameter is the path (absolute or relative to the UI host) to the corresponding
|
||||
# JSON endpoint and the second is a title that will be displayed in the document selector
|
||||
# NOTE: If you're using rspec-api to expose Swagger files (under swagger_root) as JSON endpoints,
|
||||
# endpoint and the second is a title that will be displayed in the document selector
|
||||
# NOTE: If you're using rspec-api to expose Swagger files (under swagger_root) as YAML or JSON endpoints,
|
||||
# then the list below should correspond to the relative paths for those endpoints
|
||||
|
||||
c.swagger_endpoint '/api-docs/v1/swagger.json', 'API V1 Docs'
|
||||
c.swagger_endpoint '/api-docs/v1/swagger.yaml', 'API V1 Docs'
|
||||
end
|
||||
|
||||
Loading…
Reference in New Issue
Block a user