Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OpenAPI Specification support (Version 2.0) #347

Merged
merged 1 commit into from
Jul 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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