zero-rails_openapi 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7670c75c3357ce813ab3664c7403386a9e544c58
4
- data.tar.gz: 00f1a35580391046946ce002d39ef66dad3d0a71
3
+ metadata.gz: 5b6fc0f663e643a98f744b0f75431c21a9484014
4
+ data.tar.gz: 2f0318d949040999a345d91b08f9d1ed63b525f3
5
5
  SHA512:
6
- metadata.gz: e149d59f5e44dcf5349f980debf0f000345a3bf398fe9084dda7733305af6a99c42b9aed21f2fc87eabda030ca8e24e0c423a10e8ab2617296ad136dd60bea53
7
- data.tar.gz: 12e6d7ad092fda104aa439dcf17c64bcb93d5a0e9467c1e5b4a27802d0ff21dc0c4a63c99523958f7bea7f174a877e43a04c0b5d1aad1f446343eb2e46e9a514
6
+ metadata.gz: 2e9a713e9bb2296ffb3bd9e0c5433256cf90ccf72135c92495d4eebb53778e53a151b6882bf47baf137c2b000b40fc181974f5cfa0dce45048665be7f05f29b4
7
+ data.tar.gz: 1691cb0d3d6e3e6517f6439ca869a7eeddf7032f6d3c9eb3118e446ac62a8d1e89da05d90cbfda7b5f32a63c70e4601749b11686b1c430664af8fd7426d1b74b
data/Gemfile CHANGED
@@ -3,4 +3,8 @@ source "https://rubygems.org"
3
3
  git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in zero-rails_openapi.gemspec
6
- gemspec
6
+ gemspec
7
+
8
+ ruby '>= 2.3.0'
9
+
10
+ gem 'activesupport'
@@ -1,12 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- zero-rails_openapi (1.3.0)
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//examples/open_api.rb)
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/masterdocumentation/examples/examples_controller.rb)
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
- apis_set 'ExamplesController\'s APIs' do
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-recommend): Using `ctrl_path`, you can 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
- - `apis_set` [Optional]
176
+ - `apis_tag` [Optional]
177
177
 
178
178
  ```ruby
179
179
  # method signature
180
- apis_set desc = '', external_doc_url = '', &block
180
+ apis_tag name: nil, desc: '', external_doc_url: ''
181
181
  # usage
182
- apis_set 'ExamplesController\'s APIs' do
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
- desc and external_doc_url will be output to the tags[the current tag] (tag defaults to controller_name ), but are optional.
187
- the focus is on the block, the DSL methods in the block will generate components.
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 = '', options = { }, &block
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` or `bd` to the third parameter,
215
- and `generate_jbuilder_file` in your setting file is set `true`,
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
- For example, see: [open_api.rb](https://github.com/zhandao/zero-rails_openapi/blob//examples/open_api.rb)
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, inputs_descs = { }
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
- **The bang method(`!`) means it is required, so it is optional without `!`, the same below.**
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, :page, Integer, :req, range: { gt: 0, le: 5 }, desc: 'page'
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, :QueryCompUuid, ...
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! :'Token', String
283
- query! :done, Boolean, must_be: false, default: true
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//parameter.md)
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(!) media_type, desc = '', schema_hash = { }
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 apis_set'block ([code source](https://github.com/zhandao/zero-rails_openapi/blob/master/lib/open_api/dsl_inside_block.rb):: CtrlInfoObj )
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//parameter.md#type)
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/masterdocumentation/examples/auto_gen_doc.rb).
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
- `search field, allows:<br/>1/ name<br/>2/ creator,<br/>3/ category<br/>4/ price<br/>`
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 Responses Based on DB Schema
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! :Token, String, desc: 'user token'
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
- # errors = error_class.errors
77
- # cur_errs = (errors[action.to_sym] || []) + (errors[:private] || [ ]) + (errors[:_public] || [ ])
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
- apis_set 'ExamplesController\'s APIs' do
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: [ :Token ] do # use parameters write in AutoGenDoc#api_dry
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: token do
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: [ :Token, :id ]
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: [ :Token, :id ]
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
- <<-FILE
91
- json.partial! 'api/base', total: @data.count
92
-
93
- json.data do
94
- # @data = @data.page(@_page).per(@_rows) if @_page || @_rows
95
- # json.array! @data do |datum|
96
- json.array! @data.page(@_page).per(@_rows) do |datum|
97
- json.(datum, *datum.show_attrs) if datum.present?
98
- end
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
- <<-FILE
105
- json.partial! 'api/base', total: 1
106
-
107
- json.data do
108
- json.array! [ @data ] do |datum|
109
- json.(datum, *datum.show_attrs) if datum.present?
110
- end
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
- <<-FILE
117
- json.partial! 'api/success'
118
+ <<~FILE
119
+ # *** Generated by ZRO ***
120
+ json.partial! 'api/success'
118
121
  FILE
