zero-rails_openapi 1.5.1 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -1
- data/Gemfile.lock +2 -2
- data/README.md +17 -17
- data/README_zh.md +17 -17
- data/documentation/examples/auto_gen_doc.rb +3 -3
- data/documentation/examples/examples_controller.rb +1 -1
- data/documentation/examples/open_api.rb +3 -3
- data/documentation/parameter.md +1 -1
- data/lib/oas_objs/combined_schema.rb +16 -0
- data/lib/oas_objs/helpers.rb +2 -14
- data/lib/oas_objs/schema_obj.rb +2 -2
- data/lib/open_api.rb +1 -1
- data/lib/open_api/config.rb +3 -3
- data/lib/open_api/config_dsl.rb +5 -5
- data/lib/open_api/dsl.rb +32 -31
- data/lib/open_api/dsl/{api_info_obj.rb → api_info.rb} +7 -7
- data/lib/open_api/dsl/components.rb +4 -4
- data/lib/open_api/dsl/helpers.rb +1 -1
- data/lib/open_api/generator.rb +37 -31
- data/lib/open_api/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f7b1eb53af2017d4b4b522191cd0b59e9a69df8
|
4
|
+
data.tar.gz: e476592261a8979af7ce367dce37485a0b6f85e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4022825604733ac97526339adbcd940603b12465e0cfe69fddc3317e3359d1ab019c2275e086aad688b447611baf2c51328682a2730adb79e2a1654949c60196
|
7
|
+
data.tar.gz: 250f949531bb8d03cc1b386b55dd4d7026ed67eeeef7fb42b42fbf778ff624819a221d2ab0b21157f429309cb6d590085e1cfea03618b8d0e7abb4a9f7bf85d0
|
data/CHANGELOG.md
CHANGED
@@ -2,7 +2,28 @@
|
|
2
2
|
|
3
3
|
## [Unreleased]
|
4
4
|
|
5
|
-
## [1.5.
|
5
|
+
## [1.5.2] - 2018/1/2 - [view diff](https://github.com/zhandao/zero-rails_openapi/compare/v1.5.1...v1.5.2)
|
6
|
+
|
7
|
+
## Added
|
8
|
+
|
9
|
+
1. `do_*` can be passed common schema after (or before) `by:`.
|
10
|
+
2. when this action can be accessed through multiple HTTP methods (but not set through `match`),
|
11
|
+
it also matches and generate both HTTP methods.
|
12
|
+
|
13
|
+
## Changed
|
14
|
+
|
15
|
+
1. `root_controller` => `base_doc_class`.
|
16
|
+
2. `ctrl_path` => `ctrl_base`.
|
17
|
+
3. `apis_tag` => `doc_tag`.
|
18
|
+
4. `@_ctrl_infos` => `@doc_info`, `@_api_infos` => `@api_info`, `@_apis_dry_blocks` => `@zro_dry_blocks`.
|
19
|
+
5. `OpenApi.paths_index` => `OpenApi.routes_index`.
|
20
|
+
6. `get_actions_by_ctrl_path` => `get_actions_by_route_base`.
|
21
|
+
7. `Config.dft_file_format` => `Config.file_format`.
|
22
|
+
8. Modify the description of the test case (remove `should`).
|
23
|
+
9. `deep_merge!` instead of `_fusion`.
|
24
|
+
10. `ApiInfoObj` => `ApiInfo`.
|
25
|
+
|
26
|
+
## [1.5.1 - 100% Test Coverage] - 2017/12/21 - [view diff](https://github.com/zhandao/zero-rails_openapi/compare/v1.4.3...v1.5.1)
|
6
27
|
|
7
28
|
### Completed the test code (250+ examples), and make it 100% coverage.
|
8
29
|
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
zero-rails_openapi (1.5.
|
4
|
+
zero-rails_openapi (1.5.2)
|
5
5
|
activesupport (>= 3)
|
6
6
|
rails (>= 3)
|
7
7
|
|
@@ -66,7 +66,7 @@ GEM
|
|
66
66
|
mini_mime (1.0.0)
|
67
67
|
mini_portile2 (2.3.0)
|
68
68
|
minitest (5.10.3)
|
69
|
-
nio4r (2.
|
69
|
+
nio4r (2.2.0)
|
70
70
|
nokogiri (1.8.1)
|
71
71
|
mini_portile2 (~> 2.3.0)
|
72
72
|
rack (2.0.3)
|
data/README.md
CHANGED
@@ -36,7 +36,7 @@
|
|
36
36
|
- [Atuo Generate index/show Actions's Responses Based on DB Schema](#trick5---auto-generate-indexshow-actionss-response-types-based-on-db-schema)
|
37
37
|
- [Combined Schema (one_of / all_of / any_of / not)](#trick6---combined-schema-one_of--all_of--any_of--not)
|
38
38
|
- [Troubleshooting](#troubleshooting)
|
39
|
-
- [About `OpenApi.docs` and `OpenApi.
|
39
|
+
- [About `OpenApi.docs` and `OpenApi.routes_index`](#about-openapidocs-and-openapiroutes_index)
|
40
40
|
|
41
41
|
## About OAS
|
42
42
|
|
@@ -83,8 +83,8 @@
|
|
83
83
|
c.open_api_docs = {
|
84
84
|
# The definition of the document `homepage`.
|
85
85
|
homepage: {
|
86
|
-
# [REQUIRED] ZRO will scan all the descendants of
|
87
|
-
|
86
|
+
# [REQUIRED] ZRO will scan all the descendants of base_doc_class, then generate their docs.
|
87
|
+
base_doc_class: Api::V1::BaseController,
|
88
88
|
|
89
89
|
# [REQUIRED] OAS Info Object: The section contains API information.
|
90
90
|
info: {
|
@@ -112,7 +112,7 @@
|
|
112
112
|
|
113
113
|
OpenApi::Config.tap do |c|
|
114
114
|
c.instance_eval do
|
115
|
-
open_api :homepage_api,
|
115
|
+
open_api :homepage_api, base_doc_class: ApiDoc
|
116
116
|
info version: '1.0.0', title: 'Homepage APIs'
|
117
117
|
end
|
118
118
|
end
|
@@ -138,7 +138,7 @@
|
|
138
138
|
Here is the simplest usage:
|
139
139
|
|
140
140
|
```ruby
|
141
|
-
class Api::
|
141
|
+
class Api::ExamplesController < ApiController
|
142
142
|
api :index, 'GET list' do
|
143
143
|
query :page, Integer#, desc: 'page, greater than 1', range: { ge: 1 }, dft: 1
|
144
144
|
query :rows, Integer#, desc: 'per page', range: { ge: 1 }, default: 10
|
@@ -151,26 +151,26 @@
|
|
151
151
|
|
152
152
|
### DSL as class methods ([source code](lib/open_api/dsl.rb))
|
153
153
|
|
154
|
-
#### (1) `
|
154
|
+
#### (1) `route_base` [optional if you're writing DSL in controller]
|
155
155
|
|
156
156
|
```ruby
|
157
157
|
# method signature
|
158
|
-
|
158
|
+
route_base(path)
|
159
159
|
# usage
|
160
|
-
|
160
|
+
route_base 'api/v1/examples'
|
161
161
|
```
|
162
|
-
It is optional because `
|
162
|
+
It is optional because `route_base` defaults to `controller_path`.
|
163
163
|
|
164
|
-
[Here's a trick](#trick1---write-the-dsl-somewhere-else): Using `
|
164
|
+
[Here's a trick](#trick1---write-the-dsl-somewhere-else): Using `route_base`, you can write the DSL somewhere else
|
165
165
|
to simplify the current controller.
|
166
166
|
|
167
|
-
#### (2) `
|
167
|
+
#### (2) `doc_tag` [optional]
|
168
168
|
|
169
169
|
```ruby
|
170
170
|
# method signature
|
171
|
-
|
171
|
+
doc_tag(name: nil, desc: '', external_doc_url: nil)
|
172
172
|
# usage
|
173
|
-
|
173
|
+
doc_tag name: 'ExampleTagName', desc: 'ExamplesController\'s APIs'
|
174
174
|
```
|
175
175
|
This method allows you to set the Tag (which is a node of [OpenApi Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#openapi-object)).
|
176
176
|
|
@@ -640,7 +640,7 @@
|
|
640
640
|
```ruby
|
641
641
|
# config/initializers/open_api.rb
|
642
642
|
# in your configuration
|
643
|
-
|
643
|
+
base_doc_class: ApiDoc
|
644
644
|
|
645
645
|
# app/api_doc/api_doc.rb
|
646
646
|
require 'open_api/dsl'
|
@@ -651,7 +651,7 @@
|
|
651
651
|
|
652
652
|
# app/api_doc/v1/examples_doc.rb
|
653
653
|
class V1::ExamplesDoc < ApiDoc
|
654
|
-
|
654
|
+
route_base 'api/v1/examples'
|
655
655
|
|
656
656
|
api :index do
|
657
657
|
# ...
|
@@ -750,14 +750,14 @@
|
|
750
750
|
3. Put `c.rails_routes_file = 'config/routes.txt'` to your ZRO config.
|
751
751
|
|
752
752
|
|
753
|
-
## About `OpenApi.docs` and `OpenApi.
|
753
|
+
## About `OpenApi.docs` and `OpenApi.routes_index`
|
754
754
|
|
755
755
|
After `OpenApi.write_docs`, the above two module variables will be generated.
|
756
756
|
|
757
757
|
`OpenApi.docs`: A Hash with API names as keys, and documents of each APIs as values.
|
758
758
|
documents are instances of ActiveSupport::HashWithIndifferentAccess.
|
759
759
|
|
760
|
-
`OpenApi.
|
760
|
+
`OpenApi.routes_index`: Inverted index of controller path to API name mappings.
|
761
761
|
Like: `{ 'api/v1/examples' => :homepage_api }`
|
762
762
|
It's useful when you want to look up a document based on a controller and do something.
|
763
763
|
|
data/README_zh.md
CHANGED
@@ -13,7 +13,7 @@
|
|
13
13
|
|
14
14
|
**这里是栈刀 = ▽ =
|
15
15
|
如果你在寻找能清晰书写 OAS API 文档的 DSL 工具,俺这个还挺不错的 ~
|
16
|
-
你还可以复用其所[产出](#about-openapidocs-and-
|
16
|
+
你还可以复用其所[产出](#about-openapidocs-and-openapiroutes_index)来写一些扩展,比如参数自动校验什么的(我有写哦)。
|
17
17
|
有什么想法敬请 PR,谢过!
|
18
18
|
另外,走过路过不妨来个 star?**
|
19
19
|
|
@@ -39,7 +39,7 @@
|
|
39
39
|
- [基于 DB Schema 自动生成 response 的格式](#trick5---auto-generate-indexshow-actionss-response-types-based-on-db-schema)
|
40
40
|
- [定义组合的 Schema (one_of / all_of / any_of / not)](#trick6---combined-schema-one_of--all_of--any_of--not)
|
41
41
|
- [问题集](#troubleshooting)
|
42
|
-
- [有关 `OpenApi.docs` 和 `OpenApi.
|
42
|
+
- [有关 `OpenApi.docs` 和 `OpenApi.routes_index`](#about-openapidocs-and-openapiroutes_index)
|
43
43
|
|
44
44
|
## About OAS
|
45
45
|
|
@@ -81,8 +81,8 @@
|
|
81
81
|
c.open_api_docs = {
|
82
82
|
# 对文档 `homepage` 进行定义
|
83
83
|
homepage: {
|
84
|
-
# [REQUIRED] ZRO will scan all the descendants of
|
85
|
-
|
84
|
+
# [REQUIRED] ZRO will scan all the descendants of base_doc_class, then generate their docs.
|
85
|
+
base_doc_class: Api::V1::BaseController,
|
86
86
|
|
87
87
|
# [REQUIRED] OAS Info Object: The section contains API information.
|
88
88
|
info: {
|
@@ -109,7 +109,7 @@
|
|
109
109
|
|
110
110
|
OpenApi::Config.tap do |c|
|
111
111
|
c.instance_eval do
|
112
|
-
open_api :homepage_api,
|
112
|
+
open_api :homepage_api, base_doc_class: ApiDoc
|
113
113
|
info version: '1.0.0', title: 'Homepage APIs'
|
114
114
|
end
|
115
115
|
end
|
@@ -135,7 +135,7 @@
|
|
135
135
|
这是一个最简单的实例:
|
136
136
|
|
137
137
|
```ruby
|
138
|
-
class Api::
|
138
|
+
class Api::ExamplesController < ApiController
|
139
139
|
api :index, 'GET list' do
|
140
140
|
query :page, Integer#, desc: 'page, greater than 1', range: { ge: 1 }, dft: 1
|
141
141
|
query :rows, Integer#, desc: 'per page', range: { ge: 1 }, default: 10
|
@@ -148,25 +148,25 @@
|
|
148
148
|
|
149
149
|
### 作为类方法的 DSL ([source code](lib/open_api/dsl.rb))
|
150
150
|
|
151
|
-
#### (1) `
|
151
|
+
#### (1) `route_base` [无需调用,当且仅当你是在控制器中写文档时]
|
152
152
|
|
153
153
|
```ruby
|
154
154
|
# method signature
|
155
|
-
|
155
|
+
route_base(path)
|
156
156
|
# usage
|
157
|
-
|
157
|
+
route_base 'api/v1/examples'
|
158
158
|
```
|
159
159
|
其默认设定为 `controller_path`.
|
160
160
|
|
161
|
-
[这个技巧](#trick1---write-the-dsl-somewhere-else) 展示如何使用 `
|
161
|
+
[这个技巧](#trick1---write-the-dsl-somewhere-else) 展示如何使用 `route_base` 来让你将 DSL 写在他处(与控制器分离),来简化你的控制器。
|
162
162
|
|
163
|
-
#### (2) `
|
163
|
+
#### (2) `doc_tag` [optional]
|
164
164
|
|
165
165
|
```ruby
|
166
166
|
# method signature
|
167
|
-
|
167
|
+
doc_tag(name: nil, desc: '', external_doc_url: '')
|
168
168
|
# usage
|
169
|
-
|
169
|
+
doc_tag name: 'ExampleTagName', desc: 'ExamplesController\'s APIs'
|
170
170
|
```
|
171
171
|
This method allows you to set the Tag (which is a node of [OpenApi Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#openapi-object)).
|
172
172
|
|
@@ -617,7 +617,7 @@
|
|
617
617
|
```ruby
|
618
618
|
# config/initializers/open_api.rb
|
619
619
|
# in your configuration
|
620
|
-
|
620
|
+
base_doc_class: ApiDoc
|
621
621
|
|
622
622
|
# app/api_doc/api_doc.rb
|
623
623
|
require 'open_api/dsl'
|
@@ -628,7 +628,7 @@
|
|
628
628
|
|
629
629
|
# app/api_doc/v1/examples_doc.rb
|
630
630
|
class V1::ExamplesDoc < ApiDoc
|
631
|
-
|
631
|
+
route_base 'api/v1/examples'
|
632
632
|
|
633
633
|
api :index do
|
634
634
|
# ...
|
@@ -727,14 +727,14 @@
|
|
727
727
|
3. Put `c.rails_routes_file = 'config/routes.txt'` to your ZRO config.
|
728
728
|
|
729
729
|
|
730
|
-
## About `OpenApi.docs` and `OpenApi.
|
730
|
+
## About `OpenApi.docs` and `OpenApi.routes_index`
|
731
731
|
|
732
732
|
After `OpenApi.write_docs`, the above two module variables will be generated.
|
733
733
|
|
734
734
|
`OpenApi.docs`: A Hash with API names as keys, and documents of each APIs as values.
|
735
735
|
documents are instances of ActiveSupport::HashWithIndifferentAccess.
|
736
736
|
|
737
|
-
`OpenApi.
|
737
|
+
`OpenApi.routes_index`: Inverted index of controller path to API name mappings.
|
738
738
|
Like: `{ 'api/v1/examples' => :homepage_api }`
|
739
739
|
It's useful when you want to look up a document based on a controller and do something.
|
740
740
|
|
@@ -11,7 +11,7 @@ module AutoGenDoc
|
|
11
11
|
super
|
12
12
|
subclass.class_eval do
|
13
13
|
break unless self.name.match?(/sController|sDoc/)
|
14
|
-
|
14
|
+
route_base self.name.sub('Doc', '').downcase.gsub('::', '/') if self.name.match?(/sDoc/)
|
15
15
|
open_api_dry
|
16
16
|
end
|
17
17
|
end
|
@@ -19,8 +19,8 @@ module AutoGenDoc
|
|
19
19
|
private
|
20
20
|
|
21
21
|
def open_api_dry
|
22
|
-
|
23
|
-
::OpenApi::Generator.
|
22
|
+
route_base = try(:controller_path) || instance_variable_get('@route_base')
|
23
|
+
::OpenApi::Generator.get_actions_by_route_base(route_base)&.each do |action|
|
24
24
|
api_dry action do
|
25
25
|
header! 'Token', String, desc: 'user token'
|
26
26
|
|
@@ -3,7 +3,7 @@ require 'open_api'
|
|
3
3
|
OpenApi::Config.tap do |c|
|
4
4
|
# Config DSL
|
5
5
|
c.instance_eval do
|
6
|
-
open_api :zero_rails,
|
6
|
+
open_api :zero_rails, base_doc_class: ApiDoc
|
7
7
|
info version: '0.0.1', title: 'Zero Rails APIs', description: 'API documentation of Zero-Rails Application.'
|
8
8
|
server 'http://localhost:3000', desc: 'Main (production) server'
|
9
9
|
server 'http://localhost:3000', desc: 'Internal staging server for testing'
|
@@ -21,8 +21,8 @@ OpenApi::Config.tap do |c|
|
|
21
21
|
# Getting started: https://swagger.io/docs/specification/basic-structure/
|
22
22
|
c.open_api_docs = {
|
23
23
|
blog_api: {
|
24
|
-
# [REQUIRED] ZRO will scan all the descendants of the
|
25
|
-
|
24
|
+
# [REQUIRED] ZRO will scan all the descendants of the base_doc_class, then generate their docs.
|
25
|
+
base_doc_class: ApiController,
|
26
26
|
|
27
27
|
# [REQUIRED] Info Object: The info section contains API information
|
28
28
|
info: {
|
data/documentation/parameter.md
CHANGED
@@ -20,7 +20,7 @@ int32, float, date ...
|
|
20
20
|
All the types you can use as following:
|
21
21
|
- **String, 'binary', 'base64'**
|
22
22
|
- **Integer, Long, 'int32', 'int64', Float, Double**
|
23
|
-
- **File** (it will be converted to `{ type: 'string', format: Config.
|
23
|
+
- **File** (it will be converted to `{ type: 'string', format: Config.file_format }`)
|
24
24
|
- **Date, DateTime**
|
25
25
|
- **Boolean**
|
26
26
|
- **Array**: `Array[String]` or `[String]`
|
data/lib/oas_objs/helpers.rb
CHANGED
@@ -1,19 +1,7 @@
|
|
1
1
|
module OpenApi
|
2
2
|
module Helpers
|
3
3
|
def fusion
|
4
|
-
proc { |a, b| a.
|
5
|
-
end
|
6
|
-
|
7
|
-
def _fusion
|
8
|
-
proc do |_common_key, x, y|
|
9
|
-
if x.is_a?(Hash) && y.is_a?(Hash)
|
10
|
-
x.merge(y, &_fusion)
|
11
|
-
elsif x.is_a?(Array) && y.is_a?(Array)
|
12
|
-
x.concat(y)
|
13
|
-
else
|
14
|
-
y
|
15
|
-
end
|
16
|
-
end
|
4
|
+
proc { |a, b| a.deep_merge!(b) { |common_key, va, vb| common_key == :required ? va + vb : vb } }
|
17
5
|
end
|
18
6
|
|
19
7
|
def truly_present?(obj)
|
@@ -32,7 +20,7 @@ module OpenApi
|
|
32
20
|
|
33
21
|
# reducx.then_merge! => for Hash
|
34
22
|
def reducx(*values)
|
35
|
-
@assign = values.compact.reduce({ }, :merge).keep_if &value_present
|
23
|
+
@assign = values.compact.reduce({ }, :merge!).keep_if &value_present
|
36
24
|
self
|
37
25
|
end
|
38
26
|
|
data/lib/oas_objs/schema_obj.rb
CHANGED
@@ -69,7 +69,7 @@ module OpenApi
|
|
69
69
|
elsif t.in? %w[ binary base64 ]
|
70
70
|
{ type: 'string', format: t }
|
71
71
|
elsif t == 'file'
|
72
|
-
{ type: 'string', format: Config.
|
72
|
+
{ type: 'string', format: Config.file_format }
|
73
73
|
elsif t == 'datetime'
|
74
74
|
{ type: 'string', format: 'date-time' }
|
75
75
|
else # other string
|
@@ -110,7 +110,7 @@ module OpenApi
|
|
110
110
|
{ minItems: min, maxItems: max }
|
111
111
|
else
|
112
112
|
{ minLength: min, maxLength: max }
|
113
|
-
end.merge(enum: _enum).keep_if &value_present
|
113
|
+
end.merge!(enum: _enum).keep_if &value_present
|
114
114
|
end
|
115
115
|
|
116
116
|
def processed_range
|
data/lib/open_api.rb
CHANGED
data/lib/open_api/config.rb
CHANGED
@@ -32,8 +32,8 @@ module OpenApi
|
|
32
32
|
{
|
33
33
|
# # [REQUIRED] At least one doc.
|
34
34
|
# zero_rails: {
|
35
|
-
# # [REQUIRED] ZRO will scan all the descendants of the
|
36
|
-
#
|
35
|
+
# # [REQUIRED] ZRO will scan all the descendants of the base_doc_class, and then generate their docs.
|
36
|
+
# base_doc_class: ApplicationController,
|
37
37
|
#
|
38
38
|
# # [REQUIRED] Info Object: The info section contains API information
|
39
39
|
# info: {
|
@@ -47,7 +47,7 @@ module OpenApi
|
|
47
47
|
}
|
48
48
|
end
|
49
49
|
|
50
|
-
cattr_accessor :
|
50
|
+
cattr_accessor :file_format do
|
51
51
|
'binary'
|
52
52
|
end
|
53
53
|
|
data/lib/open_api/config_dsl.rb
CHANGED
@@ -4,9 +4,9 @@ module OpenApi
|
|
4
4
|
base.class_eval do
|
5
5
|
module_function
|
6
6
|
|
7
|
-
def open_api name,
|
7
|
+
def open_api name, base_doc_class:
|
8
8
|
@api = name
|
9
|
-
open_api_docs[name] = {
|
9
|
+
open_api_docs[name] = { base_doc_class: base_doc_class }
|
10
10
|
end
|
11
11
|
|
12
12
|
def info version:, title:, desc: '', **addition
|
@@ -23,16 +23,16 @@ module OpenApi
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def base_auth scheme_name, other_info = { }
|
26
|
-
security_scheme scheme_name, { type: 'http', scheme: 'basic' }
|
26
|
+
security_scheme scheme_name, { type: 'http', scheme: 'basic', **other_info }
|
27
27
|
end
|
28
28
|
|
29
29
|
def bearer_auth scheme_name, format = 'JWT', other_info = { }
|
30
|
-
security_scheme scheme_name, { type: 'http', scheme: 'bearer', bearerFormat: format }
|
30
|
+
security_scheme scheme_name, { type: 'http', scheme: 'bearer', bearerFormat: format, **other_info }
|
31
31
|
end
|
32
32
|
|
33
33
|
def api_key scheme_name, field:, in: 'header', **other_info
|
34
34
|
_in = binding.local_variable_get(:in)
|
35
|
-
security_scheme scheme_name, { type: 'apiKey', name: field, in: _in }
|
35
|
+
security_scheme scheme_name, { type: 'apiKey', name: field, in: _in, **other_info }
|
36
36
|
end
|
37
37
|
|
38
38
|
def global_security_require scheme_name, scopes: [ ]
|
data/lib/open_api/dsl.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'open_api/dsl/
|
1
|
+
require 'open_api/dsl/api_info'
|
2
2
|
require 'open_api/dsl/components'
|
3
3
|
|
4
4
|
module OpenApi
|
@@ -9,58 +9,59 @@ module OpenApi
|
|
9
9
|
|
10
10
|
# TODO: Doc-Block Comments
|
11
11
|
module ClassMethods
|
12
|
-
def
|
13
|
-
@
|
14
|
-
@
|
12
|
+
def route_base path
|
13
|
+
@route_base = path
|
14
|
+
@doc_tag = path.split('/').last.camelize
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
#
|
19
|
-
@
|
20
|
-
@
|
21
|
-
tag = (@
|
17
|
+
def doc_tag name: nil, desc: '', external_doc_url: nil
|
18
|
+
# apis will group by the tags.
|
19
|
+
@doc_tag = name if name.present?
|
20
|
+
@doc_tag ||= controller_name.camelize
|
21
|
+
tag = (@doc_info = { })[:tag] = { name: @doc_tag }
|
22
22
|
tag[:description] = desc if desc.present?
|
23
|
-
tag[:externalDocs] = { description: 'ref', url: external_doc_url } if external_doc_url
|
23
|
+
tag[:externalDocs] = { description: 'ref', url: external_doc_url } if external_doc_url
|
24
24
|
end
|
25
25
|
|
26
26
|
def components &block
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
doc_tag if @doc_info.nil?
|
28
|
+
current_doc = @doc_info[:components] = Components.new
|
29
|
+
current_doc.instance_exec(&block)
|
30
|
+
current_doc.process_objs
|
31
31
|
end
|
32
32
|
|
33
|
-
def api action, summary = '', http: nil, skip: [ ], use: [ ], &block
|
34
|
-
|
33
|
+
def api action, summary = '', http: http_method = nil, skip: [ ], use: [ ], &block
|
34
|
+
doc_tag if @doc_info.nil?
|
35
35
|
# select the routing info (corresponding to the current method) from routing list.
|
36
|
-
action_path = "#{@
|
37
|
-
|
38
|
-
pp "[ZRO Warning] Routing mapping failed: #{@
|
36
|
+
action_path = "#{@route_base ||= controller_path}##{action}"
|
37
|
+
routes = ctrl_routes_list&.select { |api| api[:action_path].match?(/^#{action_path}$/) }
|
38
|
+
pp "[ZRO Warning] Routing mapping failed: #{@route_base}##{action}" and return if routes.blank?
|
39
39
|
|
40
|
-
api =
|
41
|
-
|
42
|
-
|
43
|
-
[action, :all].each { |blk_key| @
|
44
|
-
api.param_use
|
45
|
-
api.param_skip = [ ]
|
46
|
-
api.param_use = [ ] # `skip` and `use` only affect `api_dry`'s blocks
|
40
|
+
api = ApiInfo.new(action_path, skip: Array(skip), use: Array(use))
|
41
|
+
.merge! description: '', summary: summary, operationId: action, tags: [@doc_tag],
|
42
|
+
parameters: [ ], requestBody: '', responses: { }, security: [ ], servers: [ ]
|
43
|
+
[action, :all].each { |blk_key| @zro_dry_blocks&.[](blk_key)&.each { |blk| api.instance_eval(&blk) } }
|
44
|
+
api.param_use = api.param_skip = [ ] # `skip` and `use` only affect `api_dry`'s blocks
|
47
45
|
api.instance_exec(&block) if block_given?
|
48
46
|
api.process_objs
|
49
47
|
api.delete_if { |_, v| v.blank? }
|
50
48
|
|
51
|
-
|
52
|
-
|
49
|
+
routes.each do |route|
|
50
|
+
path = (@api_info ||= { })[route[:path]] ||= { }
|
51
|
+
(http || route[:http_verb]).split('|').each { |verb| path[verb] = api }
|
52
|
+
end
|
53
|
+
|
53
54
|
api
|
54
55
|
end
|
55
56
|
|
56
57
|
# method could be symbol array, like: %i[ .. ]
|
57
58
|
def api_dry action = :all, desc = '', &block
|
58
|
-
@
|
59
|
-
Array(action).each { |a| (@
|
59
|
+
@zro_dry_blocks ||= { }
|
60
|
+
Array(action).each { |a| (@zro_dry_blocks[a.to_sym] ||= [ ]) << block }
|
60
61
|
end
|
61
62
|
|
62
63
|
def ctrl_routes_list
|
63
|
-
Generator.routes_list[@
|
64
|
+
Generator.routes_list[@route_base]
|
64
65
|
end
|
65
66
|
end
|
66
67
|
end
|
@@ -2,7 +2,7 @@ require 'open_api/dsl/common_dsl'
|
|
2
2
|
|
3
3
|
module OpenApi
|
4
4
|
module DSL
|
5
|
-
class
|
5
|
+
class ApiInfo < Hash
|
6
6
|
include DSL::CommonDSL
|
7
7
|
include DSL::Helpers
|
8
8
|
|
@@ -57,12 +57,12 @@ module OpenApi
|
|
57
57
|
# :export! => { type: Boolean }
|
58
58
|
# }
|
59
59
|
%i[ header header! path path! query query! cookie cookie! ].each do |param_type|
|
60
|
-
define_method "do_#{param_type}" do |by
|
61
|
-
by.each do |
|
60
|
+
define_method "do_#{param_type}" do |by:, **common_schema|
|
61
|
+
by.each do |p_name, schema|
|
62
62
|
action = param_type.to_s.delete('!')
|
63
|
-
type,
|
64
|
-
args = [
|
65
|
-
param_type['!'] ||
|
63
|
+
type, schema = schema.is_a?(Hash) ? [schema[:type], schema] : [schema, { }]
|
64
|
+
args = [ p_name.to_s.delete('!').to_sym, type, schema.reverse_merge!(common_schema) ]
|
65
|
+
param_type['!'] || p_name['!'] ? send("#{action}!", *args) : send(action, *args)
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
@@ -75,7 +75,7 @@ module OpenApi
|
|
75
75
|
def request_body required, media_type, data: { }, **options
|
76
76
|
desc = options.delete(:desc) || ''
|
77
77
|
self[:requestBody] = RequestBodyObj.new(required, desc) unless self[:requestBody].is_a?(RequestBodyObj)
|
78
|
-
self[:requestBody].add_or_fusion(media_type,
|
78
|
+
self[:requestBody].add_or_fusion(media_type, { data: data , **options })
|
79
79
|
end
|
80
80
|
|
81
81
|
# [ body body! ]
|
@@ -43,7 +43,7 @@ module OpenApi
|
|
43
43
|
desc = options.delete(:desc) || ''
|
44
44
|
cur = (self[:requestBodies] ||= { })[component_key]
|
45
45
|
cur = RequestBodyObj.new(required, desc) unless cur.is_a?(RequestBodyObj)
|
46
|
-
self[:requestBodies][component_key] = cur.add_or_fusion(media_type,
|
46
|
+
self[:requestBodies][component_key] = cur.add_or_fusion(media_type, { data: data, **options })
|
47
47
|
end
|
48
48
|
|
49
49
|
# [ body body! ]
|
@@ -65,18 +65,18 @@ module OpenApi
|
|
65
65
|
alias auth_scheme security_scheme
|
66
66
|
|
67
67
|
def base_auth scheme_name, other_info = { }
|
68
|
-
security_scheme scheme_name, { type: 'http', scheme: 'basic' }
|
68
|
+
security_scheme scheme_name, { type: 'http', scheme: 'basic', **other_info }
|
69
69
|
end
|
70
70
|
arrow_enable :base_auth
|
71
71
|
|
72
72
|
def bearer_auth scheme_name, format = 'JWT', other_info = { }
|
73
|
-
security_scheme scheme_name, { type: 'http', scheme: 'bearer', bearerFormat: format }
|
73
|
+
security_scheme scheme_name, { type: 'http', scheme: 'bearer', bearerFormat: format, **other_info }
|
74
74
|
end
|
75
75
|
arrow_enable :bearer_auth
|
76
76
|
|
77
77
|
def api_key scheme_name, field:, in: 'header', **other_info
|
78
78
|
_in = binding.local_variable_get(:in)
|
79
|
-
security_scheme scheme_name, { type: 'apiKey', name: field, in: _in }
|
79
|
+
security_scheme scheme_name, { type: 'apiKey', name: field, in: _in, **other_info }
|
80
80
|
end
|
81
81
|
arrow_enable :api_key
|
82
82
|
|
data/lib/open_api/dsl/helpers.rb
CHANGED
data/lib/open_api/generator.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
require 'active_support/hash_with_indifferent_access'
|
1
2
|
require 'open_api/config'
|
2
3
|
|
3
4
|
module OpenApi
|
4
5
|
module Generator
|
6
|
+
extend self
|
7
|
+
|
5
8
|
def self.included(base)
|
6
9
|
base.extend ClassMethods
|
7
10
|
end
|
@@ -18,31 +21,33 @@ module OpenApi
|
|
18
21
|
# :nocov:
|
19
22
|
end
|
20
23
|
Dir[*Array(Config.doc_location)].each { |file| require file }
|
21
|
-
(doc_name || Config.docs.keys).map { |name| { name => generate_doc(name) } }.reduce({ }, :merge)
|
24
|
+
(doc_name || Config.docs.keys).map { |name| { name => generate_doc(name) } }.reduce({ }, :merge!)
|
22
25
|
end
|
23
26
|
|
24
27
|
def generate_doc(doc_name)
|
25
28
|
settings = Config.docs[doc_name]
|
26
|
-
doc = { openapi: '3.0.0'
|
29
|
+
doc = { openapi: '3.0.0', **settings.slice(:info, :servers) }.merge!(
|
27
30
|
security: settings[:global_security], tags: [ ], paths: { },
|
28
31
|
components: {
|
29
32
|
securitySchemes: { }, schemas: { }, parameters: { }, requestBodies: { }
|
30
|
-
}.merge(settings[:components] || { })
|
33
|
+
}.merge!(settings[:components] || { })
|
31
34
|
)
|
32
35
|
|
33
|
-
settings[:
|
34
|
-
|
35
|
-
next if
|
36
|
-
|
37
|
-
doc[:
|
38
|
-
doc[:
|
39
|
-
|
36
|
+
settings[:base_doc_class].descendants.each do |ctrl|
|
37
|
+
doc_info = ctrl.instance_variable_get('@doc_info')
|
38
|
+
next if doc_info.nil?
|
39
|
+
|
40
|
+
doc[:paths].merge!(ctrl.instance_variable_get('@api_info') || { })
|
41
|
+
doc[:tags] << doc_info[:tag]
|
42
|
+
doc[:components].deep_merge!(doc_info[:components] || { })
|
43
|
+
OpenApi.routes_index[ctrl.instance_variable_get('@route_base')] = doc_name
|
40
44
|
end
|
45
|
+
|
41
46
|
doc[:components].delete_if { |_, v| v.blank? }
|
42
47
|
doc[:tags] = doc[:tags].sort { |a, b| a[:name] <=> b[:name] }
|
43
48
|
doc[:paths] = doc[:paths].sort.to_h
|
44
49
|
|
45
|
-
OpenApi.docs[doc_name] =
|
50
|
+
OpenApi.docs[doc_name] = HashWithIndifferentAccess.new(doc.delete_if { |_, v| v.blank? })
|
46
51
|
end
|
47
52
|
|
48
53
|
def write_docs(generate_files: true)
|
@@ -52,30 +57,31 @@ module OpenApi
|
|
52
57
|
output_path = Config.file_output_path
|
53
58
|
FileUtils.mkdir_p output_path
|
54
59
|
max_length = docs.keys.map(&:size).sort.last
|
55
|
-
# puts '[ZRO] * * * * * *'
|
56
60
|
docs.each do |doc_name, doc|
|
57
61
|
puts "[ZRO] `#{doc_name.to_s.rjust(max_length)}.json` has been generated."
|
58
62
|
File.open("#{output_path}/#{doc_name}.json", 'w') { |file| file.write JSON.pretty_generate doc }
|
59
63
|
end
|
60
64
|
# :nocov:
|
61
65
|
end
|
62
|
-
end
|
66
|
+
end # end of module
|
63
67
|
|
64
|
-
def
|
65
|
-
routes
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
68
|
+
def routes
|
69
|
+
@routes ||=
|
70
|
+
if (file = Config.rails_routes_file)
|
71
|
+
File.read(file)
|
72
|
+
else
|
73
|
+
# :nocov:
|
74
|
+
# ref https://github.com/rails/rails/blob/master/railties/lib/rails/tasks/routes.rake
|
75
|
+
require './config/routes'
|
76
|
+
all_routes = Rails.application.routes.routes
|
77
|
+
require 'action_dispatch/routing/inspector'
|
78
|
+
inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
|
79
|
+
inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, nil)
|
80
|
+
# :nocov:
|
81
|
+
end
|
82
|
+
end
|
78
83
|
|
84
|
+
def routes_list
|
79
85
|
@routes_list ||= routes.split("\n").drop(1).map do |line|
|
80
86
|
next unless line.match?('#')
|
81
87
|
infos = line.match(/[A-Z|].*/).to_s.split(' ') # => [GET, /api/v1/examples/:id, api/v1/examples#index]
|
@@ -90,14 +96,14 @@ module OpenApi
|
|
90
96
|
end.compact.group_by { |api| api[:action_path].split('#').first } # => { "api/v1/examples" => [..] }, group by paths
|
91
97
|
end
|
92
98
|
|
93
|
-
def
|
94
|
-
routes_list[
|
99
|
+
def get_actions_by_route_base(route_base)
|
100
|
+
routes_list[route_base]&.map do |action_info|
|
95
101
|
action_info[:action_path].split('#').last
|
96
102
|
end
|
97
103
|
end
|
98
104
|
|
99
|
-
def
|
100
|
-
routes_list[
|
105
|
+
def find_path_httpverb_by(route_base, action)
|
106
|
+
routes_list[route_base]&.map do |action_info|
|
101
107
|
if action_info[:action_path].split('#').last == action.to_s
|
102
108
|
return [ action_info[:path], action_info[:http_verb].split('|').first ]
|
103
109
|
end
|
data/lib/open_api/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zero-rails_openapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- zhandao
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -137,7 +137,7 @@ files:
|
|
137
137
|
- lib/open_api/config.rb
|
138
138
|
- lib/open_api/config_dsl.rb
|
139
139
|
- lib/open_api/dsl.rb
|
140
|
-
- lib/open_api/dsl/
|
140
|
+
- lib/open_api/dsl/api_info.rb
|
141
141
|
- lib/open_api/dsl/common_dsl.rb
|
142
142
|
- lib/open_api/dsl/components.rb
|
143
143
|
- lib/open_api/dsl/helpers.rb
|