zero-rails_openapi 1.1.1 → 1.2.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.lock +1 -1
- data/README.md +82 -14
- data/documentation/examples/auto_gen_desc.rb +28 -0
- data/documentation/examples/auto_gen_dsl.rb +42 -0
- data/documentation/examples/examples_controller.rb +17 -3
- data/documentation/examples/open_api.rb +3 -3
- data/documentation/parameter.md +19 -13
- data/lib/oas_objs/helpers.rb +5 -5
- data/lib/oas_objs/media_type_obj.rb +29 -29
- data/lib/oas_objs/param_obj.rb +16 -7
- data/lib/oas_objs/ref_obj.rb +1 -1
- data/lib/oas_objs/request_body_obj.rb +2 -2
- data/lib/oas_objs/response_obj.rb +1 -1
- data/lib/oas_objs/schema_obj.rb +76 -20
- data/lib/open_api/config.rb +4 -4
- data/lib/open_api/dsl.rb +9 -4
- data/lib/open_api/dsl_inside_block.rb +43 -13
- data/lib/open_api/generator.rb +12 -12
- data/lib/open_api/version.rb +1 -1
- data/zero-rails_openapi.gemspec +4 -3
- metadata +9 -7
- data/lib/takes/open_api.rake +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ade759189024d01051f73655ca7f55d14264598c
|
4
|
+
data.tar.gz: f97aa2a93a2b88af05e8326e5d1ef894a205cc47
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b9ba73d3704dff23f9e5fc9d515a87ad2e822b7fd08aa9207e9e47d0e47e5ece4ba66bed8514e3465e87ddf761060ed73c2f6cfb8c79be7438a4f7ebe5452eb
|
7
|
+
data.tar.gz: eb6048d787fbc7f3001e2fe7b47b999e404282b801741c07248ee0a6516c64a24b8790f5093383aa5f816a902178752fd8f830977ba4ee55dc4fc36f5b7e4fdc
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# ZRO: OpenApi 3 DocGenerator for Rails
|
2
2
|
|
3
|
-
|
3
|
+
[](https://badge.fury.io/rb/zero-rails_openapi)
|
4
|
+
[](https://travis-ci.org/zhandao/zero-rails_openapi)
|
5
|
+
|
6
|
+
Provide concise DSL for generating the OpenAPI Specification 3 (**OAS3**, formerly Swagger3) documentation JSON file for Rails application,
|
4
7
|
then you can use Swagger UI 3.2.0+ to show the documentation.
|
5
8
|
|
6
9
|
## Contributing
|
@@ -22,7 +25,9 @@ but I dont have enough time now = ▽ =
|
|
22
25
|
- [Generate JSON Documentation File](#generate-json-documentation-file)
|
23
26
|
- [Use Swagger UI(very beautiful web page) to show your Documentation](#use-swagger-uivery-beautiful-web-page-to-show-your-documentation)
|
24
27
|
- [Tricks](#tricks)
|
25
|
-
- [Write
|
28
|
+
- [Write DSL Somewhere Else](#trick1-write-the-dsl-somewhere-else)
|
29
|
+
- [DRYing](#trick2-drying)
|
30
|
+
- [Auto Generate Description](#trick3-auto-generate-description)
|
26
31
|
- [Troubleshooting](#troubleshooting)
|
27
32
|
|
28
33
|
## About OAS
|
@@ -65,7 +70,7 @@ OpenApi.configure do |c|
|
|
65
70
|
# [REQUIRED] The output location where .json doc file will be written to.
|
66
71
|
c.file_output_path = 'public/open_api'
|
67
72
|
|
68
|
-
c.
|
73
|
+
c.register_docs = {
|
69
74
|
homepage_api: {
|
70
75
|
# [REQUIRED] ZRO will scan all the descendants of root_controller, then generate their docs.
|
71
76
|
root_controller: Api::V1::BaseController,
|
@@ -89,7 +94,7 @@ end
|
|
89
94
|
You can also set the *global configuration(/component)* of OAS:
|
90
95
|
Server Object / Security Scheme Object / Security Requirement Object ...
|
91
96
|
|
92
|
-
For more detailed configuration: [open_api.rb](https://github.com/zhandao/zero-rails_openapi/blob/
|
97
|
+
For more detailed configuration: [open_api.rb](https://github.com/zhandao/zero-rails_openapi/blob/master/documentation/examples/open_api.rb)
|
93
98
|
|
94
99
|
## Usage
|
95
100
|
|
@@ -108,17 +113,18 @@ class ApplicationController < ActionController::API
|
|
108
113
|
|
109
114
|
#### \> [DSL Usage Example](https://github.com/zhandao/zero-rails_openapi/blob/masterdocumentation/examples/examples_controller.rb)
|
110
115
|
|
116
|
+
(TODO: I consider that, here should be put in a the simplest case.)
|
111
117
|
```ruby
|
112
118
|
class Api::V1::ExamplesController < Api::V1::BaseController
|
113
119
|
apis_set 'ExamplesController\'s APIs' do
|
114
|
-
schema :Dog => [
|
120
|
+
schema :Dog => [ String, must_be: 'doge' ]
|
115
121
|
query! :QueryCompUuid => [ :product_uuid, String, desc: 'product uuid' ]
|
116
122
|
path! :PathCompId => [ :id, Integer, desc: 'user id' ]
|
117
123
|
resp :RespComp => [ 'bad request', :json ]
|
118
124
|
body! :RqBodyComp => [ :form ]
|
119
125
|
end
|
120
126
|
|
121
|
-
|
127
|
+
api_dry %i[index show], 'common response' do
|
122
128
|
response '567', 'query result export', :pdf, type: File
|
123
129
|
end
|
124
130
|
|
@@ -178,17 +184,17 @@ end
|
|
178
184
|
desc and external_doc_url will be output to the tags[the current tag] (tag defaults to controller_name ), but are optional.
|
179
185
|
the focus is on the block, the DSL methods in the block will generate components.
|
180
186
|
|
181
|
-
- `
|
187
|
+
- `api_dry` [Optional]
|
182
188
|
|
183
189
|
this method is for DRYing.
|
184
190
|
|
185
191
|
```ruby
|
186
192
|
# method signature
|
187
|
-
|
193
|
+
api_dry method = :all, desc = '', &block
|
188
194
|
# usage
|
189
|
-
|
190
|
-
|
191
|
-
|
195
|
+
api_dry :all, 'common response' do; end
|
196
|
+
api_dry :index do; end
|
197
|
+
api_dry [:index, :show] do; end
|
192
198
|
```
|
193
199
|
|
194
200
|
As you think, the DSL methods in the block will be executed to each API that you set by method.
|
@@ -205,7 +211,7 @@ end
|
|
205
211
|
```
|
206
212
|
|
207
213
|
|
208
|
-
#### \>\> DSL methods inside *open_api* and *
|
214
|
+
#### \>\> 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)
|
209
215
|
|
210
216
|
These methods in the block describe the specified API(s): description, valid?,
|
211
217
|
parameters, request body, responses, securities, servers.
|
@@ -237,6 +243,8 @@ parameters, request body, responses, securities, servers.
|
|
237
243
|
|
238
244
|
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
245
|
but that will make it long and ugly. We recommend that unite descriptions in this place.
|
246
|
+
|
247
|
+
In addition, when you want to dry the same parameters (each with a different description), it will be of great use.
|
240
248
|
|
241
249
|
- param family methods (OAS - [Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#parameterObject))
|
242
250
|
- `param`
|
@@ -300,7 +308,21 @@ parameters, request body, responses, securities, servers.
|
|
300
308
|
def form desc = '', schema_hash = { }
|
301
309
|
body :form, desc, schema_hash
|
302
310
|
end
|
311
|
+
# usage
|
312
|
+
form! 'register', data: {
|
313
|
+
name: String,
|
314
|
+
password: String,
|
315
|
+
password_confirmation: String
|
316
|
+
}
|
317
|
+
# advance usage
|
318
|
+
form 'for creating a user', data: {
|
319
|
+
:name! => { type: String, desc: 'user name' },
|
320
|
+
:password! => { type: String, pattern: /[0-9]{6,10}/, desc: 'password' },
|
321
|
+
# optional
|
322
|
+
:remarks => { type: String, desc: 'remarks' },
|
323
|
+
}
|
303
324
|
|
325
|
+
# method implement
|
304
326
|
def file! media_type, desc = '', schema_hash = { type: File }
|
305
327
|
body! media_type, desc, schema_hash
|
306
328
|
end
|
@@ -371,9 +393,16 @@ The DSL methods used to generate the components in this block are:
|
|
371
393
|
schema component_key, type, schema_hash
|
372
394
|
# usage
|
373
395
|
schema :Dog => [ { id!: Integer, name: String }, dft: { id: 1, name: 'pet' } ]
|
396
|
+
# advance usage
|
397
|
+
schema :Dog => [{
|
398
|
+
id!: Integer,
|
399
|
+
name: { type: String, must_be: 'zhandao', desc: 'name' }
|
400
|
+
}, # this is schema type*
|
401
|
+
dft: { id: 1, name: 'pet' }]
|
374
402
|
# or (unrecommended)
|
375
403
|
schema :Dog, { id!: Integer, name: String }, dft: { id: 1, name: 'pet' }
|
376
404
|
```
|
405
|
+
*: see: [Type](https://github.com/zhandao/zero-rails_openapi/blob/master/documentation/parameter.md#type)
|
377
406
|
|
378
407
|
### Generate JSON Documentation File
|
379
408
|
|
@@ -395,9 +424,9 @@ In order to use it, you may have to enable CORS, [see](https://github.com/swagge
|
|
395
424
|
|
396
425
|
### Tricks
|
397
426
|
|
398
|
-
#### Write the DSL Somewhere Else
|
427
|
+
#### Trick1 - Write the DSL Somewhere Else
|
399
428
|
|
400
|
-
Does your documentation take too
|
429
|
+
Does your documentation take too many lines?
|
401
430
|
Do you want to separate documentation from business controller to simplify both?
|
402
431
|
Very easy! Just use `ctrl_path`.
|
403
432
|
|
@@ -425,6 +454,45 @@ end
|
|
425
454
|
|
426
455
|
Notes: convention is the file name ends with `_doc.rb`
|
427
456
|
|
457
|
+
#### Trick2 - DRYing
|
458
|
+
|
459
|
+
To be written.
|
460
|
+
|
461
|
+
You can look at this [file](https://github.com/zhandao/zero-rails_openapi/blob/masterdocumentation/examples/auto_gen_dsl.rb) at the moment.
|
462
|
+
In general is to use method `api_dry`.
|
463
|
+
The implementation of the file is: do `api_dry` when inherits the base controller inside `inherited` method.
|
464
|
+
|
465
|
+
#### Trick3 - Auto Generate Description
|
466
|
+
|
467
|
+
```ruby
|
468
|
+
desc 'api desc',
|
469
|
+
search_type!: 'search field, allows:<br/>'
|
470
|
+
query :search_type, String, enum: %w[name creator category price]
|
471
|
+
|
472
|
+
# or
|
473
|
+
|
474
|
+
query :search_type, String, desc!: 'search field, allows:<br/>',
|
475
|
+
enum: %w[name creator category price]
|
476
|
+
```
|
477
|
+
|
478
|
+
Notice `!` use (`search_type!`, `desc!`), it tells ZRO to append
|
479
|
+
information that analyzed from definitions (enum, must_be ..) to description automatically.
|
480
|
+
|
481
|
+
Any one of above will generate:
|
482
|
+
`search field, allows:<br/>1/ name<br/>2/ creator,<br/>3/ category<br/>4/ price<br/>`
|
483
|
+
|
484
|
+
ZRO also allows you use Hash to define `enum`:
|
485
|
+
```ruby
|
486
|
+
query :view, String, enum: {
|
487
|
+
'all goods (default)': :all,
|
488
|
+
'only online': :online,
|
489
|
+
'only offline': :offline,
|
490
|
+
'expensive goods': :get,
|
491
|
+
'cheap goods': :borrow,
|
492
|
+
}
|
493
|
+
```
|
494
|
+
Read this [file](https://github.com/zhandao/zero-rails_openapi/blob/masterdocumentation/examples/auto_gen_desc.rb) to learn more.
|
495
|
+
|
428
496
|
## Troubleshooting
|
429
497
|
|
430
498
|
- **You wrote document of the current API, but not find in the generated json file?**
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class V1::GoodsDoc < BaseDoc
|
2
|
+
|
3
|
+
open_api :index, 'Get list of Goods.' do
|
4
|
+
desc 'listing Goods',
|
5
|
+
view!: 'search view, allows::<br/>',
|
6
|
+
# '1/ all goods (default):all<br/>' \
|
7
|
+
# '2/ only online:online<br/>' \
|
8
|
+
# '3/ only offline:offline<br/>' \
|
9
|
+
# '4/ expensive goods:expensive<br/>' \
|
10
|
+
# '5/ cheap goods:cheap<br/>',
|
11
|
+
search_type!: 'search field, allows:<br/>'
|
12
|
+
# '1/ name<br/>2/ creator,<br/>3/ category<br/>4/ price<br/>'
|
13
|
+
|
14
|
+
# query :view, String, enum: %w[all online offline get borrow]
|
15
|
+
query :view, String, enum: {
|
16
|
+
'all goods (default)': :all,
|
17
|
+
'only online': :online,
|
18
|
+
'only offline': :offline,
|
19
|
+
'expensive goods': :get,
|
20
|
+
'cheap goods': :borrow,
|
21
|
+
}
|
22
|
+
query :search_type, String, enum: %w[name creator category price]
|
23
|
+
# Same as:
|
24
|
+
# query :search_type, String, desc!: 'search field, allows:<br/>',
|
25
|
+
# enum: %w[name creator category price]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'open_api/generator'
|
2
|
+
|
3
|
+
# Usage: add `include AutoGenDSL` to base controller.
|
4
|
+
module AutoGenDSL
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def inherited(subclass)
|
11
|
+
super
|
12
|
+
subclass.class_eval do
|
13
|
+
break unless self.name.match? /sController|sDoc/
|
14
|
+
ctrl_path "api/#{self.name.sub('Doc', '').downcase.gsub('::', '/')}" if self.name.match? /sDoc/
|
15
|
+
open_api_dry
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def open_api_dry
|
22
|
+
ctrl_path = try(:controller_path) || instance_variable_get('@_ctrl_path')
|
23
|
+
::OpenApi::Generator.get_actions_by_ctrl_path(ctrl_path)&.each do |action|
|
24
|
+
api_dry action do
|
25
|
+
# Token in Header
|
26
|
+
if !action_path.match?(/NoVerificationController/) && !%w[create login].include?(action)
|
27
|
+
header! 'Token', String, desc: 'user token'
|
28
|
+
end
|
29
|
+
|
30
|
+
# Common :index parameters
|
31
|
+
if !action_path.match?(/NotDRYController/) && action == 'index'
|
32
|
+
query :page, Integer, desc: 'page'
|
33
|
+
query :per_page, Integer, desc: 'per'
|
34
|
+
end
|
35
|
+
|
36
|
+
# OAS require at least one response on each api.
|
37
|
+
default_response 'default response', :json
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,13 +1,18 @@
|
|
1
1
|
class Api::V1::ExamplesController < Api::V1::BaseController
|
2
2
|
apis_set 'ExamplesController\'s APIs' do
|
3
|
-
schema :Dog
|
3
|
+
schema :Dog => [{
|
4
|
+
id!: Integer,
|
5
|
+
name: { type: String, must_be: 'zhandao', desc: 'name' }
|
6
|
+
}, # this is schema type, see:
|
7
|
+
dft: { id: 1, name: 'pet' }]
|
8
|
+
# schema :Dog => [ String, dft: { id: 1, name: 'pet' } ]
|
4
9
|
query! :QueryCompUuid => [ :product_uuid, String, desc: 'product uuid' ]
|
5
10
|
path! :PathCompId => [ :id, Integer, desc: 'user id' ]
|
6
11
|
resp :RespComp => [ 'bad request', :json ]
|
7
12
|
body! :RqBodyComp => [ :form ]
|
8
13
|
end
|
9
14
|
|
10
|
-
|
15
|
+
api_dry %i[index show], 'common response' do
|
11
16
|
response '567', 'query result export', :pdf, type: File
|
12
17
|
end
|
13
18
|
|
@@ -21,7 +26,7 @@ class Api::V1::ExamplesController < Api::V1::BaseController
|
|
21
26
|
query! :id, Integer, enum: 0..5, length: [1, 2], pattern: /^[0-9]$/, range: {gt:0, le:5}
|
22
27
|
query! :done, Boolean, must_be: false, default: true, desc: 'must be false'
|
23
28
|
query :email_addr, String, lth: :ge_3, default: email # is_a: :email
|
24
|
-
|
29
|
+
|
25
30
|
file :pdf, 'desc: the media type is application/pdf'
|
26
31
|
|
27
32
|
response :success, 'success response', :json, type: :Dog
|
@@ -33,4 +38,13 @@ class Api::V1::ExamplesController < Api::V1::BaseController
|
|
33
38
|
param_ref :PathCompId, :QueryCompUuid
|
34
39
|
response_ref '123' => :RespComp, '223' => :RespComp
|
35
40
|
end
|
41
|
+
|
42
|
+
open_api :create do
|
43
|
+
form 'for creating a user', data: {
|
44
|
+
:name! => { type: String, desc: 'user name' },
|
45
|
+
:password! => { type: String, pattern: /[0-9]{6,10}/, desc: 'password' },
|
46
|
+
# optional
|
47
|
+
:remarks => { type: String, desc: 'remarks' },
|
48
|
+
}
|
49
|
+
end
|
36
50
|
end
|
@@ -6,7 +6,7 @@ OpenApi.configure do |c|
|
|
6
6
|
|
7
7
|
# Everything about OAS3 is on https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md
|
8
8
|
# Getting started: https://swagger.io/docs/specification/basic-structure/
|
9
|
-
c.
|
9
|
+
c.register_docs = {
|
10
10
|
homepage_api: {
|
11
11
|
# [REQUIRED] ZRO will scan all the descendants of the root_controller, then generate their docs.
|
12
12
|
root_controller: Api::V1::BaseController,
|
@@ -54,11 +54,11 @@ OpenApi.configure do |c|
|
|
54
54
|
# This URL supports Server Variables and MAY be relative,
|
55
55
|
# to indicate that the host location is relative to the location where
|
56
56
|
# the OpenAPI document is being served.
|
57
|
-
url: 'http://localhost:
|
57
|
+
url: 'http://localhost:3000',
|
58
58
|
# An optional string describing the host designated by the URL.
|
59
59
|
description: 'Optional server description, e.g. Main (production) server'
|
60
60
|
},{
|
61
|
-
url: 'http://localhost:
|
61
|
+
url: 'http://localhost:3001',
|
62
62
|
description: 'Optional server description, e.g. Internal staging server for testing'
|
63
63
|
}
|
64
64
|
],
|
data/documentation/parameter.md
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
### More Explanation of Param()
|
2
2
|
|
3
|
-
|
3
|
+
#### param_type
|
4
|
+
OpenAPI 3.0 distinguishes between the following parameter types based on the parameter location:
|
4
5
|
**header, path, query, cookie**. [more](https://swagger.io/docs/specification/describing-parameters/)
|
5
6
|
|
6
|
-
|
7
|
+
#### name
|
8
|
+
parameter name. If param_type is :path, it must correspond to the associated path segment form
|
7
9
|
the routing path, for example: the path is `/good/:id`, then you have to declare a path parameter with name `id`.
|
8
10
|
|
9
|
-
|
11
|
+
#### type
|
12
|
+
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
13
|
In addition, you can use `format` in schema_hash to define in fine detail the data type being used, like:
|
11
14
|
int32, float, date ...
|
12
15
|
All the types you can use are:
|
@@ -27,22 +30,25 @@ All the types you can use are:
|
|
27
30
|
You can use `Object.const_set()` to define a constant that does not exist, but note that
|
28
31
|
the value you set could not be a Symbol (it will be explained as a Ref Object), should be a String.
|
29
32
|
|
30
|
-
|
33
|
+
#### required
|
34
|
+
:opt or :req
|
31
35
|
|
32
|
-
|
36
|
+
#### Schema Hash
|
37
|
+
|
38
|
+
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
39
|
schema_hash(optional) will be used to generate Schema Object inside Parameter Object.
|
34
40
|
[source code](https://github.com/zhandao/zero-rails_openapi/blob/master/lib/oas_objs/schema_obj.rb)
|
35
41
|
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)
|
42
|
+
- **enum (values, allowable_values)**
|
37
43
|
Must be Array or Range(will be converted to Array)
|
38
|
-
-
|
44
|
+
- **must_be (value, allowable_value)**
|
39
45
|
Single value, could be a String, Array ...
|
40
|
-
- range (number_range)
|
46
|
+
- **range (number_range)**
|
41
47
|
Allow value in this continuous range. Set this field like this: `{ gt: 0, le: 5 }`
|
42
|
-
- length (lth)
|
48
|
+
- **length (lth)**
|
43
49
|
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)
|
50
|
+
- **format (fmt)**
|
51
|
+
- **is (is_a)**
|
46
52
|
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
53
|
2. Look at this example: the `format` is set to "int32", but you also want to express that this
|
48
54
|
schema is an "id" format —— this cannot be expressed in the current OAS version.
|
@@ -51,5 +57,5 @@ You can set the schema by following keys (all are optional), the words in parent
|
|
51
57
|
for example the parameter name "user_email" will generate "is: email". Default `is` options are:
|
52
58
|
[email phone password uuid uri url time date], to overwrite it you can set it in initializer `c.is_options = %w[]`.
|
53
59
|
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)
|
60
|
+
- **pattern (regexp, pr, reg)**
|
61
|
+
- **default (dft, default_value)**
|
data/lib/oas_objs/helpers.rb
CHANGED
@@ -7,22 +7,22 @@ module OpenApi
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def value_present
|
10
|
-
|
10
|
+
proc { |_, v| truly_present? v }
|
11
11
|
end
|
12
12
|
|
13
13
|
def assign(value)
|
14
|
-
@assign = value.is_a?(Symbol)?
|
14
|
+
@assign = value.is_a?(Symbol) ? send("_#{value}") : value
|
15
15
|
self
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
18
|
+
def reduceee(*values)
|
19
19
|
@assign = values.compact.reduce({ }, :merge).keep_if &value_present
|
20
20
|
self
|
21
21
|
end
|
22
22
|
|
23
23
|
def to_processed(who)
|
24
24
|
if who.is_a?(Symbol)
|
25
|
-
|
25
|
+
send("#{who}=", @assign)
|
26
26
|
else
|
27
27
|
processed[who.to_sym] = @assign
|
28
28
|
end if truly_present?(@assign)
|
@@ -34,7 +34,7 @@ module OpenApi
|
|
34
34
|
self[who.to_sym] = @assign if truly_present?(@assign)
|
35
35
|
end
|
36
36
|
|
37
|
-
def
|
37
|
+
def then_merge! # to_processed
|
38
38
|
processed.tap { |it| it.merge! @assign if truly_present?(@assign) }
|
39
39
|
end
|
40
40
|
end
|
@@ -7,13 +7,13 @@ module OpenApi
|
|
7
7
|
attr_accessor :media_type, :schema
|
8
8
|
def initialize(media_type, schema_hash)
|
9
9
|
self.media_type = media_type_mapping media_type
|
10
|
-
self.schema = SchemaObj.new(schema_hash[:type], schema_hash)
|
10
|
+
self.schema = SchemaObj.new(schema_hash[:type] || schema_hash[:data], schema_hash)
|
11
11
|
end
|
12
12
|
|
13
13
|
def process
|
14
|
-
schema_processed =
|
14
|
+
schema_processed = schema.process
|
15
15
|
schema = schema_processed.values.join.blank? ? { } : { schema: schema_processed }
|
16
|
-
media_type.nil? ? { } : { media_type =>
|
16
|
+
media_type.nil? ? { } : { media_type => schema }
|
17
17
|
end
|
18
18
|
|
19
19
|
# https://swagger.io/docs/specification/media-types/
|
@@ -23,32 +23,32 @@ module OpenApi
|
|
23
23
|
def media_type_mapping(media_type)
|
24
24
|
return media_type if media_type.is_a? String
|
25
25
|
case media_type
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
26
|
+
when :app then 'application/*'
|
27
|
+
when :json then 'application/json'
|
28
|
+
when :xml then 'application/xml'
|
29
|
+
when :xwww then 'application/x-www-form-urlencoded'
|
30
|
+
when :pdf then 'application/pdf'
|
31
|
+
when :zip then 'application/zip'
|
32
|
+
when :gzip then 'application/gzip'
|
33
|
+
when :doc then 'application/msword'
|
34
|
+
when :docx then 'application/application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
35
|
+
when :xls then 'application/vnd.ms-excel'
|
36
|
+
when :xlsx then 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
37
|
+
when :ppt then 'application/vnd.ms-powerpoint'
|
38
|
+
when :pptx then 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
39
|
+
# when :pdf then 'application/pdf'
|
40
|
+
when :form then 'multipart/form-data'; when :form_data then 'multipart/form-data'
|
41
|
+
when :text then 'text/*'
|
42
|
+
when :plain then 'text/plain then charset=utf-8'
|
43
|
+
when :html then 'text/html'
|
44
|
+
when :csv then 'text/csv'
|
45
|
+
when :image then 'image/*'
|
46
|
+
when :png then 'image/png'
|
47
|
+
when :jpeg then 'image/jpeg'
|
48
|
+
when :gif then 'image/gif'
|
49
|
+
when :audio then 'audio/*'
|
50
|
+
when :video then 'video/*'
|
51
|
+
else nil
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
data/lib/oas_objs/param_obj.rb
CHANGED
@@ -11,15 +11,23 @@ module OpenApi
|
|
11
11
|
self.processed = {
|
12
12
|
name: name,
|
13
13
|
in: param_type,
|
14
|
-
required:
|
14
|
+
required: required.to_s.match?(/req/),
|
15
15
|
}
|
16
16
|
self.schema = SchemaObj.new(type, schema_hash)
|
17
|
-
|
17
|
+
merge! schema_hash
|
18
18
|
end
|
19
19
|
|
20
20
|
def process
|
21
|
-
assign(
|
22
|
-
processed.tap { |it| it[:schema] = schema.process_for self[:name] }
|
21
|
+
assign(desc).to_processed 'description'
|
22
|
+
processed.tap { |it| it[:schema] = schema.process_for self.processed[:name] }
|
23
|
+
end
|
24
|
+
|
25
|
+
def desc
|
26
|
+
if __desc.present?
|
27
|
+
schema.preprocess_with_desc __desc, self[:name]
|
28
|
+
else
|
29
|
+
_desc
|
30
|
+
end
|
23
31
|
end
|
24
32
|
|
25
33
|
|
@@ -27,9 +35,10 @@ module OpenApi
|
|
27
35
|
# This mapping allows user to select the aliases in DSL writing,
|
28
36
|
# without increasing the complexity of the implementation.
|
29
37
|
{ # SELF_MAPPING
|
30
|
-
_range: %i[range number_range],
|
31
|
-
_length: %i[length lth
|
32
|
-
_desc: %i[desc description
|
38
|
+
_range: %i[ range number_range ],
|
39
|
+
_length: %i[ length lth ],
|
40
|
+
_desc: %i[ desc description ],
|
41
|
+
__desc: %i[ desc! description! ],
|
33
42
|
}.each do |key, aliases|
|
34
43
|
define_method key do
|
35
44
|
aliases.each { |alias_name| self[key] ||= self[alias_name] } if self[key].nil?
|
data/lib/oas_objs/ref_obj.rb
CHANGED
@@ -11,7 +11,7 @@ module OpenApi
|
|
11
11
|
attr_accessor :processed, :media_type
|
12
12
|
def initialize(required, media_type, desc, schema_hash)
|
13
13
|
self.media_type = MediaTypeObj.new(media_type, schema_hash)
|
14
|
-
self.processed = { required:
|
14
|
+
self.processed = { required: required.to_s.match?(/req/), description: desc }
|
15
15
|
end
|
16
16
|
|
17
17
|
def process
|
@@ -31,7 +31,7 @@ A request body with a referenced model definition.
|
|
31
31
|
{
|
32
32
|
"description": "user to add to the system",
|
33
33
|
"content": {
|
34
|
-
"
|
34
|
+
"multipart/form-data": {
|
35
35
|
"schema": {
|
36
36
|
"$ref": "#/components/schemas/User"
|
37
37
|
},
|
@@ -9,7 +9,7 @@ module OpenApi
|
|
9
9
|
|
10
10
|
attr_accessor :processed, :code, :media_type
|
11
11
|
def initialize(code, desc, media_type, schema_hash)
|
12
|
-
self.code =
|
12
|
+
self.code = code.to_s
|
13
13
|
self.media_type = MediaTypeObj.new(media_type, schema_hash)
|
14
14
|
self.processed = { description: desc }
|
15
15
|
end
|
data/lib/oas_objs/schema_obj.rb
CHANGED
@@ -18,25 +18,66 @@ module OpenApi
|
|
18
18
|
# However, user can decide how to write --
|
19
19
|
# `type: number, format: double`, or `type: double`
|
20
20
|
self.type = type
|
21
|
-
|
21
|
+
merge! schema_hash
|
22
22
|
end
|
23
23
|
|
24
24
|
|
25
|
-
def process_for(param_name = nil)
|
25
|
+
def process_for(param_name = nil, options = { desc_inside: false })
|
26
|
+
return processed if @preprocessed
|
27
|
+
|
26
28
|
processed.merge! processed_type
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
reduceee processed_enum_and_length,
|
30
|
+
processed_range,
|
31
|
+
processed_is_and_format(param_name),
|
32
|
+
{
|
33
|
+
pattern: _pattern&.inspect&.delete('/'),
|
34
|
+
default: _default,
|
35
|
+
}
|
36
|
+
then_merge!
|
37
|
+
|
38
|
+
assign(processed_desc options).then_merge!
|
33
39
|
end
|
34
40
|
alias_method :process, :process_for
|
35
41
|
|
42
|
+
def preprocess_with_desc desc, param_name = nil
|
43
|
+
self.__desc = desc
|
44
|
+
process_for param_name
|
45
|
+
@preprocessed = true
|
46
|
+
__desc
|
47
|
+
end
|
48
|
+
|
49
|
+
def processed_desc(options)
|
50
|
+
result = __desc ? self.__desc = process_desc : _desc
|
51
|
+
options[:desc_inside] ? { description: result } : nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def process_desc
|
55
|
+
if processed[:enum].present?
|
56
|
+
if @enum_info.present?
|
57
|
+
@enum_info.each_with_index do |(info, value), index|
|
58
|
+
__desc.concat "#{index + 1}/ #{info}: #{value}<br/>"
|
59
|
+
end
|
60
|
+
else
|
61
|
+
processed[:enum].each_with_index do |value, index|
|
62
|
+
__desc.concat "#{index + 1}/ #{value}<br/>"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
__desc
|
67
|
+
end
|
68
|
+
|
36
69
|
def processed_type(type = self.type)
|
37
70
|
t = type.class.in?([Hash, Array, Symbol]) ? type : "#{type}".downcase
|
38
71
|
if t.is_a? Hash
|
39
|
-
|
72
|
+
# For support writing:
|
73
|
+
# form 'desc', data: {
|
74
|
+
# id!: { type: Integer, enum: 0..5, desc: 'user id' }
|
75
|
+
# }
|
76
|
+
if t.key? :type
|
77
|
+
SchemaObj.new(t[:type], t).process_for @prop_name, desc_inside: true
|
78
|
+
else
|
79
|
+
recursive_obj_type t
|
80
|
+
end
|
40
81
|
elsif t.is_a? Array
|
41
82
|
recursive_array_type t
|
42
83
|
elsif t.is_a? Symbol
|
@@ -47,12 +88,14 @@ module OpenApi
|
|
47
88
|
{ type: 'string', format: t}
|
48
89
|
elsif t.eql? 'file'
|
49
90
|
{ type: 'string', format: OpenApi.config.dft_file_format }
|
91
|
+
elsif t.eql? 'datetime'
|
92
|
+
{ type: 'string', format: 'date-time' }
|
50
93
|
else # other string
|
51
94
|
{ type: t }
|
52
95
|
end
|
53
96
|
end
|
54
97
|
def recursive_obj_type(t) # DSL use { prop_name: prop_type } to represent object structure
|
55
|
-
return processed_type(t)
|
98
|
+
return processed_type(t) if !t.is_a?(Hash) || t.key?(:type)
|
56
99
|
|
57
100
|
_schema = {
|
58
101
|
type: 'object',
|
@@ -60,6 +103,7 @@ module OpenApi
|
|
60
103
|
required: [ ]
|
61
104
|
}
|
62
105
|
t.each do |prop_name, prop_type|
|
106
|
+
@prop_name = prop_name
|
63
107
|
_schema[:required] << "#{prop_name}".delete('!') if "#{prop_name}".match? '!'
|
64
108
|
_schema[:properties]["#{prop_name}".delete('!').to_sym] = recursive_obj_type prop_type
|
65
109
|
end
|
@@ -78,6 +122,16 @@ module OpenApi
|
|
78
122
|
end
|
79
123
|
|
80
124
|
def processed_enum_and_length
|
125
|
+
# Support this writing for auto generating desc from enum.
|
126
|
+
# enum: {
|
127
|
+
# 'all_data': :all,
|
128
|
+
# 'one_page': :one
|
129
|
+
# }
|
130
|
+
if _enum.is_a? Hash
|
131
|
+
@enum_info = _enum
|
132
|
+
self._enum = _enum.values
|
133
|
+
end
|
134
|
+
|
81
135
|
%i[_enum _length].each do |key|
|
82
136
|
value = self.send(key)
|
83
137
|
self[key] = value.to_a if value.present? && value.is_a?(Range)
|
@@ -85,7 +139,7 @@ module OpenApi
|
|
85
139
|
|
86
140
|
# generate_enums_by_enum_array
|
87
141
|
values = _enum || _value
|
88
|
-
self._enum = (values
|
142
|
+
self._enum = Array(values) if truly_present?(values)
|
89
143
|
|
90
144
|
# generate length range fields by _lth array
|
91
145
|
lth = _length || [ ]
|
@@ -124,21 +178,23 @@ module OpenApi
|
|
124
178
|
def recognize_is_options_in(name)
|
125
179
|
# identify whether `is` patterns matched the name, if so, generate `is`.
|
126
180
|
OpenApi.config.is_options.each do |pattern|
|
127
|
-
self._is = pattern or break if
|
181
|
+
self._is = pattern or break if name.match? /#{pattern}/
|
128
182
|
end if _is.nil?
|
129
183
|
self.delete :_is if _is.in?([:x, :we])
|
130
184
|
end
|
131
185
|
|
132
186
|
|
133
187
|
{ # SELF_MAPPING
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
188
|
+
_enum: %i[ enum values allowable_values ],
|
189
|
+
_value: %i[ must_be value allowable_value ],
|
190
|
+
_range: %i[ range number_range ],
|
191
|
+
_length: %i[ length lth ],
|
192
|
+
_is: %i[ is_a is ], # NOT OAS Spec, just an addition
|
193
|
+
_format: %i[ format fmt ],
|
194
|
+
_pattern: %i[ pattern regexp pr reg ],
|
195
|
+
_default: %i[ default dft default_value ],
|
196
|
+
_desc: %i[ desc description ],
|
197
|
+
__desc: %i[ desc! description! ],
|
142
198
|
}.each do |key, aliases|
|
143
199
|
define_method key do
|
144
200
|
aliases.each do |alias_name|
|
data/lib/open_api/config.rb
CHANGED
@@ -7,7 +7,7 @@ module OpenApi
|
|
7
7
|
DEFAULT_CONFIG = {
|
8
8
|
is_options: %w[email phone password uuid uri url time date],
|
9
9
|
dft_file_format: 'binary'
|
10
|
-
}
|
10
|
+
}.freeze
|
11
11
|
|
12
12
|
module ClassMethods
|
13
13
|
def config
|
@@ -19,15 +19,15 @@ module OpenApi
|
|
19
19
|
end
|
20
20
|
|
21
21
|
### config options
|
22
|
-
#
|
23
|
-
#
|
22
|
+
# register_docs = {
|
23
|
+
# doc_name: {
|
24
24
|
# :file_output_path, :root_controller
|
25
25
|
# info: {}
|
26
26
|
# }}
|
27
27
|
# is_options = %w[]
|
28
28
|
|
29
29
|
def apis
|
30
|
-
@apis ||= @config.
|
30
|
+
@apis ||= @config.register_docs
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
data/lib/open_api/dsl.rb
CHANGED
@@ -29,24 +29,29 @@ module OpenApi
|
|
29
29
|
|
30
30
|
# select the routing info (corresponding to the current method) from the routing list.
|
31
31
|
action_path = "#{@_ctrl_path ||= controller_path}##{method}"
|
32
|
-
routes_info = ctrl_routes_list
|
32
|
+
routes_info = ctrl_routes_list&.select { |api| api[:action_path].match? /^#{action_path}$/ }&.first
|
33
33
|
puts "[zero-rails_openapi] Routing mapping failed: #{@_ctrl_path}##{method}" or return if routes_info.nil?
|
34
34
|
|
35
35
|
# structural { path: { http_method:{ } } }, for Paths Object.
|
36
36
|
path = (@_api_infos ||= { })[routes_info[:path]] ||= { }
|
37
37
|
current_api = path[routes_info[:http_verb]] =
|
38
|
-
ApiInfoObj.new(action_path)
|
38
|
+
ApiInfoObj.new(action_path)
|
39
|
+
.merge! description: '', summary: summary, operationId: method, tags: [@_apis_tag],
|
40
|
+
parameters: [ ], requestBody: '', responses: { },
|
41
|
+
security: [ ], servers: [ ]
|
39
42
|
|
40
43
|
current_api.tap do |it|
|
41
|
-
it.instance_eval &block if block_given?
|
42
44
|
[method, :all].each do |key| # blocks_store_key
|
43
45
|
@_apis_blocks&.[](key)&.each { |blk| it.instance_eval &blk }
|
44
46
|
end
|
47
|
+
it.instance_eval &block if block_given?
|
48
|
+
it.instance_eval { process_params }
|
49
|
+
it.delete_if { |_, v| v.blank? }
|
45
50
|
end
|
46
51
|
end
|
47
52
|
|
48
53
|
# For DRY; method could be symbol array
|
49
|
-
def
|
54
|
+
def api_dry method = :all, desc = '', &block
|
50
55
|
@_apis_blocks ||= { }
|
51
56
|
if method.is_a? Array
|
52
57
|
method.each { |m| (@_apis_blocks[m.to_sym] ||= [ ]) << block }
|
@@ -8,7 +8,7 @@ module OpenApi
|
|
8
8
|
module DSL
|
9
9
|
module CommonDSL
|
10
10
|
def arrow_writing_support
|
11
|
-
|
11
|
+
proc do |args, executor|
|
12
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
|
@@ -45,9 +45,9 @@ module OpenApi
|
|
45
45
|
end
|
46
46
|
|
47
47
|
{ # alias_methods mapping
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
@@ -61,7 +61,7 @@ module OpenApi
|
|
61
61
|
class CtrlInfoObj < Hash
|
62
62
|
include DSL::CommonDSL
|
63
63
|
|
64
|
-
def _schema component_key, type, schema_hash
|
64
|
+
def _schema component_key, type, schema_hash = { }
|
65
65
|
(self[:schemas] ||= { }).merge! component_key => SchemaObj.new(type, schema_hash).process
|
66
66
|
end
|
67
67
|
def schema *args
|
@@ -115,12 +115,42 @@ module OpenApi
|
|
115
115
|
|
116
116
|
def desc desc, inputs_descs = { }
|
117
117
|
@inputs_descs = inputs_descs
|
118
|
+
merge_desc_for_dryed_param
|
118
119
|
self[:description] = desc
|
119
120
|
end
|
120
121
|
|
122
|
+
# TODO: HACK
|
123
|
+
def merge_desc_for_dryed_param
|
124
|
+
@inputs_descs.each do |param_name, desc|
|
125
|
+
self[:parameters].each do |param|
|
126
|
+
if param_name.match?(?!) && param.processed[:name].to_s == param_name.to_s.delete(?!)
|
127
|
+
param.merge!(desc!: desc).process
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
121
133
|
def param param_type, name, type, required, schema_hash = { }
|
122
|
-
|
123
|
-
|
134
|
+
if @inputs_descs&.[](name).present?
|
135
|
+
schema_hash[:desc] = @inputs_descs[name]
|
136
|
+
elsif @inputs_descs&.[]("#{name}!".to_sym).present?
|
137
|
+
schema_hash[:desc!] = @inputs_descs["#{name}!".to_sym]
|
138
|
+
end
|
139
|
+
|
140
|
+
param_obj = ParamObj.new(name, param_type, type, required, schema_hash)
|
141
|
+
# The definition of the same name parameter will be overwritten
|
142
|
+
index = self[:parameters].map { |p_obj| p_obj.processed[:name] }.index name
|
143
|
+
if index.present?
|
144
|
+
self[:parameters][index] = param_obj
|
145
|
+
else
|
146
|
+
self[:parameters] << param_obj
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def process_params
|
151
|
+
self[:parameters].each_with_index do |param_obj, index|
|
152
|
+
self[:parameters][index] = param_obj.process
|
153
|
+
end
|
124
154
|
end
|
125
155
|
|
126
156
|
def _param_agent name, type, schema_hash = { }
|
@@ -128,7 +158,7 @@ module OpenApi
|
|
128
158
|
end
|
129
159
|
|
130
160
|
def param_ref component_key, *keys
|
131
|
-
|
161
|
+
self[:parameters].concat [component_key].concat(keys).map { |key| RefObj.new(:parameter, key).process }
|
132
162
|
end
|
133
163
|
|
134
164
|
def request_body required, media_type, desc = '', schema_hash = { }
|
@@ -140,16 +170,16 @@ module OpenApi
|
|
140
170
|
end
|
141
171
|
|
142
172
|
def body_ref component_key
|
143
|
-
self[:requestBody] = RefObj.new(:
|
173
|
+
self[:requestBody] = RefObj.new(:requestBody, component_key).process
|
144
174
|
end
|
145
175
|
|
146
176
|
def response_ref code_compkey_hash
|
147
177
|
code_compkey_hash.each do |code, component_key|
|
148
|
-
|
178
|
+
self[:responses].merge! code => RefObj.new(:response, component_key).process
|
149
179
|
end
|
150
180
|
end
|
151
181
|
|
152
|
-
#
|
182
|
+
# TODO: 目前只能写一句 request body,包括 form 和 file, 需要同时支持一下扁平化
|
153
183
|
def form desc = '', schema_hash = { }
|
154
184
|
body :form, desc, schema_hash
|
155
185
|
end
|
@@ -164,11 +194,11 @@ module OpenApi
|
|
164
194
|
end
|
165
195
|
|
166
196
|
def security scheme_name, requirements = [ ]
|
167
|
-
|
197
|
+
self[:security] << { scheme_name => requirements }
|
168
198
|
end
|
169
199
|
|
170
200
|
def server url, desc
|
171
|
-
|
201
|
+
self[:servers] << { url: url, description: desc }
|
172
202
|
end
|
173
203
|
end # ----------------------------------------- end of ApiInfoObj
|
174
204
|
end
|
data/lib/open_api/generator.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_support/hash_with_indifferent_access'
|
2
|
-
|
3
1
|
module OpenApi
|
4
2
|
module Generator
|
5
3
|
def self.included(base)
|
@@ -9,33 +7,35 @@ module OpenApi
|
|
9
7
|
module ClassMethods
|
10
8
|
def generate_docs(api_name = nil)
|
11
9
|
Dir['./app/controllers/**/*.rb'].each { |file| require file }
|
10
|
+
# TODO: _doc should be configured
|
12
11
|
Dir['./app/**/*_doc.rb'].each { |file| require file }
|
13
12
|
if api_name.present?
|
14
13
|
[{ api_name => generate_doc(api_name) }]
|
15
14
|
else
|
16
|
-
OpenApi.apis.keys.map { |api_key| { api_key => generate_doc(api_key)} }.reduce({ }, :merge)
|
15
|
+
OpenApi.apis.keys.map { |api_key| { api_key => generate_doc(api_key) } }.reduce({ }, :merge)
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
20
19
|
def generate_doc(api_name)
|
21
20
|
settings = OpenApi.apis[api_name]
|
22
|
-
doc = { openapi: '3.0.0' }.merge(settings.slice :info, :servers).merge(
|
21
|
+
doc = { openapi: '3.0.0' }.merge(settings.slice :info, :servers).merge(
|
23
22
|
security: settings[:global_security], tags: [ ], paths: { },
|
24
23
|
components: {
|
25
24
|
securitySchemes: settings[:global_security_schemes],
|
26
25
|
schemas: { }
|
27
26
|
}
|
28
|
-
|
27
|
+
)
|
29
28
|
|
30
29
|
settings[:root_controller].descendants.each do |ctrl|
|
31
30
|
ctrl_infos = ctrl.instance_variable_get('@_ctrl_infos')
|
32
31
|
next if ctrl_infos.nil?
|
33
|
-
doc[:paths].merge! ctrl.instance_variable_get('@_api_infos')
|
32
|
+
doc[:paths].merge! ctrl.instance_variable_get('@_api_infos') || { }
|
34
33
|
doc[:tags] << ctrl_infos[:tag]
|
35
|
-
doc[:components].merge! ctrl_infos[:components]
|
34
|
+
doc[:components].merge! ctrl_infos[:components] || { }
|
36
35
|
end
|
37
|
-
doc[:components].delete_if { |_,v| v.blank? }
|
38
|
-
($open_apis ||= { })[api_name] ||=
|
36
|
+
doc[:components].delete_if { |_, v| v.blank? }
|
37
|
+
($open_apis ||= { })[api_name] ||=
|
38
|
+
ActiveSupport::HashWithIndifferentAccess.new(doc.delete_if { |_, v| v.blank? })
|
39
39
|
end
|
40
40
|
|
41
41
|
def write_docs
|
@@ -64,13 +64,13 @@ module OpenApi
|
|
64
64
|
item.match?(/:/) ? "{#{item[1..-1]}}" : item
|
65
65
|
end.join('/'), # => "/api/v1/examples/{id}"
|
66
66
|
action_path: infos[2] # => "api/v1/examples#index"
|
67
|
-
}
|
68
|
-
end.group_by {|api| api[:action_path].split('#').first } # => { "api/v1/examples" => [..] }, group by paths
|
67
|
+
} rescue next
|
68
|
+
end.compact.group_by {|api| api[:action_path].split('#').first } # => { "api/v1/examples" => [..] }, group by paths
|
69
69
|
end
|
70
70
|
|
71
71
|
def self.get_actions_by_ctrl_path(path)
|
72
72
|
@routes_list ||= generate_routes_list
|
73
|
-
@routes_list[path]
|
73
|
+
@routes_list[path]&.map do |action_info|
|
74
74
|
action_info[:action_path].split('#').last
|
75
75
|
end
|
76
76
|
end
|
data/lib/open_api/version.rb
CHANGED
data/zero-rails_openapi.gemspec
CHANGED
@@ -9,9 +9,10 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["zhandao"]
|
10
10
|
spec.email = ["x@skippingcat.com"]
|
11
11
|
|
12
|
-
spec.summary = %q{Generate the OpenAPI Specification
|
13
|
-
spec.description = %q{Provide concise DSL for
|
14
|
-
JSON file for Rails application,
|
12
|
+
spec.summary = %q{Generate the OpenAPI Specification 3 documentation for Rails application.}
|
13
|
+
spec.description = %q{Provide concise DSL for generating the OpenAPI Specification 3 (OAS3)
|
14
|
+
documentation JSON file for Rails application,
|
15
|
+
then you can use Swagger-UI 3.2.0+ to show the documentation.}
|
15
16
|
spec.homepage = "https://github.com/zhandao/zero-rails_openapi"
|
16
17
|
spec.license = "MIT"
|
17
18
|
|
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.2.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-
|
11
|
+
date: 2017-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -53,8 +53,9 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
55
|
description: |-
|
56
|
-
Provide concise DSL for
|
57
|
-
JSON file for Rails application,
|
56
|
+
Provide concise DSL for generating the OpenAPI Specification 3 (OAS3)
|
57
|
+
documentation JSON file for Rails application,
|
58
|
+
then you can use Swagger-UI 3.2.0+ to show the documentation.
|
58
59
|
email:
|
59
60
|
- x@skippingcat.com
|
60
61
|
executables: []
|
@@ -73,6 +74,8 @@ files:
|
|
73
74
|
- Rakefile
|
74
75
|
- bin/console
|
75
76
|
- bin/setup
|
77
|
+
- documentation/examples/auto_gen_desc.rb
|
78
|
+
- documentation/examples/auto_gen_dsl.rb
|
76
79
|
- documentation/examples/examples_controller.rb
|
77
80
|
- documentation/examples/open_api.rb
|
78
81
|
- documentation/parameter.md
|
@@ -89,7 +92,6 @@ files:
|
|
89
92
|
- lib/open_api/dsl_inside_block.rb
|
90
93
|
- lib/open_api/generator.rb
|
91
94
|
- lib/open_api/version.rb
|
92
|
-
- lib/takes/open_api.rake
|
93
95
|
- zero-rails_openapi.gemspec
|
94
96
|
homepage: https://github.com/zhandao/zero-rails_openapi
|
95
97
|
licenses:
|
@@ -111,8 +113,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
113
|
version: '0'
|
112
114
|
requirements: []
|
113
115
|
rubyforge_project:
|
114
|
-
rubygems_version: 2.6.
|
116
|
+
rubygems_version: 2.6.13
|
115
117
|
signing_key:
|
116
118
|
specification_version: 4
|
117
|
-
summary: Generate the OpenAPI Specification
|
119
|
+
summary: Generate the OpenAPI Specification 3 documentation for Rails application.
|
118
120
|
test_files: []
|