17 Commits

Author SHA1 Message Date
richie
77d4cbe0ea Prep for v1.2.0 release 2017-01-08 07:51:24 -08:00
domaindrivendev
32a7ab8234 Merge pull request #40 from PavelBezpalov/master
Add missing in production assets
2017-01-05 08:16:10 -08:00
Pavel Bezpalov
95b009a72f Add missing in production assets 2017-01-04 16:38:12 +02:00
richie
471dff5e34 Prep for v1.1.0 release 2016-11-08 12:43:31 -08:00
richie
99be8135f7 Wire up capybara & add simple feature spec for swagger-ui 2016-11-08 12:38:51 -08:00
richie
8315eda8b2 Update ui to use digest assets in prod 2016-11-02 22:15:09 -07:00
domaindrivendev
e1fe9f3239 Merge pull request #33 from vinh0604/master
Simplify response header validation
2016-10-19 17:15:50 -07:00
vinhbachsy
64b0de494f Simplify response header validation
Change to checks for the presence of required headers instead of using 
JSON::Validator
2016-10-20 01:33:43 +08:00
domaindrivendev
9d4069bcfe A minor readme update 2016-10-19 00:37:06 -07:00
domaindrivendev
17a6cd13c4 Merge pull request #32 from vinh0604/master
Validating response headers and setting response examples
2016-10-19 00:09:48 -07:00
vinhbachsy
0b0acfe4c7 Rename response_examples to examples for consistent DSL
Special handling `examples` invocation with no parameters to avoid
overriding the `examples` method of rspec-core ExampleGroup
2016-10-19 03:04:03 +08:00
vinhbachsy
5ea97a4278 Update README for response headers and examples 2016-10-18 21:46:35 +08:00
vinhbachsy
5cf376891a Validate response headers based on specified header
Add validate_headers step in response validator.
Using JSON::Validator with validate header value with swagger header 
object.
2016-10-18 21:46:35 +08:00
vinhbachsy
10dd37896f Support setting examples for response
Add helper method `response_examples` to inject response examples to swagger
2016-10-18 21:46:35 +08:00
richie
3506fee3d0 Prep for 1.0.3 release 2016-10-17 14:50:57 -07:00
richie
5df130922f Support x-nullable in respone_validator 2016-10-17 14:47:11 -07:00
richie
5a19cd2373 Push latest Gemfile.lock 2016-10-14 17:50:42 -07:00
26 changed files with 364 additions and 67 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
**/*/log **/*/log
**/*/*.gem **/*/*.gem
**/*/*.sqlite3 **/*/*.sqlite3
**/*/public/assets

View File

@@ -7,4 +7,8 @@ env:
- "RAILS_VERSION=5.0.0" - "RAILS_VERSION=5.0.0"
cache: bundler cache: bundler
install: bundle update install: bundle update
before_script:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
- sleep 3 # give xvfb some time to start
script: ./ci/test.sh script: ./ci/test.sh

View File

@@ -25,5 +25,12 @@ group :test do
gem 'test-unit' gem 'test-unit'
gem 'rspec-rails' gem 'rspec-rails'
gem 'generator_spec' gem 'generator_spec'
gem 'capybara'
gem 'capybara-webkit'
gem 'rswag-specs', path: './rswag-specs' gem 'rswag-specs', path: './rswag-specs'
end end
group :assets do
gem 'uglifier'
gem 'therubyracer'
end

View File

