40 Commits

Author SHA1 Message Date
domaindrivendev
741f0dc240 Merge branch 'ci-updates' 2017-07-10 23:55:51 -07:00
domaindrivendev
3404fa72aa Publish gems via CI on tagged builds of master 2017-07-10 23:52:51 -07:00
domaindrivendev
521ab14eaf Merge pull request #72 from domaindrivendev/minimal-dependencies
Reduces dependencies to just the required Rails components
2017-06-28 09:18:32 -07:00
domaindrivendev
8f5cb1aa12 Reduce depencencies to require Rails components only 2017-06-28 09:01:07 -07:00
domaindrivendev
519eb74cdd Merge pull request #71 from domaindrivendev/basic-auth-take2
Basic auth take2
2017-06-28 00:16:38 -07:00
domaindrivendev
de7ec5f15d Leverage security definitions for headers in example requests 2017-06-26 17:52:00 -07:00
domaindrivendev
e40c5fc26e Prep for 1.3.0 2017-06-26 17:39:34 -07:00
domaindrivendev
1de29136ef Merge pull request #64 from jume-dev/swagger-ui-url-patch
Add proper swagger ui url
2017-06-26 17:39:04 -07:00
domaindrivendev
88449944e1 Merge pull request #47 from DMiPartners/fixdepnotice
get no deprecation; add some docs
2017-06-26 17:32:32 -07:00
domaindrivendev
e03a6d333f Merge branch 'master' into fixdepnotice 2017-06-26 17:26:28 -07:00
domaindrivendev
215aaddb7d Merge pull request #69 from lazebny/rswag-ui-issue-with-precompilation
Fixed issue with precomilation
2017-06-19 07:17:15 -07:00
Vadim Lazebny
925b754233 Fixed issue with precomilation 2017-06-19 15:27:21 +03:00
Robert Kranz
69c7decce1 Add proper swagger ui url
As this only supports swagger ui 2, the url should link accordingly
2017-06-01 16:01:15 +02:00
domaindrivendev
353be669e4 Removed some deprecation warnings 2017-05-19 11:11:13 -07:00
domaindrivendev
75d676d753 Merge pull request #59 from grk/rails-5-1
Relax Rails dependency to allow 5.1
2017-05-19 10:39:29 -07:00
domaindrivendev
1b3a976313 Merge branch 'master' into rails-5-1 2017-05-19 10:35:00 -07:00
domaindrivendev
2617fdc48d Prep for v1.2.1 release 2017-05-19 10:32:22 -07:00
Grzesiek Kolodziejczyk
724ede0076 Remove unnecessary whitespace 2017-05-18 11:48:14 +02:00
Grzesiek Kolodziejczyk
8aeb9a6c70 Relax Rails dependency to allow 5.1 2017-05-18 11:47:40 +02:00
domaindrivendev
25d8adaf8b Merge pull request #57 from horiaradu/master
Response body value validation
2017-04-29 12:46:31 -07:00
Horia Radu
f195c82759 yield entire response instead of only the body 2017-04-29 21:17:53 +03:00
Horia Radu
37f86f6d94 yield entire response instead of only the body 2017-04-29 12:06:57 +03:00
Horia Radu
51c9f4e5e6 Response body value validation
Add the possibility to pass a block to the run_test!
method in order to add expectations on your response
2017-04-28 11:46:52 +03:00
Dante Piombino
233c5cc735 get no deprecation; add some docs 2017-02-16 16:31:45 -05:00
domaindrivendev
cf9170101b Merge pull request #42 from PavelBezpalov/master
Add way for examples generation from responses
2017-01-19 08:52:40 -08:00
Pavel Bezpalov
98d5b982c4 Add way for examples generation from responses 2017-01-19 11:46:56 +02:00
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
53 changed files with 636 additions and 373 deletions

3
.gitignore vendored
View File

@@ -2,3 +2,6 @@
**/*/log
**/*/*.gem
**/*/*.sqlite3
**/*/public/assets
*.swp
Gemfile.lock

1
.ruby-version Normal file
View File

@@ -0,0 +1 @@
2.3.0

View File

@@ -1,10 +1,64 @@
nguage: ruby
language: ruby
rvm:
- 2.2.5
env:
- "RAILS_VERSION=3.2.22"
- "RAILS_VERSION=4.2.0"
- "RAILS_VERSION=5.0.0"
cache: bundler
install: bundle update
- RAILS_VERSION=5.1.2
- RAILS_VERSION=4.2.0
- RAILS_VERSION=3.2.22
cache:
directories:
- /home/travis/.rvm/gems/ruby-2.2.5
install: bundle install
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- sleep 3
script: ./ci/test.sh
jobs:
include:
- stage: publish components
script: 'cd rswag-api'
deploy:
gemspec: rswag-api.gemspec
provider: rubygems
api_key: $RUBYGEMS_API_KEY
on:
branch: master
tags: true
- stage: publish components
script: 'cd rswag-specs'
deploy:
gemspec: rswag-specs.gemspec
provider: rubygems
api_key: $RUBYGEMS_API_KEY
on:
branch: master
tags: true
- stage: publish components
script: 'cd rswag-ui'
deploy:
gemspec: rswag-ui.gemspec
provider: rubygems
api_key: $RUBYGEMS_API_KEY
on:
branch: master
tags: true
- stage: publish rswag
script: 'cd rswag'
deploy:
gemspec: rswag.gemspec
provider: rubygems
api_key: $RUBYGEMS_API_KEY
on:
branch: master
tags: true

