zero-rails_openapi 1.0.0 → 1.1.0
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 +1 -1
- data/Gemfile.lock +1 -1
- data/README.md +244 -44
- data/README_zh.md +1 -0
- data/{lib → documentation}/examples/examples_controller.rb +2 -2
- data/{lib → documentation}/examples/open_api.rb +2 -2
- data/documentation/parameter.md +55 -0
- data/lib/oas_objs/schema_obj.rb +13 -11
- data/lib/open_api/dsl.rb +19 -13
- data/lib/open_api/dsl_inside_block.rb +8 -8
- data/lib/open_api/generator.rb +8 -6
- data/lib/open_api/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b90e9497b858c621362ea5413020a64bdbd59de
|
4
|
+
data.tar.gz: '067410229fdc842e1d2f65e3cf4fbb9482535044'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eefc1f9b19e39b1a7aee1f9817c9860da02b77f4feb94a22a72fa8dd11739e9e9b1cda3d23404bfeaefbeba9fd3a9cd2c06898f92a31f14c60eae3c1788b5b61
|
7
|
+
data.tar.gz: 1d5bf3b31a7e8ed246e50ecd63f869c0913185a0eb31c95490d32163f48ff5c0eba8e744ce4ec234ad72ff8821e3f246b515d0cec473cb3d4269eaf9c8bf11f5
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,29 @@
|
|
1
1
|
# ZRO: OpenApi 3 DocGenerator for Rails
|
2
2
|
|
3
3
|
Provide concise DSL for you to generate the OpenAPI Specification 3 (**OAS3**, formerly Swagger3) JSON file for Rails application,
|
4
|
-
then you can use Swagger
|
4
|
+
then you can use Swagger UI 3.2.0+ to show the documentation.
|
5
|
+
|
6
|
+
## Contributing
|
7
|
+
|
8
|
+
**Hi, here is ZhanDao. This gem was completed when I learned Ruby less than three months,
|
9
|
+
I'm not sure if it has problem, but it may have a lot to improve.
|
10
|
+
I'm looking forward to your issues and PRs, thanks!**
|
11
|
+
|
12
|
+
Currently, I think the most important TODO is the Unit Test (RSpec, I want is),
|
13
|
+
but I dont have enough time now = ▽ =
|
14
|
+
|
15
|
+
## Table of Contents
|
16
|
+
|
17
|
+
- [About OAS](https://github.com/zhandao/zero-rails_openapi#about-oas) (OpenAPI Specification)
|
18
|
+
- [Installation](https://github.com/zhandao/zero-rails_openapi#installation)
|
19
|
+
- [Configure](https://github.com/zhandao/zero-rails_openapi#configure)
|
20
|
+
- [Usage](https://github.com/zhandao/zero-rails_openapi#usage)
|
21
|
+
- [DSL for documenting your controller](https://github.com/zhandao/zero-rails_openapi#dsl-for-documenting-your-controller)
|
22
|
+
- [Generate JSON Documentation File](https://github.com/zhandao/zero-rails_openapi#generate-json-documentation-file)
|
23
|
+
- [Use Swagger UI(very beautiful web page) to show your Documentation](https://github.com/zhandao/zero-rails_openapi#use-swagger-uivery-beautiful-web-page-to-show-your-documentation)
|
24
|
+
- [Tricks](#tricks)
|
25
|
+
- [Write the DSL Somewhere Else]
|
26
|
+
- [Troubleshooting](https://github.com/zhandao/zero-rails_openapi#troubleshooting)
|
5
27
|
|
6
28
|
## About OAS
|
7
29
|
|
@@ -45,7 +67,7 @@ OpenApi.configure do |c|
|
|
45
67
|
|
46
68
|
c.register_apis = {
|
47
69
|
homepage_api: {
|
48
|
-
# [REQUIRED] ZRO will scan all the descendants of
|
70
|
+
# [REQUIRED] ZRO will scan all the descendants of root_controller, then generate their docs.
|
49
71
|
root_controller: Api::V1::BaseController,
|
50
72
|
|
51
73
|
# [REQUIRED] OAS Info Object: The section contains API information.
|
@@ -67,7 +89,7 @@ end
|
|
67
89
|
You can also set the *global configuration(/component)* of OAS:
|
68
90
|
Server Object / Security Scheme Object / Security Requirement Object ...
|
69
91
|
|
70
|
-
For more detailed configuration: [open_api.rb](https://github.com/zhandao/zero-rails_openapi/blob/
|
92
|
+
For more detailed configuration: [open_api.rb](https://github.com/zhandao/zero-rails_openapi/blob/masterdocumentation/examples/open_api.rb)
|
71
93
|
|
72
94
|
## Usage
|
73
95
|
|
@@ -84,16 +106,16 @@ class ApplicationController < ActionController::API
|
|
84
106
|
end
|
85
107
|
```
|
86
108
|
|
87
|
-
#### \> [DSL Usage Example](https://github.com/zhandao/zero-rails_openapi/blob/
|
109
|
+
#### \> [DSL Usage Example](https://github.com/zhandao/zero-rails_openapi/blob/masterdocumentation/examples/examples_controller.rb)
|
88
110
|
|
89
111
|
```ruby
|
90
112
|
class Api::V1::ExamplesController < Api::V1::BaseController
|
91
113
|
apis_set 'ExamplesController\'s APIs' do
|
92
114
|
schema :Dog => [ { id!: Integer, name: String }, dft: { id: 1, name: 'pet' } ]
|
115
|
+
query! :QueryCompUuid => [ :product_uuid, String, desc: 'product uuid' ]
|
93
116
|
path! :PathCompId => [ :id, Integer, desc: 'user id' ]
|
94
|
-
query! :QueryCompUuid => [ :product_uuid, { uuid: String, time: 'int32' }, desc: 'product uuid' ]
|
95
|
-
body! :RqBodyComp => [ :form ]
|
96
117
|
resp :RespComp => [ 'bad request', :json ]
|
118
|
+
body! :RqBodyComp => [ :form ]
|
97
119
|
end
|
98
120
|
|
99
121
|
open_api_set %i[index show], 'common response' do
|
@@ -128,71 +150,202 @@ end
|
|
128
150
|
|
129
151
|
#### \> Explanation
|
130
152
|
|
131
|
-
|
153
|
+
#### \>\> controller class methods ([source code](https://github.com/zhandao/zero-rails_openapi/blob/master/lib/open_api/dsl.rb))
|
132
154
|
|
133
|
-
- `
|
155
|
+
- `ctrl_path` (controller path) [Optional]
|
134
156
|
|
157
|
+
```ruby
|
158
|
+
# method signature
|
159
|
+
ctrl_path path
|
160
|
+
# usage
|
161
|
+
ctrl_path 'api/v1/examples'
|
162
|
+
```
|
163
|
+
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)).
|
164
|
+
[Here's a trick](): Using `ctrl_path`, you can write the DSL somewhere else
|
165
|
+
to simplify the current controller.
|
166
|
+
\* Take the tag from `path.split('/').last`
|
135
167
|
|
168
|
+
- `apis_set` [Optional]
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
# method signature
|
172
|
+
apis_set desc = '', external_doc_url = '', &block
|
173
|
+
# usage
|
174
|
+
apis_set 'ExamplesController\'s APIs' do
|
175
|
+
# DSL for defining components
|
176
|
+
end
|
177
|
+
```
|
178
|
+
desc and external_doc_url will be output to the tags[the current tag] (tag defaults to controller_name ), but are optional.
|
179
|
+
the focus is on the block, the DSL methods in the block will generate components.
|
136
180
|
|
137
181
|
- `open_api_set` [Optional]
|
138
182
|
|
183
|
+
this method is for DRYing.
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
# method signature
|
187
|
+
open_api_set method = :all, desc = '', &block
|
188
|
+
# usage
|
189
|
+
open_api_set :all, 'common response' do; end
|
190
|
+
open_api_set :index do; end
|
191
|
+
open_api_set [:index, :show] do; end
|
192
|
+
```
|
193
|
+
|
194
|
+
As you think, the DSL methods in the block will be executed to each API that you set by method.
|
195
|
+
|
196
|
+
- `open_api` [Required]
|
197
|
+
|
198
|
+
Define the specified API (in the following example is index).
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
# method signature
|
202
|
+
open_api method, summary = '', &block
|
203
|
+
# usage
|
204
|
+
open_api :index, '(SUMMARY) this api blah blah ...' do; end
|
205
|
+
```
|
139
206
|
|
140
|
-
- `open_api`
|
141
207
|
|
142
|
-
|
208
|
+
#### \>\> DSL methods inside *open_api* and *open_api_set*'s block ([source code](https://github.com/zhandao/zero-rails_openapi/blob/master/lib/open_api/dsl_inside_block.rb):: ApiInfoObj)
|
143
209
|
|
144
210
|
These methods in the block describe the specified API(s): description, valid?,
|
145
211
|
parameters, request body, responses, securities, servers.
|
146
212
|
|
213
|
+
(Here corresponds to OAS [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#operationObject))
|
214
|
+
|
147
215
|
- `this_api_is_invalid!`, its aliases are:
|
148
216
|
- `this_api_is_expired!`
|
149
217
|
- `this_api_is_unused!`
|
150
218
|
- `this_api_is_under_repair!`
|
151
219
|
|
152
|
-
```ruby
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
```
|
220
|
+
```ruby
|
221
|
+
# method signature
|
222
|
+
this_api_is_invalid! explain = ''
|
223
|
+
# usage
|
224
|
+
this_api_is_invalid! 'this api is expired!'
|
225
|
+
```
|
158
226
|
|
159
227
|
- `desc`: description for current API and its inputs (parameters and request body)
|
160
228
|
|
161
|
-
```ruby
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
```
|
229
|
+
```ruby
|
230
|
+
# method signature
|
231
|
+
desc desc, inputs_descs = { }
|
232
|
+
# usage
|
233
|
+
desc 'current API\'s description',
|
234
|
+
id: 'user id',
|
235
|
+
email_addr: 'email_addr\'s desc'
|
236
|
+
```
|
169
237
|
|
170
|
-
You can of course describe the input in it's DSL method (like `query! :done` this line),
|
171
|
-
but that will make it long and ugly. We recommend that unite descriptions in this place.
|
238
|
+
You can of course describe the input in it's DSL method (like `query! :done` [this line](https://github.com/zhandao/zero-rails_openapi#-dsl-usage-example)),
|
239
|
+
but that will make it long and ugly. We recommend that unite descriptions in this place.
|
172
240
|
|
173
|
-
- param family methods
|
241
|
+
- param family methods (OAS - [Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#parameterObject))
|
174
242
|
- `param`
|
175
243
|
- `param_ref`
|
176
|
-
- `header`, `path`, `query`, `cookie`
|
177
|
-
|
244
|
+
- `header`, `path`, `query`, `cookie` and bang methods: `header!`, `path!`, `query!`, `cookie!`
|
245
|
+
**The bang method(`!`) means it is required, so it is optional without `!`, the same below.**
|
246
|
+
|
247
|
+
Define the parameters for the API(operation).
|
248
|
+
You can use the Reference Object to link to parameters that are defined at the components/parameters by method param_ref().
|
249
|
+
|
250
|
+
```ruby
|
251
|
+
# method signature
|
252
|
+
param param_type, name, type, required, schema_hash = { }
|
253
|
+
# usage
|
254
|
+
param :query, :page, Integer, :req, range: { gt: 0, le: 5 }, desc: 'page'
|
178
255
|
|
256
|
+
# method signature
|
257
|
+
param_ref component_key, *component_keys
|
258
|
+
# usage
|
259
|
+
param_ref :PathCompId
|
260
|
+
param_ref :PathCompId, :QueryCompUuid, ...
|
179
261
|
|
180
|
-
|
262
|
+
# method signature
|
263
|
+
header name, type, schema_hash = { }
|
264
|
+
header! name, type, schema_hash = { }
|
265
|
+
query! name, type, schema_hash = { }
|
266
|
+
# usage
|
267
|
+
header! :'X-Token', String
|
268
|
+
query! :done, Boolean, must_be: false, default: true
|
269
|
+
```
|
270
|
+
|
271
|
+
[**>> More About Param DSL <<**](https://github.com/zhandao/zero-rails_openapi/blob/master/documentation/parameter.md)
|
272
|
+
|
273
|
+
- request_body family methods (OAS - [Request Body Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#requestBodyObject))
|
181
274
|
- `request_body`
|
182
275
|
- `body_ref`
|
183
276
|
- `body` and bang `body!`
|
277
|
+
- `form`, `form!`; `file`, `file!`
|
278
|
+
|
279
|
+
OpenAPI 3.0 uses the requestBody keyword to distinguish the payload from parameters.
|
280
|
+
Define the request body for the API(operation).
|
281
|
+
You can use the Reference Object to link to request body that is defined at the components/requestBodies by method body_ref().
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
# method signature
|
285
|
+
request_body required, media_type, desc = '', schema_hash = { }
|
286
|
+
# usage
|
287
|
+
request_body :opt, :form, type: { id!: Integer, name: String }
|
288
|
+
|
289
|
+
# method signature
|
290
|
+
body_ref component_key
|
291
|
+
# usage
|
292
|
+
body_ref :Body
|
293
|
+
|
294
|
+
# method signature
|
295
|
+
body(!) media_type, desc = '', schema_hash = { }
|
296
|
+
# usage
|
297
|
+
body :json
|
298
|
+
|
299
|
+
# method implement
|
300
|
+
def form desc = '', schema_hash = { }
|
301
|
+
body :form, desc, schema_hash
|
302
|
+
end
|
303
|
+
|
304
|
+
def file! media_type, desc = '', schema_hash = { type: File }
|
305
|
+
body! media_type, desc, schema_hash
|
306
|
+
end
|
307
|
+
```
|
184
308
|
|
309
|
+
**Notice:** Each API can only declare a request body.
|
310
|
+
That is, all of the above methods you can only choose one of them.
|
185
311
|
|
186
|
-
-
|
312
|
+
Media Type: We provide some [mapping](https://github.com/zhandao/zero-rails_openapi/blob/master/lib/oas_objs/media_type_obj.rb) from symbols to real media-types.
|
313
|
+
|
314
|
+
schema_hash: As above (see param), but more than a `type` (schema type).
|
315
|
+
|
316
|
+
- response family methods (OAS - [Response Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#response-object))
|
187
317
|
- `response` (`resp`)
|
188
318
|
- `response_ref`
|
189
319
|
- `default_response` (`dft_resp`)
|
190
|
-
- `error_response` (`other_response`, `oth_resp`, `error`, `err_resp`)
|
320
|
+
- `error_response` (`other_response`, `oth_resp`, `error`, `err_resp`): Are `response`'s aliases, should be used in the error response context.
|
321
|
+
|
322
|
+
Define the responses for the API(operation).
|
323
|
+
You can use the Response Object to link to request body that is defined at the components/responses by method response_ref().
|
324
|
+
|
325
|
+
```ruby
|
326
|
+
# method signature
|
327
|
+
response code, desc, media_type = nil, schema_hash = { }
|
328
|
+
# usage
|
329
|
+
response '200', 'query result export', :pdf, type: File
|
330
|
+
|
331
|
+
# method signature
|
332
|
+
response_ref code_compkey_hash
|
333
|
+
# usage
|
334
|
+
response_ref '700' => :RespComp, '800' => :RespComp
|
335
|
+
```
|
336
|
+
|
337
|
+
**practice:** Combined with wrong class, automatically generate error responses. TODO
|
191
338
|
|
192
|
-
|
339
|
+
- security: TODO
|
340
|
+
|
341
|
+
- server: TODO
|
342
|
+
|
343
|
+
#### \>\> 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 )
|
344
|
+
|
345
|
+
(Here corresponds to OAS [Components Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#componentsObject))
|
193
346
|
|
194
347
|
These methods in the block describe the current controller,
|
195
|
-
|
348
|
+
in other words, these methods eventually produce reusable components.
|
196
349
|
|
197
350
|
So, the following methods are used as above, except that you need to
|
198
351
|
name the key of the component, and pass it as the first of argument list, like:
|
@@ -208,23 +361,74 @@ The recommended writing is:
|
|
208
361
|
query! :QueryCompUuid => [:product_uuid, Integer, ...]
|
209
362
|
```
|
210
363
|
The DSL methods used to generate the components in this block are:
|
211
|
-
(explained above)
|
212
364
|
|
213
|
-
- param family methods (except `param_ref`)
|
214
|
-
- request_body family methods (except `body_ref`)
|
365
|
+
- param family methods (except `param_ref`) (explained above)
|
366
|
+
- request_body family methods (except `body_ref`) (explained above)
|
367
|
+
- schema: define Schema Object component.
|
368
|
+
|
369
|
+
```ruby
|
370
|
+
# method signature
|
371
|
+
schema component_key, type, schema_hash
|
372
|
+
# usage
|
373
|
+
schema :Dog => [ { id!: Integer, name: String }, dft: { id: 1, name: 'pet' } ]
|
374
|
+
# or (unrecommended)
|
375
|
+
schema :Dog, { id!: Integer, name: String }, dft: { id: 1, name: 'pet' }
|
376
|
+
```
|
215
377
|
|
378
|
+
### Generate JSON Documentation File
|
216
379
|
|
380
|
+
Initializer or Console, run:
|
217
381
|
|
218
|
-
|
382
|
+
```ruby
|
383
|
+
OpenApi.write_docs
|
384
|
+
```
|
219
385
|
|
386
|
+
Then the JSON files will be written to the directory you set. (Each API a file.)
|
220
387
|
|
221
|
-
### Use Swagger
|
388
|
+
### Use Swagger UI(very beautiful web page) to show your Documentation
|
222
389
|
|
390
|
+
Download [Swagger UI](https://github.com/swagger-api/swagger-ui) (version >= 2.3.0 support the OAS3)
|
391
|
+
to your project,
|
392
|
+
modify the default JSON file path(url) in the index.html
|
393
|
+
(window.onload >> SwaggerUIBundle >> url).
|
394
|
+
In order to use it, you may have to enable CORS, [see](https://github.com/swagger-api/swagger-ui#cors-support)
|
223
395
|
|
224
|
-
|
396
|
+
### Tricks
|
225
397
|
|
398
|
+
#### Write the DSL Somewhere Else (Recommend)
|
226
399
|
|
400
|
+
Does your documentation take too mant lines?
|
401
|
+
Do you want to separate documentation from business controller to simplify both?
|
402
|
+
Very easy! Just use `ctrl_path`.
|
227
403
|
|
404
|
+
```ruby
|
405
|
+
# config/initializers/open_api.rb
|
406
|
+
# in your configure
|
407
|
+
root_controller: BaseDoc
|
408
|
+
|
409
|
+
# app/api_doc/base_doc.rb
|
410
|
+
require 'open_api/dsl'
|
411
|
+
|
412
|
+
class BaseDoc < Object
|
413
|
+
include OpenApi::DSL
|
414
|
+
end
|
415
|
+
|
416
|
+
# app/api_doc/v1/examples_doc.rb
|
417
|
+
class V1::ExamplesDoc < BaseDoc
|
418
|
+
ctrl_path 'api/v1/examples'
|
419
|
+
|
420
|
+
open_api :index do
|
421
|
+
# ...
|
422
|
+
end
|
423
|
+
end
|
424
|
+
```
|
425
|
+
|
426
|
+
Notes: convention is the file name ends with `_doc.rb`
|
427
|
+
|
428
|
+
## Troubleshooting
|
429
|
+
|
430
|
+
- **You wrote document of the current API, but not find in the generated json file?**
|
431
|
+
Check your routing settings.
|
228
432
|
|
229
433
|
## Development
|
230
434
|
|
@@ -232,10 +436,6 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
232
436
|
|
233
437
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
234
438
|
|
235
|
-
## Contributing
|
236
|
-
|
237
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/zhandao/zero-rails_openapi. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
238
|
-
|
239
439
|
## License
|
240
440
|
|
241
441
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/README_zh.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
TODO = ▽ =
|
@@ -1,10 +1,10 @@
|
|
1
1
|
class Api::V1::ExamplesController < Api::V1::BaseController
|
2
2
|
apis_set 'ExamplesController\'s APIs' do
|
3
3
|
schema :Dog => [ { id!: Integer, name: String }, dft: { id: 1, name: 'pet' } ]
|
4
|
+
query! :QueryCompUuid => [ :product_uuid, String, desc: 'product uuid' ]
|
4
5
|
path! :PathCompId => [ :id, Integer, desc: 'user id' ]
|
5
|
-
query! :QueryCompUuid => [ :product_uuid, { uuid: String, time: 'int32' }, desc: 'product uuid' ]
|
6
|
-
body! :RqBodyComp => [ :form ]
|
7
6
|
resp :RespComp => [ 'bad request', :json ]
|
7
|
+
body! :RqBodyComp => [ :form ]
|
8
8
|
end
|
9
9
|
|
10
10
|
open_api_set %i[index show], 'common response' do
|
@@ -53,11 +53,11 @@ OpenApi.configure do |c|
|
|
53
53
|
# This URL supports Server Variables and MAY be relative,
|
54
54
|
# to indicate that the host location is relative to the location where
|
55
55
|
# the OpenAPI document is being served.
|
56
|
-
url: 'http://localhost:2333
|
56
|
+
url: 'http://localhost:2333',
|
57
57
|
# An optional string describing the host designated by the URL.
|
58
58
|
description: 'Optional server description, e.g. Main (production) server'
|
59
59
|
},{
|
60
|
-
url: 'http://localhost:3332
|
60
|
+
url: 'http://localhost:3332',
|
61
61
|
description: 'Optional server description, e.g. Internal staging server for testing'
|
62
62
|
}],
|
63
63
|
|
@@ -0,0 +1,55 @@
|
|
1
|
+
### More Explanation of Param()
|
2
|
+
|
3
|
+
- [param_type] OpenAPI 3.0 distinguishes between the following parameter types based on the parameter location:
|
4
|
+
**header, path, query, cookie**. [more](https://swagger.io/docs/specification/describing-parameters/)
|
5
|
+
|
6
|
+
- [name] parameter name. If param_type is :path, it must correspond to the associated path segment form
|
7
|
+
the routing path, for example: the path is `/good/:id`, then you have to declare a path parameter with name `id`.
|
8
|
+
|
9
|
+
- [type] parameter (schema) type. Support all [data types](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#dataTypes) defined in OAS.
|
10
|
+
In addition, you can use `format` in schema_hash to define in fine detail the data type being used, like:
|
11
|
+
int32, float, date ...
|
12
|
+
All the types you can use are:
|
13
|
+
- **String, 'binary', 'base64'**
|
14
|
+
- **Integer, Long, 'int32', 'int64', Float, Double**
|
15
|
+
- **File** (it will be converted as `{ type: 'string', format: OpenApi.config.dft_file_format }`)
|
16
|
+
- **Date, DateTime**
|
17
|
+
- **Boolean**
|
18
|
+
- **Array**: `Array[String]` or `[String]`
|
19
|
+
- Nested Array: `[[[Integer]]]`
|
20
|
+
- **Object**: you can use just `Object`, or use a hash to declare its properties `{ id!: Integer, name: String }`
|
21
|
+
(`!` bang key means it is required).
|
22
|
+
- Nested Object: `{ id!: Integer, name: { first: String, last: String } }`
|
23
|
+
- Nested Array and Object: `[[{ id!: Integer, name: { first: String, last: String } }]]`
|
24
|
+
- **:ComponentKey**: the Symbol value pass to type will generate a Schema Reference Object link
|
25
|
+
to the component correspond to ComponentKey.
|
26
|
+
|
27
|
+
You can use `Object.const_set()` to define a constant that does not exist, but note that
|
28
|
+
the value you set could not be a Symbol (it will be explained as a Ref Object), should be a String.
|
29
|
+
|
30
|
+
- [required] :opt or :req
|
31
|
+
|
32
|
+
- The [[schema]](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#schemaObject) defining the type used for the parameter.
|
33
|
+
schema_hash(optional) will be used to generate Schema Object inside Parameter Object.
|
34
|
+
[source code](https://github.com/zhandao/zero-rails_openapi/blob/master/lib/oas_objs/schema_obj.rb)
|
35
|
+
You can set the schema by following keys (all are optional), the words in parentheses are available aliases of the keys:
|
36
|
+
- enum (values, allowable_values)
|
37
|
+
Must be Array or Range(will be converted to Array)
|
38
|
+
- value (must_be, allowable_value)
|
39
|
+
Single value, could be a String, Array ...
|
40
|
+
- range (number_range)
|
41
|
+
Allow value in this continuous range. Set this field like this: `{ gt: 0, le: 5 }`
|
42
|
+
- length (lth)
|
43
|
+
Must be an Integer, Integer Array, Integer Range, or the following format Symbol: `:gt_`, `:ge_`, `:lt_`, `:le_`, examples: :ge_5 means "greater than or equal 5"; :lt_9 means "lower than 9".
|
44
|
+
- format (fmt)
|
45
|
+
- is (is_a)
|
46
|
+
1. It's not in OAS, just an addition field for better express.You can see it as `format`, but in fact they are quite different.
|
47
|
+
2. Look at this example: the `format` is set to "int32", but you also want to express that this
|
48
|
+
schema is an "id" format —— this cannot be expressed in the current OAS version.
|
49
|
+
3. So I suggest that the value of `format` should related to data type, `is` should be an entity.
|
50
|
+
4. ZRO defaults to identify whether `is` patterns matched the name, then automatically generate `is`.
|
51
|
+
for example the parameter name "user_email" will generate "is: email". Default `is` options are:
|
52
|
+
[email phone password uuid uri url time date], to overwrite it you can set it in initializer `c.is_options = %w[]`.
|
53
|
+
5. If type is Object, for describing each property's schema, the only way is use ref type, like: `{ id: :Id, name: :Name }`
|
54
|
+
- pattern (regexp, pr, reg)
|
55
|
+
- default (dft, default_value)
|
data/lib/oas_objs/schema_obj.rb
CHANGED
@@ -51,7 +51,7 @@ module OpenApi
|
|
51
51
|
{ type: t }
|
52
52
|
end
|
53
53
|
end
|
54
|
-
def recursive_obj_type(t) # DSL use {
|
54
|
+
def recursive_obj_type(t) # DSL use { prop_name: prop_type } to represent object structure
|
55
55
|
return processed_type(t) unless t.is_a? Hash
|
56
56
|
|
57
57
|
_schema = {
|
@@ -59,9 +59,9 @@ module OpenApi
|
|
59
59
|
properties: { },
|
60
60
|
required: [ ]
|
61
61
|
}
|
62
|
-
t.each do |
|
63
|
-
_schema[:required] << "#{
|
64
|
-
_schema[:properties]["#{
|
62
|
+
t.each do |prop_name, prop_type|
|
63
|
+
_schema[:required] << "#{prop_name}".delete('!') if "#{prop_name}".match? '!'
|
64
|
+
_schema[:properties]["#{prop_name}".delete('!').to_sym] = recursive_obj_type prop_type
|
65
65
|
end
|
66
66
|
_schema.keep_if &value_present
|
67
67
|
end
|
@@ -69,7 +69,8 @@ module OpenApi
|
|
69
69
|
if t.is_a? Array
|
70
70
|
{
|
71
71
|
type: 'array',
|
72
|
-
|
72
|
+
# TODO: [[String], [Integer]] <= One Of? Object?(0=>[S], 1=>[I])
|
73
|
+
items: recursive_array_type(t.first)
|
73
74
|
}
|
74
75
|
else
|
75
76
|
processed_type t
|
@@ -134,17 +135,18 @@ module OpenApi
|
|
134
135
|
_value: %i[must_be value allowable_value ],
|
135
136
|
_range: %i[range number_range ],
|
136
137
|
_length: %i[length lth ],
|
137
|
-
_is: %i[is_a is ], # NOT OAS Spec, just
|
138
|
+
_is: %i[is_a is ], # NOT OAS Spec, just an addition
|
138
139
|
_format: %i[format fmt ],
|
139
140
|
_pattern: %i[pattern regexp pr reg ],
|
140
141
|
_default: %i[default dft default_value ],
|
141
142
|
}.each do |key, aliases|
|
142
143
|
define_method key do
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
144
|
+
self[key].tap do |it|
|
145
|
+
aliases.each do |alias_name|
|
146
|
+
break if it == false
|
147
|
+
it ||= self[alias_name]
|
148
|
+
end if it.nil?
|
149
|
+
end
|
148
150
|
end
|
149
151
|
define_method "#{key}=" do |value| self[key] = value end
|
150
152
|
end
|
data/lib/open_api/dsl.rb
CHANGED
@@ -8,10 +8,15 @@ module OpenApi
|
|
8
8
|
|
9
9
|
# TODO: Doc-Block Comments
|
10
10
|
module ClassMethods
|
11
|
+
def ctrl_path path
|
12
|
+
@_ctrl_path = path
|
13
|
+
@_apis_tag = path.split('/').last.camelize
|
14
|
+
end
|
15
|
+
|
11
16
|
def apis_set desc = '', external_doc_url = '', &block
|
12
|
-
@
|
17
|
+
@_ctrl_infos = { }
|
13
18
|
# current `tag`, this means that tags is currently divided by controllers.
|
14
|
-
tag = @_ctrl_infos[:tag] = { name: controller_name.camelize }
|
19
|
+
tag = @_ctrl_infos[:tag] = { name: @_apis_tag ||= controller_name.camelize }
|
15
20
|
tag[:description] = desc if desc.present?
|
16
21
|
tag[:externalDocs] = { description: 'ref', url: external_doc_url } if external_doc_url.present?
|
17
22
|
|
@@ -20,38 +25,39 @@ module OpenApi
|
|
20
25
|
end
|
21
26
|
|
22
27
|
def open_api method, summary = '', &block
|
23
|
-
|
24
|
-
|
28
|
+
apis_set if @_ctrl_infos.nil?
|
29
|
+
|
30
|
+
# select the routing info (corresponding to the current method) from the routing list.
|
31
|
+
action_path = "#{@_ctrl_path ||= controller_path}##{method}"
|
25
32
|
routes_info = ctrl_routes_list.select { |api| api[:action_path].match? /^#{action_path}$/ }.first
|
26
|
-
puts "[zero-rails_openapi] Routing mapping failed: #{
|
33
|
+
puts "[zero-rails_openapi] Routing mapping failed: #{@_ctrl_path}##{method}" or return if routes_info.nil?
|
27
34
|
|
28
35
|
# structural { path: { http_method:{ } } }, for Paths Object.
|
29
|
-
|
30
|
-
path = @_api_infos[routes_info[:path]] ||= { }
|
36
|
+
path = (@_api_infos ||= { })[routes_info[:path]] ||= { }
|
31
37
|
current_api = path[routes_info[:http_verb]] =
|
32
|
-
ApiInfoObj.new(action_path).merge!
|
38
|
+
ApiInfoObj.new(action_path).merge! summary: summary, operationId: method, tags: [@_apis_tag]
|
33
39
|
|
34
40
|
current_api.tap do |it|
|
35
41
|
it.instance_eval &block if block_given?
|
36
42
|
[method, :all].each do |key| # blocks_store_key
|
37
|
-
@
|
43
|
+
@_apis_blocks&.[](key)&.each { |blk| it.instance_eval &blk }
|
38
44
|
end
|
39
45
|
end
|
40
46
|
end
|
41
47
|
|
42
48
|
# For DRY; method could be symbol array
|
43
49
|
def open_api_set method = :all, desc = '', &block
|
44
|
-
@
|
50
|
+
@_apis_blocks ||= { }
|
45
51
|
if method.is_a? Array
|
46
|
-
method.each { |m| (@
|
52
|
+
method.each { |m| (@_apis_blocks[m.to_sym] ||= [ ]) << block }
|
47
53
|
else
|
48
|
-
(@
|
54
|
+
(@_apis_blocks[method.to_sym] ||= [ ]) << block
|
49
55
|
end
|
50
56
|
end
|
51
57
|
|
52
58
|
def ctrl_routes_list
|
53
59
|
@routes_list ||= Generator.generate_routes_list
|
54
|
-
@routes_list[
|
60
|
+
@routes_list[@_ctrl_path]
|
55
61
|
end
|
56
62
|
end
|
57
63
|
end
|
@@ -9,7 +9,7 @@ module OpenApi
|
|
9
9
|
module CommonDSL
|
10
10
|
def arrow_writing_support
|
11
11
|
Proc.new do |args, executor|
|
12
|
-
if args.count == 1 && args
|
12
|
+
if args.count == 1 && args.first.is_a?(Hash)
|
13
13
|
send(executor, args[0].keys.first, *args[0].values.first)
|
14
14
|
else
|
15
15
|
send(executor, *args)
|
@@ -26,12 +26,12 @@ module OpenApi
|
|
26
26
|
|
27
27
|
%i[body body!].each do |method|
|
28
28
|
define_method method do |*args|
|
29
|
-
@
|
29
|
+
@method_name = method
|
30
30
|
_request_body_agent *args
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
# code represents `component_key` when
|
34
|
+
# code represents `component_key` when declare response component
|
35
35
|
def _response code, desc, media_type = nil, schema_hash = { }
|
36
36
|
(self[:responses] ||= { }).merge! ResponseObj.new(code, desc, media_type, schema_hash).process
|
37
37
|
end
|
@@ -45,9 +45,9 @@ module OpenApi
|
|
45
45
|
end
|
46
46
|
|
47
47
|
{ # alias_methods mapping
|
48
|
-
response: %i[resp
|
49
|
-
default_response: %i[dft_resp
|
50
|
-
error_response: %i[other_response oth_resp
|
48
|
+
response: %i[error_response resp ],
|
49
|
+
default_response: %i[dft_resp dft_response ],
|
50
|
+
error_response: %i[other_response oth_resp error err_resp],
|
51
51
|
}.each do |original_name, aliases|
|
52
52
|
aliases.each do |alias_name|
|
53
53
|
alias_method alias_name, original_name
|
@@ -91,7 +91,7 @@ module OpenApi
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def _request_body_arg_agent component_key, media_type, desc = '', schema_hash = { }
|
94
|
-
request_body component_key, ("#{@
|
94
|
+
request_body component_key, ("#{@method_name}".match?(/!/) ? :req : :opt), media_type, desc, schema_hash
|
95
95
|
end
|
96
96
|
end # ----------------------------------------- end of CtrlInfoObj
|
97
97
|
|
@@ -136,7 +136,7 @@ module OpenApi
|
|
136
136
|
end
|
137
137
|
|
138
138
|
def _request_body_agent media_type, desc = '', schema_hash = { }
|
139
|
-
request_body ("#{@
|
139
|
+
request_body ("#{@method_name}".match?(/!/) ? :req : :opt), media_type, desc, schema_hash
|
140
140
|
end
|
141
141
|
|
142
142
|
def body_ref component_key
|
data/lib/open_api/generator.rb
CHANGED
@@ -8,9 +8,10 @@ module OpenApi
|
|
8
8
|
|
9
9
|
module ClassMethods
|
10
10
|
def generate_docs(api_name = nil)
|
11
|
-
|
11
|
+
Dir['./app/controllers/**/*.rb'].each { |file| require file }
|
12
|
+
Dir['./app/**/*_doc.rb'].each { |file| require file }
|
12
13
|
if api_name.present?
|
13
|
-
{ api_name => generate_doc(api_name) }
|
14
|
+
[{ api_name => generate_doc(api_name) }]
|
14
15
|
else
|
15
16
|
OpenApi.apis.keys.map { |api_key| { api_key => generate_doc(api_key)} }.reduce({ }, :merge)
|
16
17
|
end
|
@@ -27,17 +28,18 @@ module OpenApi
|
|
27
28
|
})
|
28
29
|
|
29
30
|
settings[:root_controller].descendants.each do |ctrl|
|
31
|
+
ctrl_infos = ctrl.instance_variable_get('@_ctrl_infos')
|
32
|
+
next if ctrl_infos.nil?
|
30
33
|
doc[:paths].merge! ctrl.instance_variable_get('@_api_infos')
|
31
|
-
doc[:tags] <<
|
32
|
-
doc[:components].merge!
|
34
|
+
doc[:tags] << ctrl_infos[:tag]
|
35
|
+
doc[:components].merge! ctrl_infos[:components]
|
33
36
|
end
|
34
37
|
doc[:components].delete_if { |_,v| v.blank? }
|
35
|
-
($open_apis ||= { })[api_name] ||= HashWithIndifferentAccess.new
|
38
|
+
($open_apis ||= { })[api_name] ||= HashWithIndifferentAccess.new(doc.delete_if { |_,v| v.blank? })
|
36
39
|
end
|
37
40
|
|
38
41
|
def write_docs
|
39
42
|
docs = generate_docs
|
40
|
-
# puts docs
|
41
43
|
output_path = OpenApi.config.file_output_path
|
42
44
|
FileUtils.mkdir_p output_path
|
43
45
|
docs.each do |api_name, doc|
|
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.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- zhandao
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-09-
|
11
|
+
date: 2017-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -69,11 +69,13 @@ files:
|
|
69
69
|
- Gemfile.lock
|
70
70
|
- LICENSE.txt
|
71
71
|
- README.md
|
72
|
+
- README_zh.md
|
72
73
|
- Rakefile
|
73
74
|
- bin/console
|
74
75
|
- bin/setup
|
75
|
-
-
|
76
|
-
-
|
76
|
+
- documentation/examples/examples_controller.rb
|
77
|
+
- documentation/examples/open_api.rb
|
78
|
+
- documentation/parameter.md
|
77
79
|
- lib/oas_objs/helpers.rb
|
78
80
|
- lib/oas_objs/media_type_obj.rb
|
79
81
|
- lib/oas_objs/param_obj.rb
|