zero-rails_openapi 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/zero-rails_openapi.svg)](https://badge.fury.io/rb/zero-rails_openapi)
|
4
|
+
[![Build Status](https://travis-ci.org/zhandao/zero-rails_openapi.svg?branch=master)](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: []
|