Skip to content

Commit

Permalink
Merge pull request #347 from djezzzl/add-swagger-support
Browse files Browse the repository at this point in the history
Add OpenAPI Specification support (Version 2.0)
  • Loading branch information
oestrich authored Jul 9, 2018
2 parents 560c3bd + b65559c commit 229fe27
Show file tree
Hide file tree
Showing 48 changed files with 2,371 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ example/public/docs
*.gem
*.swp
/html/
/.idea
173 changes: 172 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,16 @@ RspecApiDocumentation.configure do |config|
# Set the application that Rack::Test uses
config.app = Rails.application

# Used to provide a configuration for the specification (supported only by 'open_api' format for now)
config.configurations_dir = Rails.root.join("doc", "configurations", "api")

# Output folder
config.docs_dir = Rails.root.join("doc", "api")

# An array of output format(s).
# Possible values are :json, :html, :combined_text, :combined_json,
# :json_iodocs, :textile, :markdown, :append_json, :slate,
# :api_blueprint
# :api_blueprint, :open_api
config.format = [:html]

# Location of templates
Expand Down Expand Up @@ -172,6 +175,7 @@ end
* **markdown**: Generates an index file and example files in Markdown.
* **api_blueprint**: Generates an index file and example files in [APIBlueprint](https://apiblueprint.org).
* **append_json**: Lets you selectively run specs without destroying current documentation. See section below.
* **open_api**: Generates [OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md) (OAS) (Current supported version is 2.0). Can be used for [Swagger-UI](https://swagger.io/tools/swagger-ui/)

### append_json

Expand Down Expand Up @@ -228,6 +232,173 @@ This [format](https://apiblueprint.org) (APIB) has additional functions:
* `attribute`: APIB has attributes besides parameters. Use attributes exactly
like you'd use `parameter` (see documentation below).

### open_api

This [format](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md) (OAS) has additional functions:

* `authentication(type, value, opts = {})` ([Security schema object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-scheme-object))

The values will be passed through header of the request. Option `name` has to be provided for `apiKey`.

* `authentication :basic, 'Basic Key'`
* `authentication :apiKey, 'Api Key', name: 'API_AUTH', description: 'Some description'`

You could pass `Symbol` as value. In this case you need to define a `let` with the same name.

```
authentication :apiKey, :api_key
let(:api_key) { some_value }
```

* `route_summary(text)` and `route_description(text)`. ([Operation object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#operation-object))

These two simplest methods accept `String`.
It will be used for route's `summary` and `description`.
* Several new options on `parameter` helper.
- `with_example: true`. This option will adjust your description of the parameter with the passed value.
- `default: <value>`. Will provide a default value for the parameter.
- `minimum: <integer>`. Will setup upper limit for your parameter.
- `maximum: <integer>`. Will setup lower limit for your parameter.
- `enum: [<value>, <value>, ..]`. Will provide a pre-defined list of possible values for your parameter.
- `type: [:file, :array, :object, :boolean, :integer, :number, :string]`. Will set a type for the parameter. Most of the type you don't need to provide this option manually. We extract types from values automatically.


You also can provide a configuration file in YAML or JSON format with some manual configs.
The file should be placed in `configurations_dir` folder with the name `open_api.yml` or `open_api.json`.
In this file you able to manually **hide** some endpoints/resources you want to hide from generated API specification but still want to test.
It's also possible to pass almost everything to the specification builder manually.
#### Example of configuration file
```yaml
swagger: '2.0'
info:
title: OpenAPI App
description: This is a sample server.
termsOfService: 'http://open-api.io/terms/'
contact:
name: API Support
url: 'http://www.open-api.io/support'
email: [email protected]
license:
name: Apache 2.0
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
version: 1.0.0
host: 'localhost:3000'
schemes:
- http
- https
consumes:
- application/json
- application/xml
produces:
- application/json
- application/xml
paths:
/orders:
hide: true
/instructions:
hide: false
get:
description: This description came from configuration file
hide: true
```
#### Example of spec file
```ruby
resource 'Orders' do
explanation "Orders resource"
authentication :apiKey, :api_key, description: 'Private key for API access', name: 'HEADER_KEY'
header "Content-Type", "application/json"
let(:api_key) { generate_api_key }
get '/orders' do
route_summary "This URL allows users to interact with all orders."
route_description "Long description."
# This is manual way to describe complex parameters
parameter :one_level_array, type: :array, items: {type: :string, enum: ['string1', 'string2']}, default: ['string1']
parameter :two_level_array, type: :array, items: {type: :array, items: {type: :string}}
let(:one_level_array) { ['string1', 'string2'] }
let(:two_level_array) { [['123', '234'], ['111']] }
# This is automatic way
# It's possible because we extract parameters definitions from the values
parameter :one_level_arr, with_example: true
parameter :two_level_arr, with_example: true

let(:one_level_arr) { ['value1', 'value2'] }
let(:two_level_arr) { [[5.1, 3.0], [1.0, 4.5]] }

context '200' do
example_request 'Getting a list of orders' do
expect(status).to eq(200)
expect(response_body).to eq(<response>)
end
end
end

put '/orders/:id' do
route_summary "This is used to update orders."

with_options scope: :data, with_example: true do
parameter :name, 'The order name', required: true
parameter :amount
parameter :description, 'The order description'
end

context "200" do
let(:id) { 1 }

example 'Update an order' do
request = {
data: {
name: 'order',
amount: 1,
description: 'fast order'
}
}

# It's also possible to extract types of parameters when you pass data through `do_request` method.
do_request(request)

expected_response = {
data: {
name: 'order',
amount: 1,
description: 'fast order'
}
}
expect(status).to eq(200)
expect(response_body).to eq(<response>)
end
end

context "400" do
let(:id) { "a" }

example_request 'Invalid request' do
expect(status).to eq(400)
end
end

context "404" do
let(:id) { 0 }

example_request 'Order is not found' do
expect(status).to eq(404)
end
end
end
end
```

## Filtering and Exclusion

Expand Down
3 changes: 3 additions & 0 deletions example/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ source 'https://rubygems.org'

ruby '2.3.3'

gem 'rack-cors', :require => 'rack/cors'
gem 'rails', '4.2.5.1'
gem 'sqlite3'
gem 'spring', group: :development
gem 'raddocs', :github => "smartlogic/raddocs"

group :test, :development do
gem 'byebug'
gem 'awesome_print'
gem 'rspec-rails'
gem 'rspec_api_documentation', :path => "../"
end
22 changes: 15 additions & 7 deletions example/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ GIT
sinatra (~> 1.3, >= 1.3.0)

PATH
remote: ../
remote: ..
specs:
rspec_api_documentation (4.7.0)
rspec_api_documentation (5.1.0)
activesupport (>= 3.0.0)
json (~> 1.4, >= 1.4.6)
mustache (~> 0.99, >= 0.99.4)
rspec (>= 3.0.0)
mustache (~> 1.0, >= 0.99.4)
rspec (~> 3.0)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -55,7 +54,9 @@ GEM
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
arel (6.0.3)
awesome_print (1.7.0)
builder (3.2.2)
byebug (9.0.6)
concurrent-ruby (1.0.0)
diff-lcs (1.2.5)
erubis (2.7.0)
Expand All @@ -72,10 +73,11 @@ GEM
mime-types (2.99)
mini_portile2 (2.0.0)
minitest (5.8.4)
mustache (0.99.8)
mustache (1.0.5)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
rack (1.6.4)
rack-cors (0.4.1)
rack-protection (1.5.3)
rack
rack-test (0.6.3)
Expand Down Expand Up @@ -148,12 +150,18 @@ PLATFORMS
ruby

DEPENDENCIES
awesome_print
byebug
rack-cors
raddocs!
rails (= 4.2.5.1)
rspec-rails
rspec_api_documentation!
spring
sqlite3

RUBY VERSION
ruby 2.3.3p222

BUNDLED WITH
1.11.2
1.16.2
3 changes: 2 additions & 1 deletion example/app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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: :exception
protect_from_forgery with: :null_session
end
11 changes: 10 additions & 1 deletion example/app/controllers/orders_controller.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
class OrdersController < ApplicationController
before_action only: :index do
head :unauthorized unless request.headers['HTTP_AUTH_TOKEN'] =~ /\AAPI_TOKEN$/
end

def index
render :json => Order.all
end

def show
render :json => Order.find(params[:id])
order = Order.find_by(id: params[:id])
if order
render json: order
else
head :not_found
end
end

def create
Expand Down
2 changes: 2 additions & 0 deletions example/app/controllers/uploads_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class UploadsController < ApplicationController
http_basic_authenticate_with name: 'user', password: 'password'

def create
head 201
end
Expand Down
8 changes: 8 additions & 0 deletions example/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@

module Example
class Application < Rails::Application

config.middleware.insert_before 0, 'Rack::Cors' do
allow do
origins '*'
resource '*', :headers => :any, :methods => [:get, :post, :options, :put, :patch, :delete, :head]
end
end

# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
Expand Down
23 changes: 23 additions & 0 deletions example/config/open_api.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
swagger: '2.0'
info:
title: OpenAPI App
description: This is a sample server Petstore server.
termsOfService: 'http://open-api.io/terms/'
contact:
name: API Support
url: 'http://www.open-api.io/support'
email: [email protected]
license:
name: Apache 2.0
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
version: 1.0.1
host: 'localhost:3000'
schemes:
- http
- https
consumes:
- application/json
- application/xml
produces:
- application/json
- application/xml
2 changes: 1 addition & 1 deletion example/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

ActiveRecord::Schema.define(version: 20140616151047) do

create_table "orders", force: true do |t|
create_table "orders", force: :cascade do |t|
t.string "name"
t.boolean "paid"
t.string "email"
Expand Down
Loading

0 comments on commit 229fe27

Please sign in to comment.