Skip to content

Commit

Permalink
Callback obj (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
austbot authored and zhandao committed Feb 21, 2018
1 parent dfd5055 commit 76df0fe
Show file tree
Hide file tree
Showing 14 changed files with 216 additions and 41 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

## [Unreleased]

## [1.5.5] - 2018/2/21 - [view diff](https://github.com/zhandao/zero-rails_openapi/compare/v1.5.4...v1.5.5)

## Added

1. Callback Object has been supported. (issue [#12](#https://github.com/zhandao/zero-rails_openapi/issues/12) @austbot)

## Changed

1. `mk`'s parameter `eq` is changed to `get`. (dssl.rb)

## [1.5.4] - 2018/2/15 - [view diff](https://github.com/zhandao/zero-rails_openapi/compare/v1.5.3...v1.5.4)

Thanks to @austbot, fix - colorize fails at runtime.
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
zero-rails_openapi (1.5.4)
zero-rails_openapi (1.5.5)
activesupport (>= 3)
colorize (= 0.8.1)
rails (>= 3)
Expand Down
65 changes: 59 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
[![Build Status](https://travis-ci.org/zhandao/zero-rails_openapi.svg?branch=master)](https://travis-ci.org/zhandao/zero-rails_openapi)
[![Maintainability](https://api.codeclimate.com/v1/badges/471fd60f6eb7b019ceed/maintainability)](https://codeclimate.com/github/zhandao/zero-rails_openapi/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/471fd60f6eb7b019ceed/test_coverage)](https://codeclimate.com/github/zhandao/zero-rails_openapi/test_coverage)
[![Gitter Chat](https://badges.gitter.im/zero-rails_openapi/Lobby.svg)](https://gitter.im/zero-rails_openapi/Lobby)
[![Help Contribute to Open Source](https://www.codetriage.com/zhandao/zero-rails_openapi/badges/users.svg)](https://www.codetriage.com/zhandao/zero-rails_openapi)

Concise DSL for generating OpenAPI Specification 3 (**OAS3**, formerly Swagger3) JSON documentation for Rails application.

Expand All @@ -25,7 +23,20 @@
- [Configure](#configure)
- [Usage - DSL](#usage---dsl)
- [Basic DSL](#basic-dsl)
- [route_base](#1-route_base-optional-if-youre-writing-dsl-in-controller)
- [doc_tag](#2-doc_tag-optional)
- [components](#3-components-optional)
- [api_dry](#4-api_dry-optional)
- [api](#5-api-required)
- [DSL methods inside `api` and `api_dry`'s block](#dsl-methods-inside-api-and-api_drys-block)
- [this_api_is_invalid!](#1-this_api_is_invalid-its-aliases)
- [desc](#2-desc-description-for-the-current-api-and-its-inputs-parameters-and-request-body)
- [param family methods](#3-param-family-methods-oas---parameter-object)
- [request_body family methods](#4-request_body-family-methods-oas---request-body-object)
- [response family methods](#5-response-family-methods-oas---response-object)
- [callback](#6-callback-oas---callback-object)
- [Authentication and Authorization](#7-authentication-and-authorization)
- [server](#8-overriding-global-servers-by-server)
- [DSL methods inside `components`'s block](#dsl-methods-inside-componentss-block-code-source)
- [Run! - Generate JSON documentation file](#run---generate-json-documentation-file)
- [Use Swagger UI(very beautiful web page) to show your Documentation](#use-swagger-uivery-beautiful-web-page-to-show-your-documentation)
Expand All @@ -45,7 +56,7 @@

You can getting started from [swagger.io](https://swagger.io/docs/specification/basic-structure/)

**I suggest you should understand OAS3's basic structure at least.**
**I suggest you should understand the basic structure of OAS3 at least.**
such as component (can help you reuse DSL code, when your apis are used with the
same data structure).

Expand Down Expand Up @@ -150,7 +161,8 @@
```

For more example, see [goods_doc.rb](documentation/examples/goods_doc.rb), and
[examples_controller.rb](documentation/examples/examples_controller.rb)
[examples_controller.rb](documentation/examples/examples_controller.rb),
or [HERE](https://github.com/zhandao/zero-rails/tree/master/app/_docs/v1).

### Basic DSL ([source code](lib/open_api/dsl.rb))

Expand Down Expand Up @@ -492,8 +504,47 @@
```

**practice:** Automatically generate responses based on the agreed error class. [AutoGenDoc](documentation/examples/auto_gen_doc.rb#L63)

### (6) Callback (OAS - [Callback Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#callback-object))

[About Callbacks](https://swagger.io/docs/specification/callbacks/)
> In OpenAPI 3 specs, you can define callbacks – asynchronous, out-of-band requests that your service will send to some other service in response to certain events. This helps you improve the workflow your API offers to clients.
A typical example of a callback is a subscription functionality ... you can define the format of the “subscription” operation as well as the format of callback messages and expected responses to these messages.
This description will simplify communication between different servers and will help you standardize use of webhooks in your API.
[Complete YAML Example](https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/callback-example.yaml)

The structure of Callback Object:
```
callbacks:
Event1:
path1:
...
path2:
...
Event2:
...
```

To define callbacks, you can use `callback` method:
```ruby
# method signature
callback(event_name, http_method, callback_url, &block)
# usage
callback :myEvent, :post, 'localhost:3000/api/goods' do
query :name, String
data :token, String
response 200, 'success', :json, data: { name: String, description: String }
end
```

Use runtime expressions in callback_url:
```ruby
callback :myEvent, :post, '{body callback_addr}/api/goods/{query id}'
# the final URL will be: {$request.body#/callback_addr}/api/goods/{$request.query.id}
# Note: Other expressions outside "$request" are not supported yet
```

#### (6) Authentication and Authorization
#### (7) Authentication and Authorization

First of all, please make sure that you have read one of the following documents:
[OpenApi Auth](https://swagger.io/docs/specification/authentication/)
Expand Down Expand Up @@ -551,7 +602,7 @@
auth :OAuth, scopes: %w[ read_example admin ]
```

#### (7) Overriding Global Servers by `server`
#### (8) Overriding Global Servers by `server`

```ruby
# method signature
Expand Down Expand Up @@ -763,6 +814,8 @@
## Development
TODO ..
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
Expand Down
30 changes: 22 additions & 8 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,30 @@
- [配置](#configure)
- [DSL 介绍及用例](#usage---dsl)
- [基本的 DSL](#基本的-dsl)
- [route_base](#1-route_base-optional-if-youre-writing-dsl-in-controller)
- [doc_tag](#2-doc_tag-optional)
- [components](#3-components-optional)
- [api_dry](#4-api_dry-optional)
- [api](#5-api-required)
- [用于 `api``api_dry` 块内的 DSL(描述 API 的参数、响应等)](#dsl-methods-inside-api-and-api_drys-block)
- [this_api_is_invalid!](#1-this_api_is_invalid-its-aliases)
- [desc](#2-desc-description-for-the-current-api-and-its-inputs-parameters-and-request-body)
- [param family methods](#3-param-family-methods-oas---parameter-object)
- [request_body family methods](#4-request_body-family-methods-oas---request-body-object)
- [response family methods](#5-response-family-methods-oas---response-object)
- [callback](#6-callback-oas---callback-object)
- [Authentication and Authorization](#7-authentication-and-authorization)
- [server](#8-overriding-global-servers-by-server)
- [用于 `components` 块内的 DSL(描述可复用的组件)](#dsl-methods-inside-componentss-block-code-source)
- [执行文档生成](#run---generate-json-documentation-file)
- [使用 Swagger-UI 可视化所生成的文档](#use-swagger-uivery-beautiful-web-page-to-show-your-documentation)
- [技巧](#tricks)
- [将 DSL 写于他处,与控制器分离](#trick1---write-the-dsl-somewhere-else)
- [全局 DRY](#trick2---global-drying)
- [基于 enum 等信息自动生成参数描述](#trick3---auto-generate-description)
- [跳过或使用 DRY 时(`api_dry`)所定义的参数](#trick4---skip-or-use-parameters-define-in-api_dry)
- [基于 DB Schema 自动生成 response 的格式](#trick5---auto-generate-indexshow-actionss-response-types-based-on-db-schema)
- [定义组合的 Schema (one_of / all_of / any_of / not)](#trick6---combined-schema-one_of--all_of--any_of--not)
- [将 DSL 写于他处,与控制器分离](#trick1---write-the-dsl-somewhere-else)
- [全局 DRY](#trick2---global-drying)
- [基于 enum 等信息自动生成参数描述](#trick3---auto-generate-description)
- [跳过或使用 DRY 时(`api_dry`)所定义的参数](#trick4---skip-or-use-parameters-define-in-api_dry)
- [基于 DB Schema 自动生成 response 的格式](#trick5---auto-generate-indexshow-actionss-response-types-based-on-db-schema)
- [定义组合的 Schema (one_of / all_of / any_of / not)](#trick6---combined-schema-one_of--all_of--any_of--not)
- [问题集](#troubleshooting)
- [有关 `OpenApi.docs``OpenApi.routes_index`](#about-openapidocs-and-openapiroutes_index)

Expand Down Expand Up @@ -145,8 +158,9 @@
end
```

这有两份更详细的实例: [goods_doc.rb](documentation/examples/goods_doc.rb), 以及
[examples_controller.rb](documentation/examples/examples_controller.rb)
更多更详细的实例: [goods_doc.rb](documentation/examples/goods_doc.rb)
[examples_controller.rb](documentation/examples/examples_controller.rb),以及
[这里](https://github.com/zhandao/zero-rails/tree/master/app/_docs/v1)

### 基本的 DSL ([source code](lib/open_api/dsl.rb))

Expand Down
58 changes: 58 additions & 0 deletions lib/oas_objs/callback_obj.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require 'oas_objs/helpers'

module OpenApi
module DSL
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#callbackObject
class CallbackObj < Hash
include Helpers

attr_accessor :event_name, :http_method, :callback_url, :block

def initialize(event_name, http_method, callback_url, &block)
self.event_name = event_name
self.http_method = http_method
self.callback_url = callback_url
self.block = block
end

def process
{
self.event_name => {
processed_url => {
self.http_method.downcase.to_sym => processed_block
}
}
}
end

def processed_url
self.callback_url.gsub(/{[^{}]*}/) do |exp|
key_location, key_name = exp[1..-2].split
connector = key_location == 'body' ? '#/' : '.'
key_location = '$request.' + key_location
['{', key_location, connector, key_name, '}'].join
end
end

def processed_block
api = ApiInfo.new.merge! parameters: [ ], requestBody: '', responses: { }
api.instance_exec(&(self.block || ->{ }))
api.process_objs
api.delete_if { |_, v| v.blank? }
end
end
end
end


__END__

myEvent:
'{$request.body#/callbackUrl}':
post: # Method
requestBody: # Contents of the callback message
responses: # Expected responses

https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/callback-example.yaml
2 changes: 1 addition & 1 deletion lib/oas_objs/schema_obj.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def processed_type(type = self.type)
RefObj.new(:schema, t).process
elsif t.in? %w[ float double int32 int64 ]
{ type: t.match?('int') ? 'integer' : 'number', format: t }
elsif t.in? %w[ binary base64 ]
elsif t.in? %w[ binary base64 uri ]
{ type: 'string', format: t }
elsif t == 'file'
{ type: 'string', format: Config.file_format }
Expand Down
3 changes: 2 additions & 1 deletion lib/open_api/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ def api action, summary = '', http: http_method = nil, skip: [ ], use: [ ], &blo

api = ApiInfo.new(action_path, skip: Array(skip), use: Array(use))
.merge! description: '', summary: summary, operationId: action, tags: [@doc_tag],
parameters: [ ], requestBody: '', responses: { }, security: [ ], servers: [ ]
parameters: [ ], requestBody: '', responses: { }, callbacks: { },
links: { }, security: [ ], servers: [ ]
[action, :all].each { |blk_key| @zro_dry_blocks&.[](blk_key)&.each { |blk| api.instance_eval(&blk) } }
api.param_use = api.param_skip = [ ] # `skip` and `use` only affect `api_dry`'s blocks
api.instance_exec(&block) if block_given?
Expand Down
6 changes: 5 additions & 1 deletion lib/open_api/dsl/api_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class ApiInfo < Hash

attr_accessor :action_path, :param_skip, :param_use, :param_descs, :param_order

def initialize(action_path, skip: [ ], use: [ ])
def initialize(action_path = '', skip: [ ], use: [ ])
self.action_path = action_path
self.param_skip = skip
self.param_use = use
Expand Down Expand Up @@ -115,6 +115,10 @@ def security_require scheme_name, scopes: [ ]
alias auth security_require
alias need_auth security_require

def callback event_name, http_method, callback_url, &block
self[:callbacks].deep_merge! CallbackObj.new(event_name, http_method, callback_url, &block).process
end

def server url, desc: ''
self[:servers] << { url: url, description: desc }
end
Expand Down
1 change: 1 addition & 0 deletions lib/open_api/dsl/common_dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require 'oas_objs/request_body_obj'
require 'oas_objs/ref_obj'
require 'oas_objs/example_obj'
require 'oas_objs/callback_obj'
require 'open_api/dsl/helpers'

module OpenApi
Expand Down
2 changes: 1 addition & 1 deletion lib/open_api/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module OpenApi
VERSION = '1.5.4'
VERSION = '1.5.5'
end
36 changes: 35 additions & 1 deletion spec/api_info_obj_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
get_and_dig_doc %i[ paths goods/action get ]

ctx 'when doing nothing' do
api -> { }, eq: { summary: 'test', operationId: :action, tags: ['Goods'] }
api -> { }, get: { summary: 'test', operationId: :action, tags: ['Goods'] }
end


Expand Down Expand Up @@ -246,6 +246,40 @@
end


desc :callback, subject: :callbacks do
api -> do
callback :myEvent, :post, 'localhost:3000/api/goods' do
query :name, String
data :token, String
response 200, 'success', :json, data: { name: String, description: String }
end
end, has_key!: :myEvent
focus_on :myEvent, :'localhost:3000/api/goods', :post
expect_it has_keys: %i[ parameters requestBody responses ]

context 'when using with runtime expression' do
api -> do
query! :id, Integer
data :callback_addr!, String, pattern: /^http/

callback :myEvent, :post, '{body callback_addr}/api/goods/{query id}' do
response 200, 'success', :json, data: { name: String, description: String }
end
end, has_key: { myEvent: [:'{$request.body#/callback_addr}/api/goods/{$request.query.id}'] }
end

context 'when define multiple callbacks' do
api -> do
callback :Event1, :post, 'url1'
callback :Event1, :get, 'url1'
callback :Event2, :get, 'url2'
end, has_key!: { Event1: [:url1], Event2: [:url2] }
focus_on :Event1, :url1
expect_it has_keys: %i[ post get ]
end
end


desc :server, subject: :servers, stru: %i[ url description ] do
api -> { server 'http://localhost:3000', desc: 'Internal staging server for testing' },
all_have_keys: its_structure, has_size: 1
Expand Down
14 changes: 7 additions & 7 deletions spec/components_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
correct do
mk -> { schema :SchemaA, String; schema :SchemaZ, String }, doc_will_has_keys: { schemas: %i[ SchemaA SchemaZ ] }

mk -> { schema :SchemaA, String }, eq: { SchemaA: { type: 'string'} }
mk -> { schema :SchemaA, type: Integer }, eq: { SchemaA: { type: 'integer'} }
mk -> { schema :SchemaB => [ String ] }, eq: { SchemaB: { type: 'string'} }
mk -> { schema :SchemaC => [ type: String, desc: 'test' ] }, eq: { SchemaC: { type: 'string', description: 'test' } }
mk -> { schema :SchemaA, String }, get: { SchemaA: { type: 'string'} }
mk -> { schema :SchemaA, type: Integer }, get: { SchemaA: { type: 'integer'} }
mk -> { schema :SchemaB => [ String ] }, get: { SchemaB: { type: 'string'} }
mk -> { schema :SchemaC => [ type: String, desc: 'test' ] }, get: { SchemaC: { type: 'string', description: 'test' } }

context 'when defining combined schema' do
mk -> { schema :SchemaD => [ one_of: [String] ] }, has_keys: { SchemaD: [:oneOf] }
Expand All @@ -26,7 +26,7 @@

desc :example, subject: :examples do
mk -> { example :ExampleA, { }; example :ExampleZ, { } }, doc_will_has_keys: { examples: %i[ ExampleA ExampleZ ] }
mk -> { example :ExampleA, { name: 'BeiGou' } }, eq: { ExampleA: [{ name: { value: 'BeiGou' } }] }
mk -> { example :ExampleA, { name: 'BeiGou' } }, get: { ExampleA: [{ name: { value: 'BeiGou' } }] }
end


Expand Down Expand Up @@ -127,11 +127,11 @@
end, doc_will_has_keys: { securitySchemes: %i[ OAuth ] }

describe '#base_auth' do
mk -> { base_auth :BaseAuth }, eq: { BaseAuth: { type: 'http', scheme: 'basic' }}
mk -> { base_auth :BaseAuth }, get: { BaseAuth: { type: 'http', scheme: 'basic' }}
end

describe '#bearer_auth' do
mk -> { bearer_auth :Token }, eq: { Token: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }}
mk -> { bearer_auth :Token }, get: { Token: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }}
end

describe '#api_key' do
Expand Down
Loading

0 comments on commit 76df0fe

Please sign in to comment.