@@ -1,13 +1,13 @@
PATH PATH
remote: ./rswag-api remote: ./rswag-api
specs: specs:
rswag-api (1.0.1) rswag-api (1.2.0)
rails (>= 3.1, < 5.1) rails (>= 3.1, < 5.1)
PATH PATH
remote: ./rswag-specs remote: ./rswag-specs
specs: specs:
rswag-specs (1.0.1) rswag-specs (1.2.0)
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.1) rswag-ui (1.2.0)
rails (>= 3.1, < 5.1) rails (>= 3.1, < 5.1)
GEM GEM
@@ -51,8 +51,19 @@ GEM
addressable (2.4.0) addressable (2.4.0)
arel (3.0.3) arel (3.0.3)
builder (3.0.4) builder (3.0.4)
capybara (2.10.1)
addressable
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
capybara-webkit (1.1.0)
capybara (~> 2.0, >= 2.0.2)
json
diff-lcs (1.2.5) diff-lcs (1.2.5)
erubis (2.7.0) erubis (2.7.0)
execjs (2.7.0)
generator_spec (0.9.3) generator_spec (0.9.3)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
railties (>= 3.0.0) railties (>= 3.0.0)
@@ -62,11 +73,15 @@ GEM
json (1.8.3) json (1.8.3)
json-schema (2.7.0) json-schema (2.7.0)
addressable (>= 2.4) addressable (>= 2.4)
libv8 (3.16.14.15)
mail (2.5.4) mail (2.5.4)
mime-types (~> 1.16) mime-types (~> 1.16)
treetop (~> 1.4.8) treetop (~> 1.4.8)
mime-types (1.25.1) mime-types (1.25.1)
mini_portile2 (2.1.0)
multi_json (1.12.1) multi_json (1.12.1)
nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0)
polyglot (0.3.5) polyglot (0.3.5)
power_assert (0.3.1) power_assert (0.3.1)
rack (1.4.7) rack (1.4.7)
@@ -94,6 +109,7 @@ GEM
rake (11.3.0) rake (11.3.0)
rdoc (3.12.2) rdoc (3.12.2)
json (~> 1.4) json (~> 1.4)
ref (2.0.0)
rspec-core (3.5.4) rspec-core (3.5.4)
rspec-support (~> 3.5.0) rspec-support (~> 3.5.0)
rspec-expectations (3.5.0) rspec-expectations (3.5.0)
@@ -124,17 +140,26 @@ GEM
railties (~> 3.0) railties (~> 3.0)
test-unit (3.2.1) test-unit (3.2.1)
power_assert power_assert
therubyracer (0.12.2)
libv8 (~> 3.16.14.0)
ref
thor (0.19.1) thor (0.19.1)
tilt (1.4.1) tilt (1.4.1)
treetop (1.4.15) treetop (1.4.15)
polyglot polyglot
polyglot (>= 0.3.1) polyglot (>= 0.3.1)
tzinfo (0.3.51) tzinfo (0.3.51)
uglifier (3.0.2)
execjs (>= 0.3.0, < 3)
xpath (2.0.0)
nokogiri (~> 1.3)
PLATFORMS PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
capybara
capybara-webkit
generator_spec generator_spec
rails (= 3.2.22) rails (= 3.2.22)
rspec-rails rspec-rails
@@ -144,3 +169,5 @@ DEPENDENCIES
sqlite3 sqlite3
strong_parameters strong_parameters
test-unit test-unit
therubyracer
uglifier

View File