11
Gemfile
View File

@@ -2,7 +2,7 @@ source "https://rubygems.org"
# Allow the rails version to come from an ENV setting so Travis can test multiple versions.
# See http://www.schneems.com/post/50991826838/testing-against-multiple-rails-versions/
rails_version = ENV['RAILS_VERSION'] || '3.2.22'
rails_version = ENV['RAILS_VERSION'] || '5.1.2'
gem 'rails', "#{rails_version}"
@@ -15,6 +15,7 @@ end
gem 'sqlite3'
gem 'rswag-specs', path: './rswag-specs'
gem 'rswag-api', path: './rswag-api'
gem 'rswag-ui', path: './rswag-ui'
@@ -25,5 +26,11 @@ group :test do
gem 'test-unit'
gem 'rspec-rails'
gem 'generator_spec'
gem 'rswag-specs', path: './rswag-specs'
gem 'capybara'
gem 'capybara-webkit'
end
group :assets do
gem 'uglifier'
gem 'therubyracer'
end

View File

@@ -1,146 +0,0 @@
PATH
remote: ./rswag-api
specs:
rswag-api (1.0.3)
rails (>= 3.1, < 5.1)
PATH
remote: ./rswag-specs
specs:
rswag-specs (1.0.3)
json-schema (~> 2.2)
rails (>= 3.1, < 5.1)
rspec-rails (>= 2.14, < 4)
PATH
remote: ./rswag-ui
specs:
rswag-ui (1.0.3)
rails (>= 3.1, < 5.1)
GEM
remote: https://rubygems.org/
specs:
actionmailer (3.2.22)
actionpack (= 3.2.22)
mail (~> 2.5.4)
actionpack (3.2.22)
activemodel (= 3.2.22)
activesupport (= 3.2.22)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.4)
rack (~> 1.4.5)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.2.1)
activemodel (3.2.22)
activesupport (= 3.2.22)
builder (~> 3.0.0)
activerecord (3.2.22)
activemodel (= 3.2.22)
activesupport (= 3.2.22)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.22)
activemodel (= 3.2.22)
activesupport (= 3.2.22)
activesupport (3.2.22)
i18n (~> 0.6, >= 0.6.4)
multi_json (~> 1.0)
addressable (2.4.0)
arel (3.0.3)
builder (3.0.4)
diff-lcs (1.2.5)
erubis (2.7.0)
generator_spec (0.9.3)
activesupport (>= 3.0.0)
railties (>= 3.0.0)
hike (1.2.3)
i18n (0.7.0)
journey (1.0.4)
json (1.8.3)
json-schema (2.7.0)
addressable (>= 2.4)
mail (2.5.4)
mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.25.1)
multi_json (1.12.1)
polyglot (0.3.5)
power_assert (0.3.1)
rack (1.4.7)
rack-cache (1.6.1)
rack (>= 0.4)
rack-ssl (1.3.4)
rack
rack-test (0.6.3)
rack (>= 1.0)
rails (3.2.22)
actionmailer (= 3.2.22)
actionpack (= 3.2.22)
activerecord (= 3.2.22)
activeresource (= 3.2.22)
activesupport (= 3.2.22)
bundler (~> 1.0)
railties (= 3.2.22)
railties (3.2.22)
actionpack (= 3.2.22)
activesupport (= 3.2.22)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0)
rake (11.3.0)
rdoc (3.12.2)
json (~> 1.4)
rspec-core (3.5.4)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-rails (3.5.2)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
sprockets (2.2.3)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.12)
strong_parameters (0.2.3)
actionpack (~> 3.0)
activemodel (~> 3.0)
activesupport (~> 3.0)
railties (~> 3.0)
test-unit (3.2.1)
power_assert
thor (0.19.1)
tilt (1.4.1)
treetop (1.4.15)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.51)
PLATFORMS
ruby
DEPENDENCIES
generator_spec
rails (= 3.2.22)
rspec-rails
rswag-api!
rswag-specs!
rswag-ui!
sqlite3
strong_parameters
test-unit

151
README.md
View File