119
122
  ),
120
123
 
121
124
  success_or_not: (
122
- <<-FILE
123
- unless @status
124
- # @_code, @_msg = @error_info.present? ? @error_info : ApiError.action_failed.info
125
- end
126
-
127
- json.partial! 'api/base', total: 0
128
- json.data ''
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 # Generate doc when Rails initializing
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(code, desc, media_type, schema_hash)
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
- { code => processed }
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
@@ -40,7 +40,7 @@ module OpenApi
40
40
 
41
41
  reduceee(processed_desc options).then_merge!
42
42
  end
43
- alias_method :process, :process_for
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 "#{prop_name}".match? '!'
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
@@ -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
- <<-FILE
53
- json.partial! 'api/base', total: @data.count
54
-
55
- json.data do
56
- # @data = @data.page(@_page).per(@_rows) if @_page || @_rows
57
- # json.array! @data do |datum|
58
- json.array! @data.page(@_page).per(@_rows) do |datum|
59
- json.(datum, *datum.show_attrs) if datum.present?
60
- end
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
- <<-FILE
67
- json.partial! 'api/base', total: 1
68
-
69
- json.data do
70
- json.array! [ @data ] do |datum|
71
- json.(datum, *datum.show_attrs) if datum.present?
72
- end
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
- <<-FILE
79
- json.partial! 'api/success'
84
+ <<~FILE
85
+ # *** Generated by ZRO ***
86
+ json.partial! 'api/success'
80
87
  FILE
81
88
  ),
82
89
 
83
90
  success_or_not: (
84
- <<-FILE
85
- unless @status
86
- # @_code, @_msg = @error_info.present? ? @error_info : ApiError.action_failed.info
87
- end
88
-
89
- json.partial! 'api/base', total: 0
90
- json.data ''
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
  }
@@ -0,0 +1,13 @@
1
+ module OpenApi
2
+ module ConfigDSL
3
+ def self.included(base)
4
+ base.class_eval do
5
+ module_function
6
+
7
+ def info
8
+ 1
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,4 +1,5 @@
1
- require 'open_api/dsl_inside_block'
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 apis_set desc = '', external_doc_url = '', &block
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
- tag = @_ctrl_infos[:tag] = { name: @_apis_tag ||= controller_name.camelize }
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 if block_given?
29
+ current_ctrl.instance_eval &block
30
+ current_ctrl._process_objs
25
31
  end
26
32
 
27
- def open_api method, summary = '', options = { }, &block
28
- apis_set if @_ctrl_infos.nil?
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, options.slice(:skip, :use))
45
+ ApiInfoObj.new(action_path, skip: skip, use: use)
39
46
  .merge! description: '', summary: summary, operationId: method, tags: [@_apis_tag],
40
- parameters: [ ], requestBody: '', responses: { }, security: [ ], servers: [ ]
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 = nil
53
+ api.param_use = [ ] # skip 和 use 是对 dry 块而言的
54
54
  api.instance_eval &block if block_given?
55
- api.instance_eval { process_params }
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
@@ -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 = true)
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` is generated." % "#{doc_name}"
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(option)
58
+ def self.generate_builder_file(action_path, builder)
59
59
  return unless Config.generate_jbuilder_file
60
- return unless option[:builder]
61
- dir_path = "app/views/#{option[:path]}"
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}/#{option[:action]}.json.jbuilder"
64
- File.open(file_path, 'w') do |file|
65
- file.write Config.jbuilder_templates[option[:builder]]
66
- puts "[ZRO] JBuilder file generated: #{option[:path]}/#{option[:action]}"
67
- end unless !Config.overwrite_jbuilder_file && File::exists?(file_path)
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.match?(/:/) ? "{#{item[1..-1]}}" : 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
@@ -1,3 +1,3 @@
1
1
  module OpenApi
2
- VERSION = '1.3.0'
2
+ VERSION = '1.3.1'
3
3
  end
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.0
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-10-26 00:00:00.000000000 Z
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/dsl_inside_block.rb
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.12
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