@@ -127,7 +127,7 @@ In addition to paths, operations and responses, Swagger also supports global API
# spec/swagger_helper.rb # spec/swagger_helper.rb
RSpec.configure do |config| RSpec.configure do |config|
config.swagger_root = Rails.root.to_s + '/swagger' config.swagger_root = Rails.root.to_s + '/swagger'
config.swagger_docs = { config.swagger_docs = {
'v1/swagger.json' => { 'v1/swagger.json' => {
swagger: '2.0', swagger: '2.0',
@@ -171,8 +171,8 @@ The steps described above will get you up and running with minimal setup. Howeve
|Gem|Description|Added/Updated| |Gem|Description|Added/Updated|
|---------|-----------|-------------| |---------|-----------|-------------|
|__rswag-specs__|Swagger-based DSL for rspec & accompanying rake task for generating Swagger files|_spec/swagger_helper.rb_| |__rswag-specs__|Swagger-based DSL for rspec & accompanying rake task for generating Swagger files|_spec/swagger_helper.rb_|
|__rswag-api__ |Rails Engine that exposes the Swagger files as JSON endpoints|_config/initializers/rswag-api.rb, config/routes.rb_| |__rswag-api__ |Rails Engine that exposes your Swagger files as JSON endpoints|_config/initializers/rswag-api.rb, config/routes.rb_|
|__rswag-ui__ |Rails Engine that includes [swagger-ui](https://github.com/swagger-api/swagger-ui) and powers it from the Swagger endpoints|_config/initializers/rswag-ui.rb, config/routes.rb_| |__rswag-ui__ |Rails Engine that includes [swagger-ui](https://github.com/swagger-api/swagger-ui) and powers it from your Swagger endpoints|_config/initializers/rswag-ui.rb, config/routes.rb_|
### Output Location for Generated Swagger Files ### ### Output Location for Generated Swagger Files ###
@@ -224,25 +224,66 @@ describe 'Blogs API' do
path '/blogs' do path '/blogs' do
post 'Creates a blog' do post 'Creates a blog' do
response 422, 'invalid request' do response 422, 'invalid request' do
schema '$ref' => '#/definitions/errors_object' schema '$ref' => '#/definitions/errors_object'
... ...
end end
# spec/integration/comments_spec.rb # spec/integration/comments_spec.rb
describe 'Blogs API' do describe 'Blogs API' do
path '/blogs/{blog_id}/comments' do path '/blogs/{blog_id}/comments' do
post 'Creates a comment' do post 'Creates a comment' do
response 422, 'invalid request' do response 422, 'invalid request' do
schema '$ref' => '#/definitions/errors_object' schema '$ref' => '#/definitions/errors_object'
... ...
end end
``` ```
### Response headers ###
In Rswag, you could use `header` method inside the response block to specify header objects for this response. Rswag will validate your response headers with those header objects and inject them into the generated swagger file:
```ruby
# spec/integration/comments_spec.rb
describe 'Blogs API' do
path '/blogs/{blog_id}/comments' do
post 'Creates a comment' do
response 422, 'invalid request' do
header 'X-Rate-Limit-Limit', type: :integer, description: 'The number of allowed requests in the current period'
header 'X-Rate-Limit-Remaining', type: :integer, description: 'The number of remaining requests in the current period'
...
end
```
### Response examples ###
You can provide custom response examples to the generated swagger file by calling the method `examples` inside the response block:
```ruby
# spec/integration/blogs_spec.rb
describe 'Blogs API' do
path '/blogs/{blog_id}' do
get 'Retrieves a blog' do
response 200, 'blog found' do
examples 'application/json' => {
id: 1,
title: 'Hello world!',
content: '...'
}
...
end
```
### Route Prefix for Swagger JSON Endpoints ### ### Route Prefix for Swagger JSON Endpoints ###
The functionality to expose Swagger files, such as those generated by rswag-specs, as JSON endpoints is implemented as a Rails Engine. As with any Engine, you can change it's mount prefix in _routes.rb_: The functionality to expose Swagger files, such as those generated by rswag-specs, as JSON endpoints is implemented as a Rails Engine. As with any Engine, you can change it's mount prefix in _routes.rb_:
@@ -273,7 +314,7 @@ end
``` ```
__NOTE__: If you're using rswag-specs to generate Swagger files, you'll want to ensure they both use the same &lt;swagger_root&gt;. The reason for separate settings is to maintain independence between the two gems. For example, you could install rswag-api independently and create your Swagger files manually. __NOTE__: If you're using rswag-specs to generate Swagger files, you'll want to ensure they both use the same &lt;swagger_root&gt;. The reason for separate settings is to maintain independence between the two gems. For example, you could install rswag-api independently and create your Swagger files manually.
### Dynamic Values for Swagger JSON ## ### Dynamic Values for Swagger JSON ##
There may be cases where you need to add dynamic values to the Swagger JSON that's returned by rswag-api. For example, you may want to provide an explicit host name. Rather than hardcoding it, you can configure a filter that's executed prior to serializing every Swagger document: There may be cases where you need to add dynamic values to the Swagger JSON that's returned by rswag-api. For example, you may want to provide an explicit host name. Rather than hardcoding it, you can configure a filter that's executed prior to serializing every Swagger document:
@@ -287,7 +328,7 @@ end
``` ```
Note how the filter is passed the rack env for the current request. This provides a lot of flexibilty. For example, you can assign the "host" property (as shown) or you could inspect session information or an Authoriation header and remove operations based on user permissions. Note how the filter is passed the rack env for the current request. This provides a lot of flexibilty. For example, you can assign the "host" property (as shown) or you could inspect session information or an Authoriation header and remove operations based on user permissions.
### Enable Swagger Endpoints for swagger-ui ### ### Enable Swagger Endpoints for swagger-ui ###
You can update the _rswag-ui.rb_ initializer, installed with rswag-ui, to specify which Swagger endpoints should be available to power the documentation UI. If you're using rswag-api, these should correspond to the Swagger endpoints it exposes. When the UI is rendered, you'll see these listed in a drop-down to the top right of the page: You can update the _rswag-ui.rb_ initializer, installed with rswag-ui, to specify which Swagger endpoints should be available to power the documentation UI. If you're using rswag-api, these should correspond to the Swagger endpoints it exposes. When the UI is rendered, you'll see these listed in a drop-down to the top right of the page:

View File

@@ -1,5 +1,5 @@
module Rswag module Rswag
module Api module Api
VERSION = '1.0.2' VERSION = '1.2.0'
end end
end end

View File

@@ -25,7 +25,7 @@ module Rswag
# functionality while also setting the appropriate metadata if applicable # functionality while also setting the appropriate metadata if applicable
def description(value=nil) def description(value=nil)
return super() if value.nil? return super() if value.nil?
metadata[:operation][:description] = value metadata[:operation][:description] = value
end end
# These are array properties - note the splat operator # These are array properties - note the splat operator
@@ -61,6 +61,14 @@ module Rswag
metadata[:response][:headers][name] = attributes metadata[:response][:headers][name] = attributes
end end
# NOTE: Similar to 'description', 'examples' need to handle the case when
# being invoked with no params to avoid overriding 'examples' method of
# rspec-core ExampleGroup
def examples(example = nil)
return super() if example.nil?
metadata[:response][:examples] = example
end
def run_test! def run_test!
# NOTE: rspec 2.x support # NOTE: rspec 2.x support
if RSPEC_VERSION < 3 if RSPEC_VERSION < 3

View File

@@ -0,0 +1,25 @@
require 'json-schema'
module Rswag
module Specs
class ExtendedSchema < JSON::Schema::Validator
def initialize
super
extend_schema_definition("http://json-schema.org/draft-04/schema#")
@attributes['type'] = ExtendedTypeAttribute
@uri = URI.parse('http://tempuri.org/rswag/specs/extended_schema')
end
end
class ExtendedTypeAttribute < JSON::Schema::TypeV4Attribute
def self.validate(current_schema, data, fragments, processor, validator, options={})
return if data.nil? && current_schema.schema['x-nullable'] == true
super
end
end
JSON::Validator.register_validator(ExtendedSchema.new)
end
end

View File

@@ -1,4 +1,6 @@
require 'active_support/core_ext/hash/slice'
require 'json-schema' require 'json-schema'
require 'rswag/specs/extended_schema'
module Rswag module Rswag
module Specs module Specs
@@ -11,6 +13,7 @@ module Rswag
def validate!(response) def validate!(response)
validate_code!(response.code) validate_code!(response.code)
validate_headers!(response.headers)
validate_body!(response.body) validate_body!(response.body)
end end
@@ -22,13 +25,26 @@ module Rswag
end end
end end
def validate_headers!(headers)
header_schema = @api_metadata[:response][:headers]
return if header_schema.nil?
header_schema.keys.each do |header_name|
raise UnexpectedResponse, "Expected response header #{header_name} to be present" if headers[header_name.to_s].nil?
end
end
def validate_body!(body) def validate_body!(body)
schema = @api_metadata[:response][:schema] response_schema = @api_metadata[:response][:schema]
return if schema.nil? return if response_schema.nil?
begin begin
JSON::Validator.validate!(schema.merge(@global_metadata), body) validation_schema = response_schema
.merge('$schema' => 'http://tempuri.org/rswag/specs/extended_schema')
.merge(@global_metadata.slice(:definitions))
JSON::Validator.validate!(validation_schema, body)
rescue JSON::Schema::ValidationError => ex rescue JSON::Schema::ValidationError => ex
raise UnexpectedResponse, "Expected response body to match schema: #{ex.message}" raise UnexpectedResponse, "Expected response body to match schema: #{ex.message}"
end end
end end
end end

View File

@@ -1,5 +1,5 @@
module Rswag module Rswag
module Specs module Specs
VERSION = '1.0.2' VERSION = '1.2.0'
end end
end end

View File

@@ -113,7 +113,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: {} } } 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(
[ name: :id, in: :path, required: true ] [ name: :id, in: :path, required: true ]
@@ -151,6 +151,25 @@ module Rswag
) )
end end
end end
describe '#examples(example)' do
let(:json_example) do
{
'application/json' => {
foo: 'bar'
}
}
end
let(:api_metadata) { { response: {} } }
before do
subject.examples(json_example)
end
it "adds to the 'response examples' metadata" do
expect(api_metadata[:response][:examples]).to eq(json_example)
end
end
end end
end end
end end

View File

@@ -66,6 +66,42 @@ module Rswag
it { expect { call }.to raise_error UnexpectedResponse } it { expect { call }.to raise_error UnexpectedResponse }
end end
end end
context "'headers' provided" do
before do
api_metadata[:response][:headers] = {
'X-Rate-Limit-Limit' => {
description: 'The number of allowed requests in the current period',
type: 'integer'
},
'X-Rate-Limit-Remaining' => {
description: 'The number of remaining requests in the current period',
type: 'integer'
},
'X-Rate-Limit-Reset' => {
description: 'The number of seconds left in the current period',
type: 'integer'
}
}
end
context 'response code & body matches' do
let(:response) { OpenStruct.new(code: 200, body: '{}', headers: {
'X-Rate-Limit-Limit' => 1,
'X-Rate-Limit-Remaining' => 1,
'X-Rate-Limit-Reset' => 1
}) }
it { expect { call }.to_not raise_error }
end
context 'response code matches & body does not' do
let(:response) { OpenStruct.new(code: 200, body: '{}', headers: {
'X-Rate-Limit-Limit' => 1,
'X-Rate-Limit-Remaining' => 1
}) }
it { expect { call }.to raise_error UnexpectedResponse }
end
end
end end
end end
end end

View File

@@ -3,33 +3,33 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Swagger UI</title> <title>Swagger UI</title>
<link rel="icon" type="image/png" href="/assets/swagger-ui/images/favicon-32x32.png" sizes="32x32" /> <link rel="icon" type="image/png" href="<%= asset_path 'swagger-ui/images/favicon-32x32.png' %>" sizes="32x32" />
<link rel="icon" type="image/png" href="/assets/swagger-ui/images/favicon-16x16.png" sizes="16x16" /> <link rel="icon" type="image/png" href="<%= asset_path 'swagger-ui/images/favicon-16x16.png' %>" sizes="16x16" />
<link href='/assets/swagger-ui/css/typography.css' media='screen' rel='stylesheet' type='text/css'/> <link href="<%= asset_path 'swagger-ui/css/typography.css' %>" media='screen' rel='stylesheet' type='text/css'/>
<link href='/assets/swagger-ui/css/reset.css' media='screen' rel='stylesheet' type='text/css'/> <link href="<%= asset_path 'swagger-ui/css/reset.css' %>" media='screen' rel='stylesheet' type='text/css'/>
<link href='/assets/swagger-ui/css/screen.css' media='screen' rel='stylesheet' type='text/css'/> <link href="<%= asset_path 'swagger-ui/css/screen.css' %>" media='screen' rel='stylesheet' type='text/css'/>
<link href='/assets/swagger-ui/css/reset.css' media='print' rel='stylesheet' type='text/css'/> <link href="<%= asset_path 'swagger-ui/css/reset.css' %>" media='print' rel='stylesheet' type='text/css'/>
<link href='/assets/swagger-ui/css/print.css' media='print' rel='stylesheet' type='text/css'/> <link href="<%= asset_path 'swagger-ui/css/print.css' %>" media='print' rel='stylesheet' type='text/css'/>
<script src='/assets/swagger-ui/lib/object-assign-pollyfill.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/lib/object-assign-pollyfill.js' %>" type='text/javascript'></script>
<script src='/assets/swagger-ui/lib/jquery-1.8.0.min.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/lib/jquery-1.8.0.min.js' %>" type='text/javascript'></script>
<script src='/assets/swagger-ui/lib/jquery.slideto.min.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/lib/jquery.slideto.min.js' %>" type='text/javascript'></script>
<script src='/assets/swagger-ui/lib/jquery.wiggle.min.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/lib/jquery.wiggle.min.js' %>" type='text/javascript'></script>
<script src='/assets/swagger-ui/lib/jquery.ba-bbq.min.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/lib/jquery.ba-bbq.min.js' %>" type='text/javascript'></script>
<script src='/assets/swagger-ui/lib/handlebars-4.0.5.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/lib/handlebars-4.0.5.js' %>" type='text/javascript'></script>
<script src='/assets/swagger-ui/lib/lodash.min.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/lib/lodash.min.js' %>" type='text/javascript'></script>
<script src='/assets/swagger-ui/lib/backbone-min.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/lib/backbone-min.js' %>" type='text/javascript'></script>
<script src='/assets/swagger-ui/swagger-ui.min.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/swagger-ui.min.js' %>" type='text/javascript'></script>
<script src='/assets/swagger-ui/lib/highlight.9.1.0.pack.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/lib/highlight.9.1.0.pack.js' %>" type='text/javascript'></script>
<script src='/assets/swagger-ui/lib/highlight.9.1.0.pack_extended.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/lib/highlight.9.1.0.pack_extended.js' %>" type='text/javascript'></script>
<script src='/assets/swagger-ui/lib/jsoneditor.min.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/lib/jsoneditor.min.js' %>" type='text/javascript'></script>
<script src='/assets/swagger-ui/lib/marked.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/lib/marked.js' %>" type='text/javascript'></script>
<script src='/assets/swagger-ui/lib/swagger-oauth.js' type='text/javascript'></script> <script src="<%= asset_path 'swagger-ui/lib/swagger-oauth.js' %>" type='text/javascript'></script>
<!-- Some basic translations --> <!-- Some basic translations -->
<!-- <script src='/assets/swagger-ui/lang/translator.js' type='text/javascript'></script> --> <!-- <script src="<%= asset_path 'swagger-ui/lang/translator.js' %>" type='text/javascript'></script> -->
<!-- <script src='/assets/swagger-ui/lang/ru.js' type='text/javascript'></script> --> <!-- <script src="<%= asset_path 'swagger-ui/lang/ru.js' %>" type='text/javascript'></script> -->
<!-- <script src='/assets/swagger-ui/lang/en.js' type='text/javascript'></script> --> <!-- <script src="<%= asset_path 'swagger-ui/lang/en.js' %>" type='text/javascript'></script> -->
<script type="text/javascript"> <script type="text/javascript">
$(function () { $(function () {
@@ -84,7 +84,7 @@
<body class="swagger-section"> <body class="swagger-section">
<div id='header'> <div id='header'>
<div class="swagger-ui-wrap"> <div class="swagger-ui-wrap">
<a id="logo" href="http://swagger.io"><img class="logo__img" alt="swagger" height="30" width="30" src="/assets/swagger-ui/images/logo_small.png" /><span class="logo__title">swagger</span></a> <a id="logo" href="http://swagger.io"><img class="logo__img" alt="swagger" height="30" width="30" src="<%= asset_path 'swagger-ui/images/logo_small.png' %>" /><span class="logo__title">swagger</span></a>
<form id='api_selector'> <form id='api_selector'>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/></div> <div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/></div>
<div id='auth_container'></div> <div id='auth_container'></div>

View File

@@ -1,5 +1,5 @@
module Rswag module Rswag
module Ui module Ui
VERSION = '1.0.2' VERSION = '1.2.0'
end end
end end

View File

@@ -880,7 +880,7 @@
padding: 6px 8px; padding: 6px 8px;
} }
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber { .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber {
background-image: url('../images/throbber.gif'); background-image: url(<%= asset_path('swagger-ui/images/throbber.gif') %>);
width: 128px; width: 128px;
height: 16px; height: 16px;
display: block; display: block;
@@ -1222,7 +1222,7 @@
height: 18px; height: 18px;
vertical-align: middle; vertical-align: middle;
display: inline-block; display: inline-block;
background: url(../images/explorer_icons.png) no-repeat; background: url(<%= asset_path('swagger-ui/images/explorer_icons.png') %>) no-repeat;
} }
.swagger-section .authorize__btn_operation_login { .swagger-section .authorize__btn_operation_login {
background-position: 0 0; background-position: 0 0;

View File

@@ -880,7 +880,7 @@
padding: 6px 8px; padding: 6px 8px;
} }
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber { .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber {
background-image: url('../images/throbber.gif'); background-image: url(<%= asset_path('swagger-ui/images/throbber.gif') %>);
width: 128px; width: 128px;
height: 16px; height: 16px;
display: block; display: block;
@@ -1222,7 +1222,7 @@
height: 18px; height: 18px;
vertical-align: middle; vertical-align: middle;
display: inline-block; display: inline-block;
background: url(../images/explorer_icons.png) no-repeat; background: url(<%= asset_path('swagger-ui/images/explorer_icons.png') %>) no-repeat;
} }
.swagger-section .authorize__btn_operation_login { .swagger-section .authorize__btn_operation_login {
background-position: 0 0; background-position: 0 0;
@@ -1354,7 +1354,7 @@
height: 18px; height: 18px;
vertical-align: middle; vertical-align: middle;
display: inline-block; display: inline-block;
background: url(../images/explorer_icons.png) no-repeat; background: url(<%= asset_path('swagger-ui/images/explorer_icons.png') %>) no-repeat;
} }
.swagger-section .api-ic .api_information_panel { .swagger-section .api-ic .api_information_panel {
position: relative; position: relative;

View File

@@ -2,7 +2,7 @@
font-size: 1.5em; font-size: 1.5em;
font-weight: bold; font-weight: bold;
text-decoration: none; text-decoration: none;
background: transparent url(../images/logo.png) no-repeat left center; background: transparent url(<%= asset_path('swagger-ui/images/logo.png') %>) no-repeat left center;
padding: 20px 0 20px 40px; padding: 20px 0 20px 40px;
} }
#text-head { #text-head {
@@ -64,7 +64,7 @@ h1 {
width: 1500px; width: 1500px;
margin: auto; margin: auto;
margin-top: 0; margin-top: 0;
background-image: url('../images/shield.png'); background-image: url(<%= asset_path('swagger-ui/images/shield.png') %>);
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: -40px -20px; background-position: -40px -20px;
margin-bottom: 210px; margin-bottom: 210px;

View File

@@ -1,14 +0,0 @@
/* Google Font's Droid Sans */
@font-face {
font-family: 'Droid Sans';
font-style: normal;
font-weight: 400;
src: local('Droid Sans'), local('DroidSans'), url('../fonts/DroidSans.ttf'), format('truetype');
}
/* Google Font's Droid Sans Bold */
@font-face {
font-family: 'Droid Sans';
font-style: normal;
font-weight: 700;
src: local('Droid Sans Bold'), local('DroidSans-Bold'), url('../fonts/DroidSans-Bold.ttf'), format('truetype');
}

View File

@@ -0,0 +1,14 @@
/* Google Font's Droid Sans */
@font-face {
font-family: 'Droid Sans';
font-style: normal;
font-weight: 400;
src: local('Droid Sans'), local('DroidSans'), url(<%= asset_path('swagger-ui/fonts/DroidSans.ttf') %>), format('truetype');
}
/* Google Font's Droid Sans Bold */
@font-face {
font-family: 'Droid Sans';
font-style: normal;
font-weight: 700;
src: local('Droid Sans Bold'), local('DroidSans-Bold'), url(<%= asset_path('swagger-ui/fonts/DroidSans-Bold.ttf') %>), format('truetype');
}

View File

@@ -1,3 +1,3 @@
module Rswag module Rswag
VERSION = '1.0.2' VERSION = '1.2.0'
end end

View File

@@ -17,6 +17,10 @@ class BlogsController < ApplicationController
# GET /blogs/1 # GET /blogs/1
def show def show
@blog = Blog.find_by_id(params[:id]) @blog = Blog.find_by_id(params[:id])
fresh_when(@blog)
return unless stale?(@blog)
respond_with @blog, status: :not_found and return unless @blog respond_with @blog, status: :not_found and return unless @blog
respond_with @blog respond_with @blog
end end

View File

@@ -0,0 +1,67 @@
TestApp::Application.configure do
# Settings specified here will take precedence over those in config/application.rb
# Code is not reloaded between requests
config.cache_classes = true
# Full error reports are disabled and caching is turned on
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
# Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_assets = true
# Compress JavaScripts and CSS
config.assets.compress = true
# Don't fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = false
# Generate digests for assets URLs
config.assets.digest = true
# Defaults to nil and saved in location specified by config.assets.prefix
# config.assets.manifest = YOUR_PATH
# Specifies the header that your server uses for sending files
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
# See everything in the log (default is :info)
# config.log_level = :debug
# Prepend all log lines with the following tags
# config.log_tags = [ :subdomain, :uuid ]
# Use a different logger for distributed setups
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
# Use a different cache store in production
# config.cache_store = :mem_cache_store
# Enable serving of images, stylesheets, and JavaScripts from an asset server
# config.action_controller.asset_host = "http://assets.example.com"
# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
# config.assets.precompile += %w( search.js )
# Disable delivery errors, bad email addresses will be ignored
# config.action_mailer.raise_delivery_errors = false
# Enable threaded mode
# config.threadsafe!
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation can not be found)
config.i18n.fallbacks = true
# Send deprecation notices to registered listeners
config.active_support.deprecation = :notify
# Log the query plan for queries taking more than this (works
# with SQLite, MySQL, and PostgreSQL)
# config.active_record.auto_explain_threshold_in_seconds = 0.5
end

View File

@@ -0,0 +1,12 @@
require 'rails_helper'
feature 'swagger-ui', js: true do
scenario 'browsing api-docs' do
visit '/api-docs'
expect(page).to have_content('GET /blogs Searches blogs')
expect(page).to have_content('POST /blogs Creates a blog')
expect(page).to have_content('GET /blogs/{id} Retrieves a blog')
end
end

View File

@@ -50,8 +50,18 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
produces 'application/json' produces 'application/json'
response '200', 'blog found' do response '200', 'blog found' do
header 'ETag', type: :string
header 'Last-Modified', type: :string
header 'Cache-Control', type: :string
schema '$ref' => '#/definitions/blog' schema '$ref' => '#/definitions/blog'
examples 'application/json' => {
id: 1,
title: 'Hello world!',
content: 'Hello world and hello universe. Thank you all very much!!!'
}
let(:blog) { Blog.create(title: 'foo', content: 'bar') } let(:blog) { Blog.create(title: 'foo', content: 'bar') }
let(:id) { blog.id } let(:id) { blog.id }
run_test! run_test!

View File

@@ -50,4 +50,6 @@ RSpec.configure do |config|
config.filter_rails_from_backtrace! config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via: # arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name") # config.filter_gems_from_backtrace("gem name")
Capybara.javascript_driver = :webkit
end end

View File

@@ -89,8 +89,26 @@
"responses": { "responses": {
"200": { "200": {
"description": "blog found", "description": "blog found",
"headers": {
"ETag": {
"type": "string"
},
"Last-Modified": {
"type": "string"
},
"Cache-Control": {
"type": "string"
}
},
"schema": { "schema": {
"$ref": "#/definitions/blog" "$ref": "#/definitions/blog"
},
"examples": {
"application/json": {
"id": 1,
"title": "Hello world!",
"content": "Hello world and hello universe. Thank you all very much!!!"
}
} }
}, },
"404": { "404": {