@@ -103,6 +103,17 @@ If you've used [Swagger](http://swagger.io/specification) before, then the synta
Take special note of the __run_test!__ method that's called within each response block. This tells rswag to create and execute a corresponding example. It builds and submits a request based on parameter descriptions and corresponding values that have been provided using the rspec "let" syntax. For example, the "post" description in the example above specifies a "body" parameter called "blog". It also lists 2 different responses. For the success case (i.e. the 201 response), notice how "let" is used to set the blog parameter to a value that matches the provided schema. For the failure case (i.e. the 422 response), notice how it's set to a value that does not match the provided schema. When the test is executed, rswag also validates the actual response code and, where applicable, the response body against the provided [JSON Schema](http://json-schema.org/documentation.html).
If you want to do additional validation on the response, pass a block to the __run_test!__ method:
```ruby
response '201', 'blog created' do
run_test! do |response|
data = JSON.parse(response.body)
expect(data['title']).to eq('foo')
end
end
```
If you'd like your specs to be a little more explicit about what's going on here, you can replace the call to __run_test!__ with equivalent "before" and "it" blocks:
```ruby
@@ -119,6 +130,31 @@ response '201', 'blog created' do
end
```
### Null Values ###
This library is currently using JSON::Draft4 for validation of response models. It does not support null as a value. So you can add the property 'x-nullable' to a definition to allow null/nil values to pass.
```ruby
describe 'Blogs API' do
path '/blogs' do
post 'Creates a blog' do
...
response '200', 'blog found' do
schema type: :object,
properties: {
id: { type: :integer },
title: { type: :string },
content: { type: :string, 'x-nullable': true }
}
....
end
end
end
end
```
*Note:* the OAI v3 may be released soon(ish?) and include a nullable property. This may have an effect on the need/use of custom extension to the draft. Do not use this property if you don't understand the implications.
<https://github.com/OAI/OpenAPI-Specification/issues/229#issuecomment-280376087>
### Global Metadata ###
In addition to paths, operations and responses, Swagger also supports global API metadata. When you install rswag, a file called _swagger_helper.rb_ is added to your spec folder. This is where you define one or more Swagger documents and provide global metadata. Again, the format is based on Swagger so most of the global fields supported by the top level ["Swagger" object](http://swagger.io/specification/#swaggerObject) can be provided with each document definition. As an example, you could define a Swagger document for each version of your API and in each case specify a title, version string and URL basePath:
@@ -164,6 +200,58 @@ describe 'Blogs API', swagger_doc: 'v2/swagger.json' do
end
```
### Specifying/Testing API Security ###
Swagger allows for the specification of different security schemes and their applicability to operations in an API. To leverage this in rswag, you define the schemes globally in _swagger_helper.rb_ and then use the "security" attribute at the operation level to specify which schemes, if any, are applicable to that operation. Swagger supports :basic, :apiKey and :oauth2 scheme types. See [the spec](http://swagger.io/specification/#security-definitions-object-109) for more info.
```ruby
# spec/swagger_helper.rb
RSpec.configure do |config|
config.swagger_root = Rails.root.to_s + '/swagger'
config.swagger_docs = {
'v1/swagger.json' => {
...
securityDefinitions: {
basic: {
type: :basic
},
apiKey: {
type: :apiKey,
name: 'api_key',
in: :query
}
}
}
}
end
# spec/integration/blogs_spec.rb
describe 'Blogs API' do
path '/blogs' do
post 'Creates a blog' do
tags 'Blogs'
security [ basic: [] ]
...
response '201', 'blog created' do
let(:Authorization) { "Basic #{::Base64.strict_encode64('jsmith:jspass')}" }
run_test!
end
response '401', 'authentication failed' do
let(:Authorization) { "Basic #{::Base64.strict_encode64('bogus:bogus')}" }
run_test!
end
end
end
end
```
__NOTE:__ Depending on the scheme types, you'll be required to assign a corresponding parameter value with each example. For example, :basic auth is required above and so the :Authorization (header) parameter must be set accordingly
## Configuration & Customization ##
The steps described above will get you up and running with minimal setup. However, rswag offers a lot of flexibility to customize as you see fit. Before exploring the various options, you'll need to be aware of it's different components. The following table lists each of them and the files that get added/updated as part of a standard install.
@@ -171,8 +259,8 @@ The steps described above will get you up and running with minimal setup. Howeve
|Gem|Description|Added/Updated|
|---------|-----------|-------------|
|__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-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-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 your Swagger endpoints|_config/initializers/rswag-ui.rb, config/routes.rb_|
### Output Location for Generated Swagger Files ###
@@ -243,6 +331,63 @@ describe 'Blogs API' do
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
```
### Enable generation examples from responses ###
To enable examples generation from responses add callback above run_test! like:
```ruby
after do |example|
example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) }
end
```
You need to disable --dry-run option for Rspec > 3
Add to application.rb:
```ruby
RSpec.configure do |config|
config.swagger_dry_run = false
end
```
### 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_:
@@ -314,7 +459,7 @@ end
### Customizing the swagger-ui ###
The swagger-ui provides several options for customizing it's behavior, all of which are documented here https://github.com/swagger-api/swagger-ui#swaggerui. If you need to tweak these or customize the overall look and feel of your swagger-ui, then you'll need to provide your own version of index.html. You can do this with the following generator.
The swagger-ui provides several options for customizing it's behavior, all of which are documented here https://github.com/swagger-api/swagger-ui/tree/2.x#swaggerui. If you need to tweak these or customize the overall look and feel of your swagger-ui, then you'll need to provide your own version of index.html. You can do this with the following generator.
```ruby
rails g rswag:ui:custom

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env bash
ROOT_PATH=$PWD
set -e # abort if anything fails
bundle check || bundle
echo '####################'
echo 'Build Gems'
echo '####################'
echo ''
echo '##### rswag-api #####'
cd $ROOT_PATH/rswag-api
gem build rswag-api.gemspec
echo '##### rswag-specs #####'
cd $ROOT_PATH/rswag-specs
gem build rswag-specs.gemspec
echo '##### rswag-ui #####'
cd $ROOT_PATH/rswag-ui
gem build rswag-ui.gemspec
echo '##### rswag #####'
cd $ROOT_PATH/rswag
gem build rswag.gemspec
echo '####################'
echo 'Push to RubyGems'
echo '####################'
echo ''
echo 'Type the version no, followed by [ENTER]:'
read version
echo '##### rswag-api #####'
cd $ROOT_PATH/rswag-api
gem push rswag-api-$version.gem
echo '##### rswag-specs #####'
cd $ROOT_PATH/rswag-specs
gem push rswag-specs-$version.gem
echo '##### rswag-ui #####'
cd $ROOT_PATH/rswag-ui
gem push rswag-ui-$version.gem
echo '##### rswag #####'
cd $ROOT_PATH/rswag
gem push rswag-$version.gem
# Cleanup
cd $ROOT_PATH
# Create git tag
git tag v$version
git push origin v$version

View File

@@ -1,4 +1,3 @@
require 'rswag/api/version'
require 'rswag/api/configuration'
require 'rswag/api/engine'

View File

@@ -1,5 +0,0 @@
module Rswag
module Api
VERSION = '1.0.3'
end
end

View File

@@ -1,12 +1,9 @@
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require 'rswag/api/version'
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "rswag-api"
s.version = Rswag::Api::VERSION
s.version = ENV['TRAVIS_TAG'] || '0.0.0'
s.authors = ["Richie Morris"]
s.email = ["domaindrivendev@gmail.com"]
s.homepage = "https://github.com/domaindrivendev/rswag"
@@ -14,7 +11,7 @@ Gem::Specification.new do |s|
s.description = "Open up your API to the phenomenal Swagger ecosystem by exposing Swagger files, that describe your service, as JSON endpoints"
s.license = "MIT"
s.files = Dir["{lib}/**/*"] + ["MIT-LICENSE", "Rakefile" ]
s.files = Dir["{lib}/**/*"] + ["MIT-LICENSE", "Rakefile"]
s.add_dependency "rails", ">= 3.1", "< 5.1"
s.add_dependency 'railties', '>= 3.1', '< 6.0'
end

View File

@@ -1,5 +1,4 @@
require 'rspec/core'
require 'rswag/specs/version'
require 'rswag/specs/example_group_helpers'
require 'rswag/specs/example_helpers'
require 'rswag/specs/configuration'
@@ -12,6 +11,7 @@ module Rswag
::RSpec.configure do |c|
c.add_setting :swagger_root
c.add_setting :swagger_docs
c.add_setting :swagger_dry_run
c.extend ExampleGroupHelpers, type: :request
c.include ExampleHelpers, type: :request
end

View File

@@ -25,6 +25,12 @@ module Rswag
end
end
def swagger_dry_run
@swagger_dry_run ||= begin
@rspec_config.swagger_dry_run.nil? || @rspec_config.swagger_dry_run
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]

View File

@@ -61,7 +61,15 @@ module Rswag
metadata[:response][:headers][name] = attributes
end
def run_test!
# 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!(&block)
# NOTE: rspec 2.x support
if RSPEC_VERSION < 3
before do
@@ -69,7 +77,7 @@ module Rswag
end
it "returns a #{metadata[:response][:code]} response" do
assert_response_matches_metadata(example.metadata)
assert_response_matches_metadata(example.metadata, &block)
end
else
before do |example|
@@ -77,7 +85,7 @@ module Rswag
end
it "returns a #{metadata[:response][:code]} response" do |example|
assert_response_matches_metadata(example.metadata)
assert_response_matches_metadata(example.metadata, &block)
end
end
end

View File

@@ -14,7 +14,7 @@ module Rswag
api_metadata[:operation][:verb],
factory.build_fullpath(self),
factory.build_body(self),
factory.build_headers(self)
rackify_headers(factory.build_headers(self)) # Rails test infrastructure requires Rack headers
)
else
send(
@@ -28,14 +28,30 @@ module Rswag
end
end
def assert_response_matches_metadata(api_metadata)
def assert_response_matches_metadata(api_metadata, &block)
global_metadata = rswag_config.get_swagger_doc(api_metadata[:swagger_doc])
validator = ResponseValidator.new(api_metadata, global_metadata)
validator.validate!(response)
validator.validate!(response, &block)
end
private
def rackify_headers(headers)
name_value_pairs = headers.map do |name, value|
[
case name
when 'Accept' then 'HTTP_ACCEPT'
when 'Content-Type' then 'CONTENT_TYPE'
when 'Authorization' then 'HTTP_AUTHORIZATION'
else name
end,
value
]
end
Hash[ name_value_pairs ]
end
def rswag_config
::Rswag::Specs.config
end

View File

@@ -2,13 +2,13 @@ require 'json-schema'
module Rswag
module Specs
class ExtendedSchema < JSON::Schema::Validator
class ExtendedSchema < JSON::Schema::Draft4
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')
@names = ['http://tempuri.org/rswag/specs/extended_schema']
end
end

View File

@@ -32,13 +32,20 @@ module Rswag
end
def build_headers(example)
headers = Hash[ parameters_in(:header).map { |p| [ p[:name], example.send(p[:name]).to_s ] } ]
headers.tap do |h|
name_value_pairs = parameters_in(:header).map do |param|
[
param[:name],
example.send(param[:name]).to_s
]
end
# Add MIME type headers based on produces/consumes metadata
produces = @api_metadata[:operation][:produces] || @global_metadata[:produces]
consumes = @api_metadata[:operation][:consumes] || @global_metadata[:consumes]
h['ACCEPT'] = produces.join(';') unless produces.nil?
h['CONTENT_TYPE'] = consumes.join(';') unless consumes.nil?
end
name_value_pairs << [ 'Accept', produces.join(';') ] unless produces.nil?
name_value_pairs << [ 'Content-Type', consumes.join(';') ] unless consumes.nil?
Hash[ name_value_pairs ]
end
private
@@ -52,7 +59,7 @@ module Rswag
applicable_params
.map { |p| p['$ref'] ? resolve_parameter(p['$ref']) : p } # resolve any references
.concat(resolve_api_key_parameters)
.concat(security_parameters)
.select { |p| p[:in] == location }
end
@@ -63,17 +70,23 @@ module Rswag
defined_params[key]
end
def resolve_api_key_parameters
@api_key_params ||= begin
def security_parameters
applicable_security_schemes.map do |scheme|
if scheme[:type] == :apiKey
{ name: scheme[:name], type: :string, in: scheme[:in] }
else
{ name: 'Authorization', type: :string, in: :header } # use auth header for basic & oauth2
end
end
end
def applicable_security_schemes
# First figure out the security requirement applicable to the operation
global_requirements = (@global_metadata[:security] || [] ).map { |r| r.keys.first }
operation_requirements = (@api_metadata[:operation][:security] || [] ).map { |r| r.keys.first }
requirements = global_requirements | operation_requirements
requirements = @api_metadata[:operation][:security] || @global_metadata[:security]
scheme_names = requirements ? requirements.map { |r| r.keys.first } : []
# Then obtain the scheme definitions for those requirements
definitions = (@global_metadata[:securityDefinitions] || {}).slice(*requirements)
definitions.values.select { |d| d[:type] == :apiKey }
end
(@global_metadata[:securityDefinitions] || {}).slice(*scheme_names).values
end
def build_query_string_part(param, value)

View File

@@ -1,5 +1,6 @@
require 'active_support/core_ext/hash/slice'
require 'json-schema'
require 'json'
require 'rswag/specs/extended_schema'
module Rswag
@@ -11,9 +12,11 @@ module Rswag
@global_metadata = global_metadata
end
def validate!(response)
def validate!(response, &block)
validate_code!(response.code)
validate_body!(response.body)
validate_headers!(response.headers)
validate_body!(response.body, &block)
block.call(response) if block_given?
end
private
@@ -24,6 +27,15 @@ module Rswag
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)
response_schema = @api_metadata[:response][:schema]
return if response_schema.nil?

View File

@@ -1,5 +0,0 @@
module Rswag
module Specs
VERSION = '1.0.3'
end
end

View File

@@ -8,7 +8,7 @@ namespace :rswag do
t.pattern = 'spec/requests/**/*_spec.rb, spec/api/**/*_spec.rb, spec/integration/**/*_spec.rb'
# NOTE: rspec 2.x support
if Rswag::Specs::RSPEC_VERSION > 2
if Rswag::Specs::RSPEC_VERSION > 2 && Rswag::Specs.config.swagger_dry_run
t.rspec_opts = [ '--format Rswag::Specs::SwaggerFormatter', '--dry-run', '--order defined' ]
else
t.rspec_opts = [ '--format Rswag::Specs::SwaggerFormatter', '--order defined' ]

View File

@@ -1,12 +1,9 @@
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require 'rswag/specs/version'
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "rswag-specs"
s.version = Rswag::Specs::VERSION
s.version = ENV['TRAVIS_TAG'] || '0.0.0'
s.authors = ["Richie Morris"]
s.email = ["domaindrivendev@gmail.com"]
s.homepage = "https://github.com/domaindrivendev/rswag"
@@ -16,7 +13,7 @@ Gem::Specification.new do |s|
s.files = Dir["{lib}/**/*"] + ["MIT-LICENSE", "Rakefile" ]
s.add_dependency "rails", ">= 3.1", "< 5.1"
s.add_dependency 'activesupport', '>= 3.1', '< 6.0'
s.add_dependency 'railties', '>= 3.1', '< 6.0'
s.add_dependency 'json-schema', '~> 2.2'
s.add_dependency 'rspec-rails', '>= 2.14', '< 4'
end

View File

@@ -151,6 +151,25 @@ module Rswag
)
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

View File

@@ -193,6 +193,33 @@ module Rswag
end
end
context "global definition for 'basic auth'" do
before do
global_metadata[:securityDefinitions] = { basic_auth: { type: :basic} }
allow(example).to receive(:'Authorization').and_return('Basic foobar')
end
context 'global requirement' do
before { global_metadata[:security] = [ { basic_auth: [] } ] }
it "includes a corresponding Authorization header" do
expect(headers).to match(
'Authorization' => 'Basic foobar'
)
end
end
context 'operation-specific requirement' do
before { api_metadata[:operation][:security] = [ { basic_auth: [] } ] }
it "includes a corresponding Authorization header" do
expect(headers).to match(
'Authorization' => 'Basic foobar'
)
end
end
end
context 'consumes & produces' do
before do
api_metadata[:operation][:consumes] = [ 'application/json', 'application/xml' ]
@@ -201,8 +228,8 @@ module Rswag
it "includes corresponding 'Accept' & 'Content-Type' headers" do
expect(headers).to match(
'ACCEPT' => 'application/json;application/xml',
'CONTENT_TYPE' => 'application/json;application/xml'
'Accept' => 'application/json;application/xml',
'Content-Type' => 'application/json;application/xml'
)
end
end
@@ -217,8 +244,8 @@ module Rswag
it "includes corresponding 'Accept' & 'Content-Type' headers" do
expect(headers).to match(
'ACCEPT' => 'application/json;application/xml',
'CONTENT_TYPE' => 'application/json;application/xml'
'Accept' => 'application/json;application/xml',
'Content-Type' => 'application/json;application/xml'
)
end
end

View File

@@ -29,7 +29,7 @@ module Rswag
api_metadata[:response][:schema] = {
type: 'object',
properties: { text: { type: 'string' } },
required: [ 'text' ]
required: ['text']
}
end
@@ -42,6 +42,25 @@ module Rswag
let(:response) { OpenStruct.new(code: 200, body: "{\"foo\":\"Some comment\"}") }
it { expect { call }.to raise_error UnexpectedResponse }
end
context "'block' provided" do
let(:call) do
subject.validate!(response) do |response|
data = JSON.parse(response.body)
expect(data['text']).to eq('Some comment')
end
end
context 'the block validation passes' do
let(:response) { OpenStruct.new(code: 200, body: "{\"text\":\"Some comment\"}") }
it { expect { call }.to_not raise_error }
end
context 'the block validation fails' do
let(:response) { OpenStruct.new(code: 200, body: "{\"text\":\"Some other comment\"}") }
it { expect { call }.to raise_error(RSpec::Expectations::ExpectationNotMetError) }
end
end
end
context "referenced 'schema' provided" do
@@ -51,7 +70,7 @@ module Rswag
author: {
type: 'object',
properties: { name: { type: 'string' } },
required: [ 'name' ]
required: ['name']
}
}
end
@@ -66,6 +85,42 @@ module Rswag
it { expect { call }.to raise_error UnexpectedResponse }
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

View File

@@ -3,33 +3,33 @@
<head>
<meta charset="UTF-8">
<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="/assets/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='/assets/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='/assets/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 rel="icon" type="image/png" href="<%= asset_path 'swagger-ui/images/favicon-32x32.png' %>" sizes="32x32" />
<link rel="icon" type="image/png" href="<%= asset_path 'swagger-ui/images/favicon-16x16.png' %>" sizes="16x16" />
<link href="<%= asset_path 'swagger-ui/css/typography.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="<%= asset_path 'swagger-ui/css/screen.css' %>" media='screen' rel='stylesheet' type='text/css'/>
<link href="<%= asset_path 'swagger-ui/css/reset.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='/assets/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='/assets/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='/assets/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='/assets/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='/assets/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='/assets/swagger-ui/lib/jsoneditor.min.js' type='text/javascript'></script>
<script src='/assets/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/object-assign-pollyfill.js' %>" type='text/javascript'></script>
<script src="<%= asset_path 'swagger-ui/lib/jquery-1.8.0.min.js' %>" type='text/javascript'></script>
<script src="<%= asset_path 'swagger-ui/lib/jquery.slideto.min.js' %>" type='text/javascript'></script>
<script src="<%= asset_path 'swagger-ui/lib/jquery.wiggle.min.js' %>" type='text/javascript'></script>
<script src="<%= asset_path 'swagger-ui/lib/jquery.ba-bbq.min.js' %>" type='text/javascript'></script>
<script src="<%= asset_path 'swagger-ui/lib/handlebars-4.0.5.js' %>" type='text/javascript'></script>
<script src="<%= asset_path 'swagger-ui/lib/lodash.min.js' %>" type='text/javascript'></script>
<script src="<%= asset_path 'swagger-ui/lib/backbone-min.js' %>" type='text/javascript'></script>
<script src="<%= asset_path 'swagger-ui/swagger-ui.min.js' %>" type='text/javascript'></script>
<script src="<%= asset_path '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_extended.js' %>" type='text/javascript'></script>
<script src="<%= asset_path 'swagger-ui/lib/jsoneditor.min.js' %>" type='text/javascript'></script>
<script src="<%= asset_path 'swagger-ui/lib/marked.js' %>" type='text/javascript'></script>
<script src="<%= asset_path 'swagger-ui/lib/swagger-oauth.js' %>" type='text/javascript'></script>
<!-- Some basic translations -->
<!-- <script src='/assets/swagger-ui/lang/translator.js' type='text/javascript'></script> -->
<!-- <script src='/assets/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/translator.js' %>" type='text/javascript'></script> -->
<!-- <script src="<%= asset_path 'swagger-ui/lang/ru.js' %>" type='text/javascript'></script> -->
<!-- <script src="<%= asset_path 'swagger-ui/lang/en.js' %>" type='text/javascript'></script> -->
<script type="text/javascript">
$(function () {
@@ -84,7 +84,7 @@
<body class="swagger-section">
<div id='header'>
<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'>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/></div>
<div id='auth_container'></div>

View File

@@ -1,4 +1,3 @@
require 'rswag/ui/version'
require 'rswag/ui/configuration'
require 'rswag/ui/engine'

View File

@@ -5,7 +5,14 @@ module Rswag
initializer 'rswag-ui.initialize' do |app|
if app.config.respond_to?(:assets)
app.config.assets.precompile += [ 'swagger-ui/*' ]
app.config.assets.precompile += [
'swagger-ui/css/*',
'swagger-ui/fonts/*',
'swagger-ui/images/*',
'swagger-ui/lang/*',
'swagger-ui/lib/*',
'swagger-ui/swagger-ui.min.js'
]
end
end
end

View File

@@ -1,5 +0,0 @@
module Rswag
module Ui
VERSION = '1.0.3'
end
end

View File

@@ -1,12 +1,9 @@
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require 'rswag/ui/version'
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "rswag-ui"
s.version = Rswag::Ui::VERSION
s.version = ENV['TRAVIS_TAG'] || '0.0.0'
s.authors = ["Richie Morris"]
s.email = ["domaindrivendev@gmail.com"]
s.homepage = "https://github.com/domaindrivendev/rswag"
@@ -16,5 +13,6 @@ Gem::Specification.new do |s|
s.files = Dir["{app,config,lib,vendor}/**/*"] + ["MIT-LICENSE", "Rakefile" ]
s.add_dependency "rails", ">= 3.1", "< 5.1"
s.add_dependency 'actionpack', '>=3.1', '< 6.0'
s.add_dependency 'railties', '>= 3.1', '< 6.0'
end

View File

@@ -880,7 +880,7 @@
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 {
background-image: url('../images/throbber.gif');
background-image: url(<%= asset_path('swagger-ui/images/throbber.gif') %>);
width: 128px;
height: 16px;
display: block;
@@ -1222,7 +1222,7 @@
height: 18px;
vertical-align: middle;
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 {
background-position: 0 0;

View File

@@ -880,7 +880,7 @@
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 {
background-image: url('../images/throbber.gif');
background-image: url(<%= asset_path('swagger-ui/images/throbber.gif') %>);
width: 128px;
height: 16px;
display: block;
@@ -1222,7 +1222,7 @@
height: 18px;
vertical-align: middle;
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 {
background-position: 0 0;
@@ -1354,7 +1354,7 @@
height: 18px;
vertical-align: middle;
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 {
position: relative;

View File

@@ -2,7 +2,7 @@
font-size: 1.5em;
font-weight: bold;
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;
}
#text-head {
@@ -64,7 +64,7 @@ h1 {
width: 1500px;
margin: auto;
margin-top: 0;
background-image: url('../images/shield.png');
background-image: url(<%= asset_path('swagger-ui/images/shield.png') %>);
background-repeat: no-repeat;
background-position: -40px -20px;
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,4 +1,3 @@
require 'rswag/version'
require 'rswag/specs'
require 'rswag/api'
require 'rswag/ui'

View File

@@ -1,3 +0,0 @@
module Rswag
VERSION = '1.0.3'
end

View File

@@ -1,12 +1,9 @@
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require 'rswag/version'
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "rswag"
s.version = Rswag::VERSION
s.version = ENV['TRAVIS_TAG'] || '0.0.0'
s.authors = ["Richie Morris"]
s.email = ["domaindrivendev@gmail.com"]
s.homepage = "https://github.com/domaindrivendev/rswag"
@@ -16,7 +13,7 @@ Gem::Specification.new do |s|
s.files = Dir["{lib}/**/*"] + [ "MIT-LICENSE" ]
s.add_dependency 'rswag-specs', Rswag::VERSION
s.add_dependency 'rswag-api', Rswag::VERSION
s.add_dependency 'rswag-ui', Rswag::VERSION
s.add_dependency 'rswag-specs', ENV['TRAVIS_TAG'] || '0.0.0'
s.add_dependency 'rswag-api', ENV['TRAVIS_TAG'] || '0.0.0'
s.add_dependency 'rswag-ui', ENV['TRAVIS_TAG'] || '0.0.0'
end

View File

@@ -1,7 +1,7 @@
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
protect_from_forgery with: :null_session
wrap_parameters format: [ :json ]
end

View File

@@ -0,0 +1,13 @@
class AuthTestsController < ApplicationController
wrap_parameters Blog
respond_to :json
# POST /auth-tests/basic
def basic
if authenticate_with_http_basic { |u, p| u == 'jsmith' && p == 'jspass' }
head :no_content
else
request_http_basic_authentication
end
end
end

View File

@@ -17,6 +17,10 @@ class BlogsController < ApplicationController
# GET /blogs/1
def show
@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
end

View File

@@ -5,7 +5,7 @@ class Blog < ActiveRecord::Base
{
id: id,
title: title,
content: content
content: nil
}
end
end

View File

@@ -1,5 +1,5 @@
require 'rubygems'
gemfile = File.expand_path('../../../../Gemfile', __FILE__)
gemfile = File.expand_path('../../../Gemfile', __FILE__)
if File.exist?(gemfile)
ENV['BUNDLE_GEMFILE'] = gemfile

View File

@@ -28,4 +28,6 @@ TestApp::Application.configure do
# Expands the lines which load the assets
config.assets.debug = true
config.eager_load = false
end

View File

@@ -32,4 +32,6 @@ TestApp::Application.configure do
# Print deprecation notices to the stderr
config.active_support.deprecation = :stderr
config.eager_load = false
end

View File

@@ -5,3 +5,6 @@
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
TestApp::Application.config.secret_token = '60f36cd33756d73f362053f1d45256ae50d75440b634ae73b070a6e35a2df38692f59e28e5ecbd1f9f2e850255f6d29a468bc59ac4484c2b7f0548ddbfc1b870'
# See http://guides.rubyonrails.org/upgrading_ruby_on_rails.html#config-secrets-yml
TestApp::Application.config.secret_key_base = 'f6a820cc8aa76094583cd68ef46a735e25e3278648086355f8bd24721f036959c728c06a28dcecfe695f17ae2db44dfa1424f22b81377f2a1496d4e19f6f7faa'

View File

@@ -1,6 +1,8 @@
TestApp::Application.routes.draw do
resources :blogs, defaults: { :format => :json }
post 'auth-tests/basic', to: 'auth_tests#basic'
mount Rswag::Api::Engine => 'api-docs'
mount Rswag::Ui::Engine => 'api-docs'
end

View File

@@ -1,4 +1,10 @@
class CreateBlogs < ActiveRecord::Migration
migration_class = if Gem::Version.new(Rails.version) >= Gem::Version.new("5.0")
ActiveRecord::Migration[4.2]
else
ActiveRecord::Migration
end
class CreateBlogs < migration_class
def change
create_table :blogs do |t|
t.string :title

View File

@@ -1,4 +1,3 @@
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
@@ -9,15 +8,15 @@
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended to check this file into your version control system.
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(:version => 20160218212104) do
ActiveRecord::Schema.define(version: 20160218212104) do
create_table "blogs", :force => true do |t|
create_table "blogs", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.datetime "created_at"
t.datetime "updated_at"
end
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

@@ -0,0 +1,22 @@
require 'swagger_helper'
describe 'Auth Tests API', type: :request, swagger_doc: 'v1/swagger.json' do
path '/auth-tests/basic' do
post 'Authenticates with basic auth' do
tags 'Auth Test'
operationId 'testBasicAuth'
security [ basic_auth: [] ]
response '204', 'Valid credentials' do
let(:Authorization) { "Basic #{::Base64.strict_encode64('jsmith:jspass')}" }
run_test!
end
response '401', 'Invalid credentials' do
let(:Authorization) { "Basic #{::Base64.strict_encode64('foo:bar')}" }
run_test!
end
end
end
end

View File

@@ -50,8 +50,18 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
produces 'application/json'
response '200', 'blog found' do
header 'ETag', type: :string
header 'Last-Modified', type: :string
header 'Cache-Control', type: :string
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(:id) { blog.id }
run_test!

View File

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

View File

@@ -39,12 +39,15 @@ RSpec.configure do |config|
properties: {
id: { type: 'integer' },
title: { type: 'string' },
content: { type: 'string' }
content: { type: 'string', 'x-nullable': true }
},
required: [ 'id', 'title', 'content' ]
}
},
securityDefinitions: {
basic_auth: {
type: :basic
},
api_key: {
type: :apiKey,
name: 'api_key',

View File

@@ -5,6 +5,30 @@
"version": "v1"
},
"paths": {
"/auth-tests/basic": {
"post": {
"summary": "Authenticates with basic auth",
"tags": [
"Auth Test"
],
"operationId": "testBasicAuth",
"security": [
{
"basic_auth": [
]
}
],
"responses": {
"204": {
"description": "Valid credentials"
},
"401": {
"description": "Invalid credentials"
}
}
}
},
"/blogs": {
"post": {
"summary": "Creates a blog",
@@ -89,8 +113,26 @@
"responses": {
"200": {
"description": "blog found",
"headers": {
"ETag": {
"type": "string"
},
"Last-Modified": {
"type": "string"
},
"Cache-Control": {
"type": "string"
}
},
"schema": {
"$ref": "#/definitions/blog"
},
"examples": {
"application/json": {
"id": 1,
"title": "Hello world!",
"content": "Hello world and hello universe. Thank you all very much!!!"
}
}
},
"404": {
@@ -128,7 +170,8 @@
"type": "string"
},
"content": {
"type": "string"
"type": "string",
"x-nullable": true
}
},
"required": [
@@ -139,6 +182,9 @@
}
},
"securityDefinitions": {
"basic_auth": {
"type": "basic"
},
"api_key": {
"type": "apiKey",
"name": "api_key",