zero-rails_openapi 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|