zero-rails_openapi 1.3.0 → 1.3.1
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/Gemfile +5 -1
- data/Gemfile.lock +17 -1
- data/README.md +56 -33
- data/documentation/examples/auto_gen_doc.rb +3 -4
- data/documentation/examples/examples_controller.rb +3 -1
- data/documentation/examples/goods_doc.rb +4 -4
- data/documentation/examples/open_api.rb +32 -28
- data/lib/oas_objs/response_obj.rb +10 -2
- data/lib/oas_objs/schema_obj.rb +2 -2
- data/lib/open_api/config.rb +35 -27
- data/lib/open_api/config_dsl.rb +13 -0
- data/lib/open_api/dsl.rb +18 -18
- data/lib/open_api/dsl/api_info_obj.rb +124 -0
- data/lib/open_api/dsl/common_dsl.rb +45 -0
- data/lib/open_api/dsl/ctrl_info_obj.rb +47 -0
- data/lib/open_api/{helpers.rb → dsl/helpers.rb} +27 -1
- data/lib/open_api/generator.rb +14 -11
- data/lib/open_api/version.rb +1 -1
- metadata +8 -5
- data/lib/open_api/dsl_inside_block.rb +0 -214
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b6fc0f663e643a98f744b0f75431c21a9484014
|
4
|
+
data.tar.gz: 2f0318d949040999a345d91b08f9d1ed63b525f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e9a713e9bb2296ffb3bd9e0c5433256cf90ccf72135c92495d4eebb53778e53a151b6882bf47baf137c2b000b40fc181974f5cfa0dce45048665be7f05f29b4
|
7
|
+
data.tar.gz: 1691cb0d3d6e3e6517f6439ca869a7eeddf7032f6d3c9eb3118e446ac62a8d1e89da05d90cbfda7b5f32a63c70e4601749b11686b1c430664af8fd7426d1b74b
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,12 +1,21 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
zero-rails_openapi (1.3.
|
4
|
+
zero-rails_openapi (1.3.1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
+
activesupport (5.1.4)
|
10
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
11
|
+
i18n (~> 0.7)
|
12
|
+
minitest (~> 5.1)
|
13
|
+
tzinfo (~> 1.1)
|
14
|
+
concurrent-ruby (1.0.5)
|
9
15
|
diff-lcs (1.3)
|
16
|
+
i18n (0.9.0)
|
17
|
+
concurrent-ruby (~> 1.0)
|
18
|
+
minitest (5.10.3)
|
10
19
|
rake (10.5.0)
|
11
20
|
rspec (3.6.0)
|
12
21
|
rspec-core (~> 3.6.0)
|
@@ -21,15 +30,22 @@ GEM
|
|
21
30
|
diff-lcs (>= 1.2.0, < 2.0)
|
22
31
|
rspec-support (~> 3.6.0)
|
23
32
|
rspec-support (3.6.0)
|
33
|
+
thread_safe (0.3.6)
|
34
|
+
tzinfo (1.2.4)
|
35
|
+
thread_safe (~> 0.1)
|
24
36
|
|
25
37
|
PLATFORMS
|
26
38
|
ruby
|
27
39
|
|
28
40
|
DEPENDENCIES
|
41
|
+
activesupport
|
29
42
|
bundler (~> 1.16.a)
|
30
43
|
rake (~> 10.0)
|
31
44
|
rspec (~> 3.0)
|
32
45
|
zero-rails_openapi!
|
33
46
|
|
47
|
+
RUBY VERSION
|
48
|
+
ruby 2.4.1p111
|
49
|
+
|
34
50
|
BUNDLED WITH
|
35
51
|
1.16.0.pre.2
|
data/README.md
CHANGED
@@ -29,7 +29,7 @@ but I dont have enough time now = ▽ =
|
|
29
29
|
- [Global DRYing](#trick2---global-drying)
|
30
30
|
- [Auto generate description](#trick3---auto-generate-description)
|
31
31
|
- [Skip or Use parameters define in api_dry](#trick4---skip-or-use-parameters-define-in-api_dry)
|
32
|
-
- [Atuo Generate index/show Actions's Responses Based on DB Schema](#trick5)
|
32
|
+
- [Atuo Generate index/show Actions's Responses Based on DB Schema](#trick5---auto-generate-indexshow-actionss-responses-based-on-db-schema)
|
33
33
|
- [Troubleshooting](#troubleshooting)
|
34
34
|
|
35
35
|
## About OAS
|
@@ -96,7 +96,7 @@ end
|
|
96
96
|
You can also set the *global configuration(/component)* of OAS:
|
97
97
|
Server Object / Security Scheme Object / Security Requirement Object ...
|
98
98
|
|
99
|
-
For more detailed configuration: [open_api.rb](https://github.com/zhandao/zero-rails_openapi/blob
|
99
|
+
For more detailed configuration: [open_api.rb](https://github.com/zhandao/zero-rails_openapi/blob/master/documentation/examples/open_api.rb)
|
100
100
|
|
101
101
|
## Usage
|
102
102
|
|
@@ -106,19 +106,19 @@ For more detailed configuration: [open_api.rb](https://github.com/zhandao/zero-r
|
|
106
106
|
|
107
107
|
```ruby
|
108
108
|
# application_controller.rb
|
109
|
-
require 'open_api/dsl'
|
110
|
-
|
111
109
|
class ApplicationController < ActionController::API
|
112
110
|
include OpenApi::DSL
|
113
111
|
end
|
114
112
|
```
|
115
113
|
|
116
|
-
#### \> [DSL Usage Example](https://github.com/zhandao/zero-rails_openapi/blob/
|
114
|
+
#### \> [DSL Usage Example](https://github.com/zhandao/zero-rails_openapi/blob/master/documentation/examples/examples_controller.rb)
|
117
115
|
|
118
116
|
(TODO: I consider that, here should be put in a the simplest case.)
|
119
117
|
```ruby
|
120
118
|
class Api::V1::ExamplesController < Api::V1::BaseController
|
121
|
-
|
119
|
+
apis_tag name: 'ExampleTagName', desc: 'ExamplesController\'s APIs'
|
120
|
+
|
121
|
+
components do
|
122
122
|
schema :Dog => [ String, must_be: 'doge' ]
|
123
123
|
query! :QueryCompUuid => [ :product_uuid, String, desc: 'product uuid' ]
|
124
124
|
path! :PathCompId => [ :id, Integer, desc: 'user id' ]
|
@@ -169,22 +169,33 @@ end
|
|
169
169
|
ctrl_path 'api/v1/examples'
|
170
170
|
```
|
171
171
|
This option 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
|
-
[Here's a trick](#write-the-dsl-somewhere-else
|
172
|
+
[Here's a trick](#trick1---write-the-dsl-somewhere-else): Using `ctrl_path`, you can write the DSL somewhere else
|
173
173
|
to simplify the current controller.
|
174
174
|
\* take the tag from `path.split('/').last`
|
175
175
|
|
176
|
-
- `
|
176
|
+
- `apis_tag` [Optional]
|
177
177
|
|
178
178
|
```ruby
|
179
179
|
# method signature
|
180
|
-
|
180
|
+
apis_tag name: nil, desc: '', external_doc_url: ''
|
181
181
|
# usage
|
182
|
-
|
182
|
+
apis_tag name: 'ExampleTagName', desc: 'ExamplesController\'s APIs'
|
183
|
+
```
|
184
|
+
desc and external_doc_url will be output to the tags[the current tag] (tag defaults to controller_name ), but are optional.
|
185
|
+
|
186
|
+
- `components` [Optional]
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
# method signature
|
190
|
+
components &block
|
191
|
+
# usage
|
192
|
+
components do
|
183
193
|
# DSL for defining components
|
194
|
+
schema :Dog => [ String, dft: { id: 1, name: 'pet' } ]
|
184
195
|
end
|
185
196
|
```
|
186
|
-
|
187
|
-
|
197
|
+
Component can be used to simplify your DSL code in `*_ref` methods.
|
198
|
+
Each RefObj you define is associated with components through component key.
|
188
199
|
|
189
200
|
- `api_dry` [Optional]
|
190
201
|
|
@@ -207,15 +218,15 @@ end
|
|
207
218
|
|
208
219
|
```ruby
|
209
220
|
# method signature
|
210
|
-
open_api method, summary = '',
|
221
|
+
open_api method, summary = '', builder: nil, skip: [ ], use: [ ], &block
|
211
222
|
# usage
|
212
|
-
open_api :index, '(SUMMARY) this api blah blah ...', builder: template1 do end
|
223
|
+
open_api :index, '(SUMMARY) this api blah blah ...', builder: :template1 do end
|
213
224
|
```
|
214
|
-
If pass `builder`
|
215
|
-
and `generate_jbuilder_file` in your
|
225
|
+
If pass `builder` to the third parameter,
|
226
|
+
and `generate_jbuilder_file` is set `true` in your initializer file,
|
216
227
|
ZRO will generate JBuilder file by using specified template that you set
|
217
228
|
`template1` in your setting file.
|
218
|
-
|
229
|
+
You can see: [open_api.rb](https://github.com/zhandao/zero-rails_openapi/blob/master/documentation/examples/open_api.rb)
|
219
230
|
|
220
231
|
|
221
232
|
#### \>\> DSL methods inside *open_api* and *api_dry*'s block ([source code](https://github.com/zhandao/zero-rails_openapi/blob/master/lib/open_api/dsl_inside_block.rb):: ApiInfoObj)
|
@@ -241,7 +252,7 @@ parameters, request body, responses, securities, servers.
|
|
241
252
|
|
242
253
|
```ruby
|
243
254
|
# method signature
|
244
|
-
desc desc,
|
255
|
+
desc desc, param_descs = { }
|
245
256
|
# usage
|
246
257
|
desc 'current API\'s description',
|
247
258
|
id: 'user id',
|
@@ -257,7 +268,8 @@ parameters, request body, responses, securities, servers.
|
|
257
268
|
- `param`
|
258
269
|
- `param_ref`
|
259
270
|
- `header`, `path`, `query`, `cookie` and bang methods: `header!`, `path!`, `query!`, `cookie!`
|
260
|
-
|
271
|
+
- `do_* by: { }`
|
272
|
+
**The bang method(`!`) means it is required, so without `!` means it is optional. the same below.**
|
261
273
|
|
262
274
|
Define the parameters for the API(operation).
|
263
275
|
You can use the Reference Object to link to parameters that are defined at the components/parameters by method param_ref().
|
@@ -266,24 +278,35 @@ parameters, request body, responses, securities, servers.
|
|
266
278
|
# method signature
|
267
279
|
param param_type, name, type, required, schema_hash = { }
|
268
280
|
# usage
|
269
|
-
param :query,
|
281
|
+
param :query, :page, Integer, :req, range: { gt: 0, le: 5 }, desc: 'page'
|
270
282
|
|
271
283
|
# method signature
|
272
284
|
param_ref component_key, *component_keys
|
273
285
|
# usage
|
274
286
|
param_ref :PathCompId
|
275
|
-
param_ref :PathCompId, :
|
287
|
+
param_ref :PathCompId, :QueryComp#, ...
|
276
288
|
|
277
289
|
# method signature
|
278
290
|
header name, type, schema_hash = { }
|
279
291
|
header! name, type, schema_hash = { }
|
280
292
|
query! name, type, schema_hash = { }
|
281
293
|
# usage
|
282
|
-
header!
|
283
|
-
query! :
|
294
|
+
header! 'Token', String
|
295
|
+
query! :read, Boolean, must_be: true, default: false
|
296
|
+
|
297
|
+
# method signature
|
298
|
+
do_query by:
|
299
|
+
# usage
|
300
|
+
do_query by: {
|
301
|
+
:search_type => { type: String },
|
302
|
+
:export! => { type: Boolean }
|
303
|
+
}
|
304
|
+
# Same as below, but a little more succinctly
|
305
|
+
query :search_type, String
|
306
|
+
query! :export, Boolean
|
284
307
|
```
|
285
308
|
|
286
|
-
[**>> More About Param DSL <<**](https://github.com/zhandao/zero-rails_openapi/blob
|
309
|
+
[**>> More About Param DSL <<**](https://github.com/zhandao/zero-rails_openapi/blob/master/documentation/parameter.md)
|
287
310
|
|
288
311
|
- request_body family methods (OAS - [Request Body Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#requestBodyObject))
|
289
312
|
- `request_body`
|
@@ -307,7 +330,7 @@ parameters, request body, responses, securities, servers.
|
|
307
330
|
body_ref :Body
|
308
331
|
|
309
332
|
# method signature
|
310
|
-
body
|
333
|
+
body! media_type, desc = '', schema_hash = { }
|
311
334
|
# usage
|
312
335
|
body :json
|
313
336
|
|
@@ -369,7 +392,7 @@ parameters, request body, responses, securities, servers.
|
|
369
392
|
|
370
393
|
- server: TODO
|
371
394
|
|
372
|
-
#### \>\> DSL methods inside
|
395
|
+
#### \>\> DSL methods inside components'block ([code source](https://github.com/zhandao/zero-rails_openapi/blob/master/lib/open_api/dsl_inside_block.rb):: CtrlInfoObj )
|
373
396
|
|
374
397
|
(Here corresponds to OAS [Components Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#componentsObject))
|
375
398
|
|
@@ -409,7 +432,7 @@ The DSL methods used to generate the components in this block are:
|
|
409
432
|
# or (unrecommended)
|
410
433
|
schema :Dog, { id!: Integer, name: String }, dft: { id: 1, name: 'pet' }
|
411
434
|
```
|
412
|
-
*: see: [Type](https://github.com/zhandao/zero-rails_openapi/blob
|
435
|
+
*: see: [Type](https://github.com/zhandao/zero-rails_openapi/blob/master/documentation/parameter.md#type)
|
413
436
|
|
414
437
|
### Generate JSON Documentation File
|
415
438
|
|
@@ -465,7 +488,7 @@ Notes: convention is the file name ends with `_doc.rb`
|
|
465
488
|
|
466
489
|
Method `api_dry` is for DRY but its scope is limited to the current controller.
|
467
490
|
|
468
|
-
I have no idea of best practices, But you can look at this [file](https://github.com/zhandao/zero-rails_openapi/blob/
|
491
|
+
I have no idea of best practices, But you can look at this [file](https://github.com/zhandao/zero-rails_openapi/blob/master/documentation/examples/auto_gen_doc.rb).
|
469
492
|
The implementation of the file is: do `api_dry` when inherits the base controller inside `inherited` method.
|
470
493
|
|
471
494
|
#### Trick3 - Auto Generate Description
|
@@ -485,7 +508,7 @@ Notice `!` use (`search_type!`, `desc!`), it tells ZRO to append
|
|
485
508
|
information that analyzed from definitions (enum, must_be ..) to description automatically.
|
486
509
|
|
487
510
|
Any one of above will generate:
|
488
|
-
|
511
|
+
> search field, allows:<br/>1/ name<br/>2/ creator,<br/>3/ category<br/>4/ price<br/>
|
489
512
|
|
490
513
|
ZRO also allows you use Hash to define `enum`:
|
491
514
|
```ruby
|
@@ -497,7 +520,7 @@ query :view, String, enum: {
|
|
497
520
|
'cheap goods': :borrow,
|
498
521
|
}
|
499
522
|
```
|
500
|
-
Read this [file](https://github.com/zhandao/zero-rails_openapi/blob/examples/auto_gen_desc.rb) to learn more.
|
523
|
+
Read this [file](https://github.com/zhandao/zero-rails_openapi/blob/master/documentation/examples/auto_gen_desc.rb) to learn more.
|
501
524
|
|
502
525
|
#### Trick4 - Skip or Use parameters define in api_dry
|
503
526
|
|
@@ -506,13 +529,13 @@ Pass `skip: []` and `use: []` to `open_api` like following code:
|
|
506
529
|
open_api :index, 'desc', builder: :index, skip: [ :Token ]
|
507
530
|
```
|
508
531
|
|
509
|
-
Look at this [file](https://github.com/zhandao/zero-rails_openapi/blob/examples/goods_doc.rb) to learn more.
|
532
|
+
Look at this [file](https://github.com/zhandao/zero-rails_openapi/blob/master/documentation/examples/goods_doc.rb) to learn more.
|
510
533
|
|
511
|
-
#### Trick5 - Auto Generate index/show Actions's
|
534
|
+
#### Trick5 - Auto Generate index/show Actions's Response-Types Based on DB Schema
|
512
535
|
|
513
536
|
Use method `load_schema` in `api_dry`.
|
514
537
|
|
515
|
-
See this [file](https://github.com/zhandao/zero-rails_openapi/blob/examples/auto_gen_doc.rb#L51) for uasge information.
|
538
|
+
See this [file](https://github.com/zhandao/zero-rails_openapi/blob/master/documentation/examples/auto_gen_doc.rb#L51) for uasge information.
|
516
539
|
|
517
540
|
|
518
541
|
## Troubleshooting
|
@@ -22,7 +22,7 @@ module AutoGenDoc
|
|
22
22
|
ctrl_path = try(:controller_path) || instance_variable_get('@_ctrl_path')
|
23
23
|
::OpenApi::Generator.get_actions_by_ctrl_path(ctrl_path)&.each do |action|
|
24
24
|
api_dry action do
|
25
|
-
header!
|
25
|
+
header! 'Token', String, desc: 'user token'
|
26
26
|
|
27
27
|
# Common :index parameters
|
28
28
|
if action == 'index'
|
@@ -73,9 +73,8 @@ module AutoGenDoc
|
|
73
73
|
# # api/v1/examples#index => ExamplesError
|
74
74
|
# error_class_name = action_path.split('#').first.split('/').last.camelize.concat('Error')
|
75
75
|
# error_class = Object.const_get(error_class_name) rescue next
|
76
|
-
#
|
77
|
-
#
|
78
|
-
# cur_errs.each do |error|
|
76
|
+
# cur_api_errs = error_class.errors.values_at(action.to_sym, :private, :_public).flatten.compact.uniq
|
77
|
+
# cur_api_errs.each do |error|
|
79
78
|
# info = error_class.send(error, :info)
|
80
79
|
# response info[:code], info[:msg]
|
81
80
|
# end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
class Api::V1::ExamplesController < Api::V1::BaseController
|
2
|
-
|
2
|
+
apis_tag name: 'ExampleTagName', desc: 'ExamplesController\'s APIs'
|
3
|
+
|
4
|
+
components do
|
3
5
|
schema :Dog => [{
|
4
6
|
id!: Integer,
|
5
7
|
name: { type: String, must_be: 'zhandao', desc: 'name' }
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class V2::GoodsDoc < BaseDoc
|
2
2
|
|
3
3
|
open_api :index, 'Get list of Goods.', builder: :index,
|
4
|
-
use: [
|
4
|
+
use: [ 'Token' ] do # use parameters write in AutoGenDoc#api_dry
|
5
5
|
# skip: %i[ Token ] do # you can also skip parameters
|
6
6
|
desc 'listing Goods',
|
7
7
|
view!: 'search view, allows::<br/>',
|
@@ -18,7 +18,7 @@ class V2::GoodsDoc < BaseDoc
|
|
18
18
|
end
|
19
19
|
|
20
20
|
|
21
|
-
open_api :create, 'Create a Good', builder: :success_or_not, use:
|
21
|
+
open_api :create, 'Create a Good', builder: :success_or_not, use: 'Token' do
|
22
22
|
form! 'for creating a good', data: {
|
23
23
|
:name! => { type: String, desc: 'good\'s name' },
|
24
24
|
:category_id! => { type: Integer, desc: 'sub_category\'s id', npmt: true, range: { ge: 1 }, as: :cate },
|
@@ -31,8 +31,8 @@ class V2::GoodsDoc < BaseDoc
|
|
31
31
|
end
|
32
32
|
|
33
33
|
|
34
|
-
open_api :show, 'Show a Good.', builder: :show, use: [
|
34
|
+
open_api :show, 'Show a Good.', builder: :show, use: [ 'Token', :id ]
|
35
35
|
|
36
36
|
|
37
|
-
open_api :destroy, 'Delete a Good.', builder: :success_or_not, use: [
|
37
|
+
open_api :destroy, 'Delete a Good.', builder: :success_or_not, use: [ 'Token', :id ]
|
38
38
|
end
|
@@ -87,45 +87,49 @@ OpenApi::Config.tap do |c|
|
|
87
87
|
c.overwrite_jbuilder_file = false
|
88
88
|
c.jbuilder_templates = {
|
89
89
|
index: (
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
end
|
90
|
+
<<~FILE
|
91
|
+
# *** Generated by ZRO ***
|
92
|
+
json.partial! 'api/base', total: @data.size
|
93
|
+
|
94
|
+
json.data do
|
95
|
+
# @data = @data.page(@_page).per(@_rows) if @_page || @_rows
|
96
|
+
# json.array! @data do |datum|
|
97
|
+
json.array! @data.page(@_page).per(@_rows) do |datum|
|
98
|
+
json.(datum, *datum.show_attrs) if datum.present?
|
99
|
+
end
|
100
|
+
end
|
100
101
|
FILE
|
101
102
|
),
|
102
103
|
|
103
104
|
show: (
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
end
|
105
|
+
<<~FILE
|
106
|
+
# *** Generated by ZRO ***
|
107
|
+
json.partial! 'api/base', total: 1
|
108
|
+
|
109
|
+
json.data do
|
110
|
+
json.array! [ @data ] do |datum|
|
111
|
+
json.(datum, *datum.show_attrs) if datum.present?
|
112
|
+
end
|
113
|
+
end
|
112
114
|
FILE
|
113
115
|
),
|
114
116
|
|
115
117
|
success: (
|
116
|
-
|
117
|
-
|
118
|
+
<<~FILE
|
119
|
+
# *** Generated by ZRO ***
|
120
|
+
json.partial! 'api/success'
|
118
121
|
FILE
|
119
122
|
),
|
120
123
|
|
121
124
|
success_or_not: (
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
json.
|
125
|
+
<<~FILE
|
126
|
+
# *** Generated by ZRO ***
|
127
|
+
unless @status
|
128
|
+
# @_code, @_msg = @error_info.present? ? @error_info : ApiError.action_failed.info
|
129
|
+
end
|
130
|
+
|
131
|
+
json.partial! 'api/base', total: 0
|
132
|
+
json.data ''
|
129
133
|
FILE
|
130
134
|
),
|
131
135
|
}
|
@@ -134,4 +138,4 @@ end
|
|
134
138
|
|
135
139
|
Object.const_set('Boolean', 'boolean') # Support `Boolean` writing in DSL
|
136
140
|
|
137
|
-
OpenApi.write_docs
|
141
|
+
OpenApi.write_docs generate_files: !Rails.env.production?
|
@@ -8,7 +8,9 @@ module OpenApi
|
|
8
8
|
include Helpers
|
9
9
|
|
10
10
|
attr_accessor :processed, :code, :media_type
|
11
|
-
def initialize(
|
11
|
+
def initialize(desc, media_type, schema_hash)
|
12
|
+
@schema_hash = schema_hash
|
13
|
+
@mt = media_type
|
12
14
|
self.code = code.to_s
|
13
15
|
self.media_type = MediaTypeObj.new(media_type, schema_hash)
|
14
16
|
self.processed = { description: desc }
|
@@ -16,7 +18,13 @@ module OpenApi
|
|
16
18
|
|
17
19
|
def process
|
18
20
|
assign(media_type.process).to_processed 'content'
|
19
|
-
|
21
|
+
processed
|
22
|
+
end
|
23
|
+
|
24
|
+
def override type_hash
|
25
|
+
@schema_hash[:type].merge!(type_hash)
|
26
|
+
self.media_type = MediaTypeObj.new(@mt, @schema_hash)
|
27
|
+
self
|
20
28
|
end
|
21
29
|
end
|
22
30
|
end
|
data/lib/oas_objs/schema_obj.rb
CHANGED
@@ -40,7 +40,7 @@ module OpenApi
|
|
40
40
|
|
41
41
|
reduceee(processed_desc options).then_merge!
|
42
42
|
end
|
43
|
-
|
43
|
+
alias process process_for
|
44
44
|
|
45
45
|
def preprocess_with_desc desc, param_name = nil
|
46
46
|
self.__desc = desc
|
@@ -109,7 +109,7 @@ module OpenApi
|
|
109
109
|
}
|
110
110
|
t.each do |prop_name, prop_type|
|
111
111
|
@prop_name = prop_name
|
112
|
-
_schema[:required] << "#{prop_name}".delete('!') if
|
112
|
+
_schema[:required] << "#{prop_name}".delete('!') if prop_name['!']
|
113
113
|
_schema[:properties]["#{prop_name}".delete('!').to_sym] = recursive_obj_type prop_type
|
114
114
|
end
|
115
115
|
_schema.keep_if &value_present
|
data/lib/open_api/config.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
require 'open_api/config_dsl'
|
2
|
+
|
1
3
|
module OpenApi
|
2
4
|
module Config
|
5
|
+
include ConfigDSL
|
6
|
+
|
3
7
|
# [REQUIRED] The location where .json doc file will be output.
|
4
8
|
cattr_accessor :file_output_path do
|
5
9
|
'public/open_api'
|
@@ -49,45 +53,49 @@ module OpenApi
|
|
49
53
|
cattr_accessor :jbuilder_templates do
|
50
54
|
{
|
51
55
|
index: (
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
56
|
+
<<~FILE
|
57
|
+
# *** Generated by ZRO ***
|
58
|
+
json.partial! 'api/base', total: @data.size
|
59
|
+
|
60
|
+
json.data do
|
61
|
+
# @data = @data.page(@_page).per(@_rows) if @_page || @_rows
|
62
|
+
# json.array! @data do |datum|
|
63
|
+
json.array! @data.page(@_page).per(@_rows) do |datum|
|
64
|
+
json.(datum, *datum.show_attrs) if datum.present?
|
65
|
+
end
|
66
|
+
end
|
62
67
|
FILE
|
63
68
|
),
|
64
69
|
|
65
70
|
show: (
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
71
|
+
<<~FILE
|
72
|
+
# *** Generated by ZRO ***
|
73
|
+
json.partial! 'api/base', total: 1
|
74
|
+
|
75
|
+
json.data do
|
76
|
+
json.array! [ @data ] do |datum|
|
77
|
+
json.(datum, *datum.show_attrs) if datum.present?
|
78
|
+
end
|
79
|
+
end
|
74
80
|
FILE
|
75
81
|
),
|
76
82
|
|
77
83
|
success: (
|
78
|
-
|
79
|
-
|
84
|
+
<<~FILE
|
85
|
+
# *** Generated by ZRO ***
|
86
|
+
json.partial! 'api/success'
|
80
87
|
FILE
|
81
88
|
),
|
82
89
|
|
83
90
|
success_or_not: (
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
json.
|
91
|
+
<<~FILE
|
92
|
+
# *** Generated by ZRO ***
|
93
|
+
unless @status
|
94
|
+
# @_code, @_msg = @error_info.present? ? @error_info : ApiError.action_failed.info
|
95
|
+
end
|
96
|
+
|
97
|
+
json.partial! 'api/base', total: 0
|
98
|
+
json.data ''
|
91
99
|
FILE
|
92
100
|
),
|
93
101
|
}
|
data/lib/open_api/dsl.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
require 'open_api/
|
1
|
+
require 'open_api/dsl/api_info_obj'
|
2
|
+
require 'open_api/dsl/ctrl_info_obj'
|
2
3
|
|
3
4
|
module OpenApi
|
4
5
|
module DSL
|
@@ -13,46 +14,45 @@ module OpenApi
|
|
13
14
|
@_apis_tag = path.split('/').last.camelize
|
14
15
|
end
|
15
16
|
|
16
|
-
def
|
17
|
-
@_ctrl_infos = { }
|
17
|
+
def apis_tag name: nil, desc: '', external_doc_url: ''
|
18
18
|
# current `tag`, this means that tags is currently divided by controllers.
|
19
|
-
|
19
|
+
@_apis_tag = name if name.present?
|
20
|
+
@_apis_tag ||= controller_name.camelize
|
21
|
+
tag = (@_ctrl_infos = { })[:tag] = { name: @_apis_tag }
|
20
22
|
tag[:description] = desc if desc.present?
|
21
23
|
tag[:externalDocs] = { description: 'ref', url: external_doc_url } if external_doc_url.present?
|
24
|
+
end
|
22
25
|
|
26
|
+
def components &block
|
27
|
+
apis_tag if @_ctrl_infos.nil?
|
23
28
|
current_ctrl = @_ctrl_infos[:components] = CtrlInfoObj.new
|
24
|
-
current_ctrl.instance_eval &block
|
29
|
+
current_ctrl.instance_eval &block
|
30
|
+
current_ctrl._process_objs
|
25
31
|
end
|
26
32
|
|
27
|
-
def open_api method, summary = '',
|
28
|
-
|
33
|
+
def open_api method, summary = '', builder: nil, skip: [ ], use: [ ], &block
|
34
|
+
apis_tag if @_ctrl_infos.nil?
|
29
35
|
|
30
36
|
# select the routing info (corresponding to the current method) from the routing list.
|
31
37
|
action_path = "#{@_ctrl_path ||= controller_path}##{method}"
|
32
38
|
routes_info = ctrl_routes_list&.select { |api| api[:action_path].match? /^#{action_path}$/ }&.first
|
33
39
|
pp "[ZRO Warnning] Routing mapping failed: #{@_ctrl_path}##{method}" and return if routes_info.nil?
|
40
|
+
Generator.generate_builder_file(action_path, builder) if builder.present?
|
34
41
|
|
35
42
|
# structural { #path: { #http_method:{ } } }, for pushing into Paths Object.
|
36
43
|
path = (@_api_infos ||= { })[routes_info[:path]] ||= { }
|
37
44
|
current_api = path[routes_info[:http_verb]] =
|
38
|
-
ApiInfoObj.new(action_path,
|
45
|
+
ApiInfoObj.new(action_path, skip: skip, use: use)
|
39
46
|
.merge! description: '', summary: summary, operationId: method, tags: [@_apis_tag],
|
40
|
-
parameters: [ ], requestBody: '',
|
41
|
-
|
42
|
-
if (builder = options.values_at(:builder, :bd, :jbuilder).compact.first).present?
|
43
|
-
Generator
|
44
|
-
.generate_builder_file path: action_path.split('#').first,
|
45
|
-
action: action_path.split('#').last,
|
46
|
-
builder: builder
|
47
|
-
end
|
47
|
+
parameters: [ ], requestBody: '', responses: { }, security: [ ], servers: [ ]
|
48
48
|
|
49
49
|
current_api.tap do |api|
|
50
50
|
[method, :all].each do |key| # blocks_store_key
|
51
51
|
@_apis_blocks&.[](key)&.each { |blk| api.instance_eval &blk }
|
52
52
|
end
|
53
|
-
api.param_use =
|
53
|
+
api.param_use = [ ] # skip 和 use 是对 dry 块而言的
|
54
54
|
api.instance_eval &block if block_given?
|
55
|
-
api.
|
55
|
+
api._process_objs
|
56
56
|
api.delete_if { |_, v| v.blank? }
|
57
57
|
end
|
58
58
|
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'open_api/dsl/common_dsl'
|
2
|
+
|
3
|
+
module OpenApi
|
4
|
+
module DSL
|
5
|
+
class ApiInfoObj < Hash
|
6
|
+
include DSL::CommonDSL
|
7
|
+
include DSL::Helpers
|
8
|
+
|
9
|
+
attr_accessor :action_path, :param_skip, :param_use, :param_descs
|
10
|
+
|
11
|
+
def initialize(action_path, skip: [ ], use: [ ])
|
12
|
+
self.action_path = action_path
|
13
|
+
self.param_skip = skip
|
14
|
+
self.param_use = use
|
15
|
+
self.param_descs = { }
|
16
|
+
end
|
17
|
+
|
18
|
+
def this_api_is_invalid! explain = ''
|
19
|
+
self[:deprecated] = true
|
20
|
+
end
|
21
|
+
|
22
|
+
alias this_api_is_expired! this_api_is_invalid!
|
23
|
+
alias this_api_is_unused! this_api_is_invalid!
|
24
|
+
alias this_api_is_under_repair this_api_is_invalid!
|
25
|
+
|
26
|
+
def desc desc, param_descs = { }
|
27
|
+
self.param_descs = param_descs
|
28
|
+
self[:description] = desc
|
29
|
+
end
|
30
|
+
|
31
|
+
def param param_type, name, type, required, schema_hash = { }
|
32
|
+
return if param_skip.include?(name)
|
33
|
+
return if param_use.present? && !param_use.include?(name)
|
34
|
+
|
35
|
+
_t = nil
|
36
|
+
schema_hash[:desc] = _t if (_t = param_descs[name]).present?
|
37
|
+
schema_hash[:desc!] = _t if (_t = param_descs["#{name}!".to_sym]).present?
|
38
|
+
|
39
|
+
param_obj = ParamObj.new(name, param_type, type, required, schema_hash)
|
40
|
+
# The definition of the same name parameter will be overwritten
|
41
|
+
index = self[:parameters].map { |p| p.processed[:name] if p.is_a? ParamObj }.index name
|
42
|
+
index.present? ? self[:parameters][index] = param_obj : self[:parameters] << param_obj
|
43
|
+
end
|
44
|
+
|
45
|
+
# Support this writing: (just like `form '', data: { }`)
|
46
|
+
# do_query by: {
|
47
|
+
# :search_type => { type: String },
|
48
|
+
# :export! => { type: Boolean }
|
49
|
+
# }
|
50
|
+
%i[header header! path path! query query! cookie cookie!].each do |param_type|
|
51
|
+
define_method "do_#{param_type}" do |by:|
|
52
|
+
by.each do |key, value|
|
53
|
+
args = [ key.dup.to_s.delete('!'), value.delete(:type), value ]
|
54
|
+
key.to_s['!'] ? send("#{param_type}!", *args) : send(param_type, *args)
|
55
|
+
end
|
56
|
+
end unless param_type.to_s['!']
|
57
|
+
end
|
58
|
+
|
59
|
+
def _param_agent name, type, schema_hash = { }
|
60
|
+
param "#{@param_type}".delete('!'), name, type, (@param_type['!'] ? :req : :opt), schema_hash
|
61
|
+
end
|
62
|
+
|
63
|
+
def param_ref component_key, *keys
|
64
|
+
self[:parameters].concat([component_key].concat(keys).map { |key| RefObj.new(:parameter, key).process })
|
65
|
+
end
|
66
|
+
|
67
|
+
def request_body required, media_type, desc = '', schema_hash = { }
|
68
|
+
self[:requestBody] = RequestBodyObj.new(required, media_type, desc, schema_hash).process
|
69
|
+
end
|
70
|
+
|
71
|
+
def _request_body_agent media_type, desc = '', schema_hash = { }
|
72
|
+
request_body (@method_name['!'] ? :req : :opt), media_type, desc, schema_hash
|
73
|
+
end
|
74
|
+
|
75
|
+
def body_ref component_key
|
76
|
+
self[:requestBody] = RefObj.new(:requestBody, component_key).process
|
77
|
+
end
|
78
|
+
|
79
|
+
def override_response code, type_hash
|
80
|
+
_response = self[:responses].fetch(code)
|
81
|
+
self[:responses][code] = _response.override(type_hash).process
|
82
|
+
end
|
83
|
+
|
84
|
+
def response_ref code_compkey_hash
|
85
|
+
code_compkey_hash.each do |code, component_key|
|
86
|
+
self[:responses][code] = RefObj.new(:response, component_key).process
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# TODO: 目前只能写一句 request body,包括 form 和 file, 需要同时支持一下扁平化
|
91
|
+
def form desc = '', schema_hash = { }
|
92
|
+
body :form, desc, schema_hash
|
93
|
+
end
|
94
|
+
def form! desc = '', schema_hash = { }
|
95
|
+
body! :form, desc, schema_hash
|
96
|
+
end
|
97
|
+
def file media_type, desc = '', schema_hash = { type: File }
|
98
|
+
body media_type, desc, schema_hash
|
99
|
+
end
|
100
|
+
def file! media_type, desc = '', schema_hash = { type: File }
|
101
|
+
body! media_type, desc, schema_hash
|
102
|
+
end
|
103
|
+
|
104
|
+
def security scheme_name, requirements = [ ]
|
105
|
+
self[:security] << { scheme_name => requirements }
|
106
|
+
end
|
107
|
+
|
108
|
+
def server url, desc
|
109
|
+
self[:servers] << { url: url, description: desc }
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
def _process_objs
|
114
|
+
self[:parameters]&.each_with_index do |p, index|
|
115
|
+
self[:parameters][index] = p.process if p.is_a?(ParamObj)
|
116
|
+
end
|
117
|
+
|
118
|
+
self[:responses]&.each do |code, obj|
|
119
|
+
self[:responses][code] = obj.process if obj.is_a?(ResponseObj)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end # ----------------------------------------- end of ApiInfoObj
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'oas_objs/schema_obj'
|
2
|
+
require 'oas_objs/param_obj'
|
3
|
+
require 'oas_objs/response_obj'
|
4
|
+
require 'oas_objs/request_body_obj'
|
5
|
+
require 'oas_objs/ref_obj'
|
6
|
+
require 'open_api/dsl/helpers'
|
7
|
+
|
8
|
+
module OpenApi
|
9
|
+
module DSL
|
10
|
+
module CommonDSL
|
11
|
+
%i[ header header! path path! query query! cookie cookie! ].each do |param_type|
|
12
|
+
define_method param_type do |*args|
|
13
|
+
@param_type = param_type
|
14
|
+
_param_agent *args
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
%i[ body body! ].each do |method|
|
19
|
+
define_method method do |*args|
|
20
|
+
@method_name = method
|
21
|
+
_request_body_agent *args
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# `code`: when defining components, `code` means `component_key`
|
26
|
+
def response code, desc, media_type = nil, schema_hash = { }
|
27
|
+
(self[:responses] ||= { })[code] = ResponseObj.new(desc, media_type, schema_hash)
|
28
|
+
end
|
29
|
+
|
30
|
+
def default_response desc, media_type = nil, schema_hash = { }
|
31
|
+
response :default, desc, media_type, schema_hash
|
32
|
+
end
|
33
|
+
|
34
|
+
{ # alias_methods mapping
|
35
|
+
response: %i[ error_response resp ],
|
36
|
+
default_response: %i[ dft_resp dft_response ],
|
37
|
+
error_response: %i[ other_response oth_resp error err_resp ],
|
38
|
+
}.each do |original_name, aliases|
|
39
|
+
aliases.each do |alias_name|
|
40
|
+
alias_method alias_name, original_name
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'open_api/dsl/common_dsl'
|
2
|
+
|
3
|
+
module OpenApi
|
4
|
+
module DSL
|
5
|
+
class CtrlInfoObj < Hash
|
6
|
+
include DSL::CommonDSL
|
7
|
+
include DSL::Helpers
|
8
|
+
|
9
|
+
def schema component_key, type, schema_hash = { }
|
10
|
+
(self[:schemas] ||= { })[component_key] = SchemaObj.new(type, schema_hash).process
|
11
|
+
end
|
12
|
+
arrow_enable :schema
|
13
|
+
|
14
|
+
def param component_key, param_type, name, type, required, schema_hash = { }
|
15
|
+
(self[:parameters] ||= { })[component_key] =
|
16
|
+
ParamObj.new(name, param_type, type, required, schema_hash).process
|
17
|
+
end
|
18
|
+
|
19
|
+
def _param_agent component_key, name, type, schema_hash = { }
|
20
|
+
param component_key,
|
21
|
+
"#{@param_type}".delete('!'), name, type, (@param_type['!'] ? :req : :opt), schema_hash
|
22
|
+
end
|
23
|
+
arrow_enable :_param_agent
|
24
|
+
|
25
|
+
def request_body component_key, required, media_type, desc = '', schema_hash = { }
|
26
|
+
(self[:requestBodies] ||= { })[component_key] =
|
27
|
+
RequestBodyObj.new(required, media_type, desc, schema_hash).process
|
28
|
+
end
|
29
|
+
|
30
|
+
def _request_body_agent component_key, media_type, desc = '', schema_hash = { }
|
31
|
+
request_body component_key,
|
32
|
+
(@method_name['!'] ? :req : :opt), media_type, desc, schema_hash
|
33
|
+
end
|
34
|
+
arrow_enable :_request_body_agent
|
35
|
+
|
36
|
+
arrow_enable :resp # alias_method 竟然也会指向旧的方法?
|
37
|
+
arrow_enable :response
|
38
|
+
|
39
|
+
|
40
|
+
def _process_objs
|
41
|
+
self[:responses]&.each do |code, obj|
|
42
|
+
self[:responses][code] = obj.process if obj.is_a?(ResponseObj)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -1,6 +1,10 @@
|
|
1
1
|
module OpenApi
|
2
2
|
module DSL
|
3
3
|
module Helpers
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
4
8
|
def load_schema(model)
|
5
9
|
# About `show_attrs`, see:
|
6
10
|
# (1) BuilderSupport module: https://github.com/zhandao/zero-rails/blob/master/app/models/concerns/builder_support.rb
|
@@ -19,7 +23,7 @@ module OpenApi
|
|
19
23
|
# TODO: 如何获知关系是 many?因为不能只判断结尾是否 ‘s’
|
20
24
|
assoc_model = Object.const_get(attr.to_s.split('_').first.singularize.camelize)
|
21
25
|
{ attr => load_schema(assoc_model) }
|
22
|
-
end
|
26
|
+
end rescue next
|
23
27
|
end
|
24
28
|
else
|
25
29
|
model&.columns&.map do |column|
|
@@ -30,6 +34,28 @@ module OpenApi
|
|
30
34
|
end
|
31
35
|
end&.compact&.reduce({ }, :merge)
|
32
36
|
end
|
37
|
+
|
38
|
+
# Arrow Writing:
|
39
|
+
# response :RespComponent => [ '200', 'success', :json ]
|
40
|
+
# It is equivalent to:
|
41
|
+
# response :RespComponent, '200', 'success', :json
|
42
|
+
# But I think, in the definition of a component,
|
43
|
+
# the key-value (arrow) writing is easy to understand.
|
44
|
+
def arrow_writing_support
|
45
|
+
proc do |args, executor|
|
46
|
+
_args = args.size == 1 && args.first.is_a?(Hash) ? args[0].to_a.flatten : args
|
47
|
+
send executor, *_args
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module ClassMethods
|
52
|
+
def arrow_enable method
|
53
|
+
alias_method "_#{method}".to_sym, method
|
54
|
+
define_method method do |*args|
|
55
|
+
arrow_writing_support.call(args, "_#{method}")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
33
59
|
end
|
34
60
|
end
|
35
61
|
end
|
data/lib/open_api/generator.rb
CHANGED
@@ -40,7 +40,7 @@ module OpenApi
|
|
40
40
|
ActiveSupport::HashWithIndifferentAccess.new(doc.delete_if { |_, v| v.blank? })
|
41
41
|
end
|
42
42
|
|
43
|
-
def write_docs(generate_files
|
43
|
+
def write_docs(generate_files: true)
|
44
44
|
docs = generate_docs
|
45
45
|
return unless generate_files
|
46
46
|
output_path = Config.file_output_path
|
@@ -48,23 +48,26 @@ module OpenApi
|
|
48
48
|
max_length = docs.keys.map(&:size).sort.last
|
49
49
|
puts '[ZRO] * * * * * *'
|
50
50
|
docs.each do |doc_name, doc|
|
51
|
-
puts "[ZRO] `%#{max_length}s.json`
|
51
|
+
puts "[ZRO] `%#{max_length}s.json` has been generated." % "#{doc_name}"
|
52
52
|
File.open("#{output_path}/#{doc_name}.json", 'w') { |file| file.write JSON.pretty_generate doc }
|
53
53
|
end
|
54
54
|
# pp $open_apis
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
def self.generate_builder_file(
|
58
|
+
def self.generate_builder_file(action_path, builder)
|
59
59
|
return unless Config.generate_jbuilder_file
|
60
|
-
return
|
61
|
-
|
60
|
+
return if builder.nil?
|
61
|
+
|
62
|
+
path, action = action_path.split('#')
|
63
|
+
dir_path = "app/views/#{path}"
|
62
64
|
FileUtils.mkdir_p dir_path
|
63
|
-
file_path = "#{dir_path}/#{
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
65
|
+
file_path = "#{dir_path}/#{action}.json.jbuilder"
|
66
|
+
|
67
|
+
unless !Config.overwrite_jbuilder_file && File::exists?(file_path)
|
68
|
+
File.open(file_path, 'w') { |file| file.write Config.jbuilder_templates[builder] }
|
69
|
+
puts "[ZRO] JBuilder file has been generated: #{path}/#{action}"
|
70
|
+
end
|
68
71
|
end
|
69
72
|
|
70
73
|
def self.generate_routes_list
|
@@ -79,7 +82,7 @@ module OpenApi
|
|
79
82
|
{
|
80
83
|
http_verb: infos[0].downcase, # => "get"
|
81
84
|
path: infos[1][0..-11].split('/').map do |item|
|
82
|
-
item
|
85
|
+
item[':'] ? "{#{item[1..-1]}}" : item
|
83
86
|
end.join('/'), # => "/api/v1/examples/{id}"
|
84
87
|
action_path: infos[2] # => "api/v1/examples#index"
|
85
88
|
} rescue next
|
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.3.
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- zhandao
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-11-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -90,10 +90,13 @@ files:
|
|
90
90
|
- lib/oas_objs/schema_obj.rb
|
91
91
|
- lib/open_api.rb
|
92
92
|
- lib/open_api/config.rb
|
93
|
+
- lib/open_api/config_dsl.rb
|
93
94
|
- lib/open_api/dsl.rb
|
94
|
-
- lib/open_api/
|
95
|
+
- lib/open_api/dsl/api_info_obj.rb
|
96
|
+
- lib/open_api/dsl/common_dsl.rb
|
97
|
+
- lib/open_api/dsl/ctrl_info_obj.rb
|
98
|
+
- lib/open_api/dsl/helpers.rb
|
95
99
|
- lib/open_api/generator.rb
|
96
|
-
- lib/open_api/helpers.rb
|
97
100
|
- lib/open_api/version.rb
|
98
101
|
- zero-rails_openapi.gemspec
|
99
102
|
homepage: https://github.com/zhandao/zero-rails_openapi
|
@@ -116,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
119
|
version: '0'
|
117
120
|
requirements: []
|
118
121
|
rubyforge_project:
|
119
|
-
rubygems_version: 2.6.
|
122
|
+
rubygems_version: 2.6.13
|
120
123
|
signing_key:
|
121
124
|
specification_version: 4
|
122
125
|
summary: Generate the OpenAPI Specification 3 documentation for Rails application.
|
@@ -1,214 +0,0 @@
|
|
1
|
-
require 'oas_objs/schema_obj'
|
2
|
-
require 'oas_objs/param_obj'
|
3
|
-
require 'oas_objs/response_obj'
|
4
|
-
require 'oas_objs/request_body_obj'
|
5
|
-
require 'oas_objs/ref_obj'
|
6
|
-
require 'open_api/helpers'
|
7
|
-
|
8
|
-
module OpenApi
|
9
|
-
module DSL
|
10
|
-
module CommonDSL
|
11
|
-
def arrow_writing_support
|
12
|
-
proc do |args, executor|
|
13
|
-
if args.count == 1 && args.first.is_a?(Hash)
|
14
|
-
send(executor, args[0].keys.first, *args[0].values.first)
|
15
|
-
else
|
16
|
-
send(executor, *args)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
%i[header header! path path! query query! cookie cookie!].each do |param_type|
|
22
|
-
define_method param_type do |*args|
|
23
|
-
@param_type = param_type
|
24
|
-
_param_agent *args
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
%i[body body!].each do |method|
|
29
|
-
define_method method do |*args|
|
30
|
-
@method_name = method
|
31
|
-
_request_body_agent *args
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# code represents `component_key` when declare response component
|
36
|
-
def _response code, desc, media_type = nil, schema_hash = { }
|
37
|
-
(self[:responses] ||= { }).merge! ResponseObj.new(code, desc, media_type, schema_hash).process
|
38
|
-
end
|
39
|
-
|
40
|
-
def response *args
|
41
|
-
arrow_writing_support.call(args, :_response)
|
42
|
-
end
|
43
|
-
|
44
|
-
def default_response desc, media_type = nil, schema_hash = { }
|
45
|
-
response :default, desc, media_type, schema_hash
|
46
|
-
end
|
47
|
-
|
48
|
-
{ # alias_methods mapping
|
49
|
-
response: %i[ error_response resp ],
|
50
|
-
default_response: %i[ dft_resp dft_response ],
|
51
|
-
error_response: %i[ other_response oth_resp error err_resp ],
|
52
|
-
}.each do |original_name, aliases|
|
53
|
-
aliases.each do |alias_name|
|
54
|
-
alias_method alias_name, original_name
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end # ----------------------------------------- end of CommonDSL
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
class CtrlInfoObj < Hash
|
63
|
-
include DSL::CommonDSL
|
64
|
-
|
65
|
-
def _schema component_key, type, schema_hash = { }
|
66
|
-
(self[:schemas] ||= { }).merge! component_key => SchemaObj.new(type, schema_hash).process
|
67
|
-
end
|
68
|
-
def schema *args
|
69
|
-
arrow_writing_support.call(args, :_schema)
|
70
|
-
end
|
71
|
-
|
72
|
-
def param component_key, param_type, name, type, required, schema_hash = { }
|
73
|
-
(self[:parameters] ||= { })
|
74
|
-
.merge! component_key => ParamObj.new(name, param_type, type, required, schema_hash).process
|
75
|
-
end
|
76
|
-
|
77
|
-
def _param_agent *args
|
78
|
-
arrow_writing_support.call(args, :_param_arg_agent)
|
79
|
-
end
|
80
|
-
|
81
|
-
def _param_arg_agent component_key, name, type, schema_hash = { }
|
82
|
-
param component_key, "#{@param_type}".delete('!'), name, type,
|
83
|
-
("#{@param_type}".match?(/!/) ? :req : :opt), schema_hash
|
84
|
-
end
|
85
|
-
|
86
|
-
def request_body component_key, required, media_type, desc = '', schema_hash = { }
|
87
|
-
self[:requestBodies] = { component_key => RequestBodyObj.new(required, media_type, desc, schema_hash).process }
|
88
|
-
end
|
89
|
-
|
90
|
-
def _request_body_agent *args
|
91
|
-
arrow_writing_support.call(args, :_request_body_arg_agent)
|
92
|
-
end
|
93
|
-
|
94
|
-
def _request_body_arg_agent component_key, media_type, desc = '', schema_hash = { }
|
95
|
-
request_body component_key, ("#{@method_name}".match?(/!/) ? :req : :opt), media_type, desc, schema_hash
|
96
|
-
end
|
97
|
-
end # ----------------------------------------- end of CtrlInfoObj
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
class ApiInfoObj < Hash
|
103
|
-
include DSL::CommonDSL
|
104
|
-
include DSL::Helpers
|
105
|
-
|
106
|
-
attr_accessor :action_path, :param_skip, :param_use
|
107
|
-
def initialize(action_path, options = { })
|
108
|
-
self.action_path = action_path
|
109
|
-
self.param_skip = options[:skip] || [ ]
|
110
|
-
self.param_use = options[:use] || [ ]
|
111
|
-
end
|
112
|
-
|
113
|
-
def this_api_is_invalid! explain = ''
|
114
|
-
self[:deprecated] = true
|
115
|
-
end
|
116
|
-
alias_method :this_api_is_expired!, :this_api_is_invalid!
|
117
|
-
alias_method :this_api_is_unused!, :this_api_is_invalid!
|
118
|
-
alias_method :this_api_is_under_repair, :this_api_is_invalid!
|
119
|
-
|
120
|
-
def desc desc, inputs_descs = { }
|
121
|
-
@inputs_descs = inputs_descs
|
122
|
-
merge_desc_for_dryed_param
|
123
|
-
self[:description] = desc
|
124
|
-
end
|
125
|
-
|
126
|
-
# TODO: HACK
|
127
|
-
def merge_desc_for_dryed_param
|
128
|
-
@inputs_descs.each do |param_name, desc|
|
129
|
-
self[:parameters].each do |param|
|
130
|
-
if param_name.match?(?!) && param.processed[:name].to_s == param_name.to_s.delete(?!)
|
131
|
-
param.merge!(desc!: desc).process
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def param param_type, name, type, required, schema_hash = { }
|
138
|
-
return if param_skip.include? name
|
139
|
-
return unless param_use.include? name if param_use.present?
|
140
|
-
|
141
|
-
if @inputs_descs&.[](name).present?
|
142
|
-
schema_hash[:desc] = @inputs_descs[name]
|
143
|
-
elsif @inputs_descs&.[]("#{name}!".to_sym).present?
|
144
|
-
schema_hash[:desc!] = @inputs_descs["#{name}!".to_sym]
|
145
|
-
end
|
146
|
-
|
147
|
-
param_obj = ParamObj.new(name, param_type, type, required, schema_hash)
|
148
|
-
# The definition of the same name parameter will be overwritten
|
149
|
-
index = self[:parameters].map do |p|
|
150
|
-
p.processed[:name] if p.is_a? ParamObj
|
151
|
-
end.index name
|
152
|
-
if index.present?
|
153
|
-
self[:parameters][index] = param_obj
|
154
|
-
else
|
155
|
-
self[:parameters] << param_obj
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def process_params
|
160
|
-
self[:parameters].each_with_index do |p, index|
|
161
|
-
self[:parameters][index] = p.is_a?(ParamObj) ? p.process : p
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
def _param_agent name, type, schema_hash = { }
|
166
|
-
param "#{@param_type}".delete('!'), name, type, ("#{@param_type}".match?(/!/) ? :req : :opt), schema_hash
|
167
|
-
end
|
168
|
-
|
169
|
-
def param_ref component_key, *keys
|
170
|
-
self[:parameters].concat [component_key].concat(keys).map { |key| RefObj.new(:parameter, key).process }
|
171
|
-
end
|
172
|
-
|
173
|
-
def request_body required, media_type, desc = '', schema_hash = { }
|
174
|
-
self[:requestBody] = RequestBodyObj.new(required, media_type, desc, schema_hash).process
|
175
|
-
end
|
176
|
-
|
177
|
-
def _request_body_agent media_type, desc = '', schema_hash = { }
|
178
|
-
request_body ("#{@method_name}".match?(/!/) ? :req : :opt), media_type, desc, schema_hash
|
179
|
-
end
|
180
|
-
|
181
|
-
def body_ref component_key
|
182
|
-
self[:requestBody] = RefObj.new(:requestBody, component_key).process
|
183
|
-
end
|
184
|
-
|
185
|
-
def response_ref code_compkey_hash
|
186
|
-
code_compkey_hash.each do |code, component_key|
|
187
|
-
self[:responses].merge! code => RefObj.new(:response, component_key).process
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
# TODO: 目前只能写一句 request body,包括 form 和 file, 需要同时支持一下扁平化
|
192
|
-
def form desc = '', schema_hash = { }
|
193
|
-
body :form, desc, schema_hash
|
194
|
-
end
|
195
|
-
def form! desc = '', schema_hash = { }
|
196
|
-
body! :form, desc, schema_hash
|
197
|
-
end
|
198
|
-
def file media_type, desc = '', schema_hash = { type: File }
|
199
|
-
body media_type, desc, schema_hash
|
200
|
-
end
|
201
|
-
def file! media_type, desc = '', schema_hash = { type: File }
|
202
|
-
body! media_type, desc, schema_hash
|
203
|
-
end
|
204
|
-
|
205
|
-
def security scheme_name, requirements = [ ]
|
206
|
-
self[:security] << { scheme_name => requirements }
|
207
|
-
end
|
208
|
-
|
209
|
-
def server url, desc
|
210
|
-
self[:servers] << { url: url, description: desc }
|
211
|
-
end
|
212
|
-
end # ----------------------------------------- end of ApiInfoObj
|
213
|
-
end
|
214
|
-
end
|