zero-rails_openapi 1.4.0 → 1.4.1
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/CHANGELOG.md +74 -0
- data/Gemfile.lock +8 -10
- data/README.md +48 -34
- data/README_zh.md +747 -1
- data/documentation/examples/auto_gen_doc.rb +1 -1
- data/documentation/examples/examples_controller.rb +14 -0
- data/documentation/examples/goods_doc.rb +5 -6
- data/documentation/examples/open_api.rb +1 -52
- data/lib/oas_objs/helpers.rb +0 -1
- data/lib/oas_objs/param_obj.rb +2 -5
- data/lib/oas_objs/schema_obj.rb +38 -89
- data/lib/oas_objs/schema_obj_helpers.rb +68 -0
- data/lib/open_api/config.rb +8 -0
- data/lib/open_api/config_dsl.rb +4 -4
- data/lib/open_api/dsl.rb +4 -4
- data/lib/open_api/dsl/api_info_obj.rb +1 -1
- data/lib/open_api/dsl/{ctrl_info_obj.rb → components.rb} +3 -2
- data/lib/open_api/dsl/helpers.rb +22 -18
- data/lib/open_api/generator.rb +4 -21
- data/lib/open_api/version.rb +1 -1
- data/zero-rails_openapi.gemspec +4 -4
- metadata +9 -9
@@ -10,7 +10,7 @@ module AutoGenDoc
|
|
10
10
|
def inherited(subclass)
|
11
11
|
super
|
12
12
|
subclass.class_eval do
|
13
|
-
break unless self.name.match?
|
13
|
+
break unless self.name.match?(/sController|sDoc/)
|
14
14
|
ctrl_path self.name.sub('Doc', '').downcase.gsub('::', '/') if self.name.match?(/sDoc/)
|
15
15
|
open_api_dry
|
16
16
|
end
|
@@ -3,6 +3,7 @@ class Api::V1::ExamplesController < Api::V1::BaseController
|
|
3
3
|
|
4
4
|
components do
|
5
5
|
schema :DogSchema => [ String, dft: 'doge' ]
|
6
|
+
schema :PetSchema => [ not: [ Integer, Boolean ] ]
|
6
7
|
query! :UidQuery => [ :uid, String, desc: 'user uid' ]
|
7
8
|
path! :IdPath => [ :id, Integer, desc: 'product id' ]
|
8
9
|
resp :BadRqResp => [ 'bad request', :json ]
|
@@ -27,7 +28,20 @@ class Api::V1::ExamplesController < Api::V1::BaseController
|
|
27
28
|
query :email, String, lth: :ge_3, default: email # is_a: :email
|
28
29
|
file :pdf, 'upload a file: the media type should be application/pdf'
|
29
30
|
|
31
|
+
query :test_type, type: String
|
32
|
+
query :combination, one_of: [ :DogSchema, String, { type: Integer, desc: 'integer input'}]
|
33
|
+
form '', data: {
|
34
|
+
:combination => { any_of: [ Integer, String ] }
|
35
|
+
}
|
36
|
+
|
30
37
|
response :success, 'success response', :json, type: :DogSchema
|
38
|
+
merge_to_resp 200, by: {
|
39
|
+
data: {
|
40
|
+
type: [
|
41
|
+
String
|
42
|
+
]
|
43
|
+
}
|
44
|
+
}
|
31
45
|
|
32
46
|
security :ApiKeyAuth
|
33
47
|
end
|
@@ -1,9 +1,8 @@
|
|
1
1
|
class V2::GoodsDoc < BaseDoc
|
2
2
|
SCHEMA_DRY = { a: 1, b: 2 }
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
# skip: [ 'Token' ] do # you can also skip parameters
|
4
|
+
# skip: [ 'Token' ] do # you can also skip parameters
|
5
|
+
api :index, 'GET list of goods.', use: [ 'Token', :page, :rows ] do # use parameters write in AutoGenDoc#api_dry
|
7
6
|
desc 'listing goods',
|
8
7
|
view!: 'search view, allows:<br/>',
|
9
8
|
search_type!: 'search field, allows:<br/>'
|
@@ -28,7 +27,7 @@ class V2::GoodsDoc < BaseDoc
|
|
28
27
|
end
|
29
28
|
|
30
29
|
|
31
|
-
api :create, 'POST create a good',
|
30
|
+
api :create, 'POST create a good', use: 'Token' do
|
32
31
|
form! 'for creating a good', data: {
|
33
32
|
:name! => { type: String, desc: 'good\'s name' },
|
34
33
|
:category_id! => { type: Integer, desc: 'sub_category\'s id', npmt: true, range: { ge: 1 }, as: :cate },
|
@@ -46,8 +45,8 @@ class V2::GoodsDoc < BaseDoc
|
|
46
45
|
end
|
47
46
|
|
48
47
|
|
49
|
-
api :show, 'GET the specified Good.',
|
48
|
+
api :show, 'GET the specified Good.', use: [ 'Token', :id ]
|
50
49
|
|
51
50
|
|
52
|
-
api :destroy, 'DELETE the specified Good.',
|
51
|
+
api :destroy, 'DELETE the specified Good.', use: [ 'Token', :id ]
|
53
52
|
end
|
@@ -80,7 +80,7 @@ OpenApi::Config.tap do |c|
|
|
80
80
|
# The securitySchemes and security keywords are used to describe the authentication methods used in your API.
|
81
81
|
# https://swagger.io/docs/specification/authentication/
|
82
82
|
# Security Scheme Object: An object to hold reusable Security Scheme Objects.
|
83
|
-
|
83
|
+
securitySchemes: {
|
84
84
|
ApiKeyAuth: { type: 'apiKey', name: 'server_token', in: 'query' },
|
85
85
|
Token: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }
|
86
86
|
},
|
@@ -94,57 +94,6 @@ OpenApi::Config.tap do |c|
|
|
94
94
|
}
|
95
95
|
}
|
96
96
|
|
97
|
-
c.generate_jbuilder_file = true
|
98
|
-
c.overwrite_jbuilder_file = false
|
99
|
-
c.jbuilder_templates = {
|
100
|
-
index: (
|
101
|
-
<<~FILE
|
102
|
-
# *** Generated by ZRO [ please make sure that you have checked this file ] ***
|
103
|
-
json.partial! 'api/base', total: @data.size
|
104
|
-
|
105
|
-
json.data do
|
106
|
-
# @data = @data.page(@page).per(@rows) if @page || @rows
|
107
|
-
# json.array! @data do |datum|
|
108
|
-
json.array! @data.page(@page).per(@rows) do |datum|
|
109
|
-
json.(datum, *datum.show_attrs) if datum.present?
|
110
|
-
end
|
111
|
-
end
|
112
|
-
FILE
|
113
|
-
),
|
114
|
-
|
115
|
-
show: (
|
116
|
-
<<~FILE
|
117
|
-
# *** Generated by ZRO [ please make sure that you have checked this file ] ***
|
118
|
-
json.partial! 'api/base', total: 1
|
119
|
-
|
120
|
-
json.data do
|
121
|
-
json.array! [ @data ] do |datum|
|
122
|
-
json.(datum, *datum.show_attrs) if datum.present?
|
123
|
-
end
|
124
|
-
end
|
125
|
-
FILE
|
126
|
-
),
|
127
|
-
|
128
|
-
success: (
|
129
|
-
<<~FILE
|
130
|
-
# *** Generated by ZRO [ please make sure that you have checked this file ] ***
|
131
|
-
json.partial! 'api/success'
|
132
|
-
FILE
|
133
|
-
),
|
134
|
-
|
135
|
-
success_or_not: (
|
136
|
-
<<~FILE
|
137
|
-
# *** Generated by ZRO [ please make sure that you have checked this file ] ***
|
138
|
-
unless @status
|
139
|
-
# @_code, @_msg = @error_info.present? ? @error_info : ApiError.action_failed.info
|
140
|
-
end
|
141
|
-
|
142
|
-
json.partial! 'api/base', total: 0
|
143
|
-
json.data ''
|
144
|
-
FILE
|
145
|
-
),
|
146
|
-
}
|
147
|
-
|
148
97
|
end
|
149
98
|
|
150
99
|
Object.const_set('Boolean', 'boolean') # Support `Boolean` writing in DSL
|
data/lib/oas_objs/helpers.rb
CHANGED
data/lib/oas_objs/param_obj.rb
CHANGED
data/lib/oas_objs/schema_obj.rb
CHANGED
@@ -2,11 +2,13 @@ require 'oas_objs/helpers'
|
|
2
2
|
require 'open_api/config'
|
3
3
|
require 'oas_objs/ref_obj'
|
4
4
|
require 'oas_objs/example_obj'
|
5
|
+
require 'oas_objs/schema_obj_helpers'
|
5
6
|
|
6
7
|
module OpenApi
|
7
8
|
module DSL
|
8
9
|
# https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#schemaObject
|
9
10
|
class SchemaObj < Hash
|
11
|
+
include SchemaObjHelpers
|
10
12
|
include Helpers
|
11
13
|
|
12
14
|
attr_accessor :processed, :type
|
@@ -28,19 +30,20 @@ module OpenApi
|
|
28
30
|
return processed if @preprocessed
|
29
31
|
|
30
32
|
processed.merge! processed_type
|
31
|
-
reducx
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
33
|
+
reducx(
|
34
|
+
processed_enum_and_length,
|
35
|
+
processed_range,
|
36
|
+
processed_is_and_format(param_name),
|
37
|
+
{
|
38
|
+
pattern: _pattern&.inspect&.delete('/'),
|
39
|
+
default: _default.nil? ? nil : '_default',
|
40
|
+
examples: self[:examples].present? ? ExampleObj.new(self[:examples], self[:exp_by]).process : nil
|
41
|
+
},
|
42
|
+
{ as: _as, permit: _permit, not_permit: _npermit, req_if: _req_if, opt_if: _opt_if }
|
43
|
+
).then_merge!
|
41
44
|
processed[:default] = _default unless _default.nil?
|
42
45
|
|
43
|
-
reducx(processed_desc
|
46
|
+
reducx(processed_desc(options)).then_merge!
|
44
47
|
end
|
45
48
|
|
46
49
|
alias process process_for
|
@@ -53,44 +56,16 @@ module OpenApi
|
|
53
56
|
end
|
54
57
|
|
55
58
|
def processed_desc(options)
|
56
|
-
result = __desc ? self.__desc =
|
59
|
+
result = __desc ? self.__desc = auto_generate_desc : _desc
|
57
60
|
options[:desc_inside] ? { description: result } : nil
|
58
61
|
end
|
59
62
|
|
60
|
-
# TODO: more info
|
61
|
-
# TODO: desc configure
|
62
|
-
def process_desc
|
63
|
-
if processed[:enum].present?
|
64
|
-
if @enum_info.present?
|
65
|
-
@enum_info.each_with_index do |(info, value), index|
|
66
|
-
__desc.concat "<br/>#{index + 1}/ #{info}: #{value}"
|
67
|
-
end
|
68
|
-
else
|
69
|
-
processed[:enum].each_with_index do |value, index|
|
70
|
-
__desc.concat "<br/>#{index + 1}/ #{value}"
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
__desc
|
75
|
-
end
|
76
|
-
|
77
63
|
def processed_type(type = self.type)
|
78
64
|
t = type.class.in?([Hash, Array, Symbol]) ? type : type.to_s.downcase
|
79
65
|
if t.is_a? Hash
|
80
|
-
|
81
|
-
# form 'desc', data: {
|
82
|
-
# id!: { type: Integer, enum: 0..5, desc: 'user id' }
|
83
|
-
# }
|
84
|
-
if t.key?(:type)
|
85
|
-
SchemaObj.new(t[:type], t).process_for(@prop_name, desc_inside: true)
|
86
|
-
# For supporting combined schema in nested schema.
|
87
|
-
elsif (t.keys & %i[ one_of any_of all_of not ]).present?
|
88
|
-
CombinedSchema.new(t).process_for(@prop_name, desc_inside: true)
|
89
|
-
else
|
90
|
-
recursive_obj_type t
|
91
|
-
end
|
66
|
+
processed_hash_type(t)
|
92
67
|
elsif t.is_a? Array
|
93
|
-
recursive_array_type
|
68
|
+
recursive_array_type(t)
|
94
69
|
elsif t.is_a? Symbol
|
95
70
|
RefObj.new(:schema, t).process
|
96
71
|
elsif t.in? %w[float double int32 int64] # to README: 这些值应该传 string 进来, symbol 只允许 $ref
|
@@ -106,53 +81,24 @@ module OpenApi
|
|
106
81
|
end
|
107
82
|
end
|
108
83
|
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
t.
|
118
|
-
@prop_name
|
119
|
-
_schema[:required] << "#{prop_name}".delete('!') if prop_name['!']
|
120
|
-
_schema[:properties]["#{prop_name}".delete('!').to_sym] = recursive_obj_type prop_type
|
121
|
-
end
|
122
|
-
_schema.keep_if(&value_present)
|
123
|
-
end
|
124
|
-
|
125
|
-
def recursive_array_type(t)
|
126
|
-
if t.is_a? Array
|
127
|
-
{
|
128
|
-
type: 'array',
|
129
|
-
# TODO: [[String], [Integer]] <= One Of? Object?(0=>[S], 1=>[I])
|
130
|
-
items: recursive_array_type(t.first)
|
131
|
-
}
|
84
|
+
def processed_hash_type(t)
|
85
|
+
# For supporting this:
|
86
|
+
# form 'desc', data: {
|
87
|
+
# id!: { type: Integer, enum: 0..5, desc: 'user id' }
|
88
|
+
# }
|
89
|
+
if t.key?(:type)
|
90
|
+
SchemaObj.new(t[:type], t).process_for(@prop_name, desc_inside: true)
|
91
|
+
# For supporting combined schema in nested schema.
|
92
|
+
elsif (t.keys & %i[ one_of any_of all_of not ]).present?
|
93
|
+
CombinedSchema.new(t).process_for(@prop_name, desc_inside: true)
|
132
94
|
else
|
133
|
-
|
95
|
+
recursive_obj_type(t)
|
134
96
|
end
|
135
97
|
end
|
136
98
|
|
137
99
|
def processed_enum_and_length
|
138
|
-
|
139
|
-
|
140
|
-
# 'all_data': :all,
|
141
|
-
# 'one_page': :one
|
142
|
-
# }
|
143
|
-
if _enum.is_a? Hash
|
144
|
-
@enum_info = _enum
|
145
|
-
self._enum = _enum.values
|
146
|
-
end
|
147
|
-
|
148
|
-
%i[_enum _length].each do |key|
|
149
|
-
value = self.send(key)
|
150
|
-
self[key] = value.to_a if value.present? && value.is_a?(Range)
|
151
|
-
end
|
152
|
-
|
153
|
-
# generate_enums_by_enum_array
|
154
|
-
values = _enum || _value
|
155
|
-
self._enum = Array(values) if truly_present?(values)
|
100
|
+
process_enum_info
|
101
|
+
process_enum_lth_range
|
156
102
|
|
157
103
|
# generate length range fields by _lth array
|
158
104
|
lth = _length || [ ]
|
@@ -180,16 +126,17 @@ module OpenApi
|
|
180
126
|
recognize_is_options_in name
|
181
127
|
{ }.tap do |it|
|
182
128
|
# `format` that generated in process_type() may be overwrote here.
|
183
|
-
it
|
184
|
-
it
|
129
|
+
it[:format] = _format || _is if processed[:format].blank? || _format.present?
|
130
|
+
it[:is] = _is
|
185
131
|
end
|
186
132
|
end
|
187
133
|
|
188
134
|
def recognize_is_options_in(name)
|
135
|
+
return unless _is.nil?
|
189
136
|
# identify whether `is` patterns matched the name, if so, generate `is`.
|
190
137
|
Config.is_options.each do |pattern|
|
191
|
-
self._is = pattern or break if name.match?(/#{pattern}/)
|
192
|
-
end
|
138
|
+
(self._is = pattern) or break if name.match?(/#{pattern}/)
|
139
|
+
end
|
193
140
|
end
|
194
141
|
|
195
142
|
|
@@ -211,10 +158,12 @@ module OpenApi
|
|
211
158
|
_opt_if: %i[ opt_if opt_when ], # NOT OAS Spec, it's for zero-params_processor
|
212
159
|
}.each do |key, aliases|
|
213
160
|
define_method key do
|
161
|
+
return self[key] unless self[key].nil?
|
162
|
+
|
214
163
|
aliases.each do |alias_name|
|
215
164
|
break if self[key] == false
|
216
165
|
self[key] ||= self[alias_name]
|
217
|
-
end
|
166
|
+
end
|
218
167
|
self[key]
|
219
168
|
end
|
220
169
|
define_method "#{key}=" do |value| self[key] = value end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module OpenApi
|
2
|
+
module DSL
|
3
|
+
module SchemaObjHelpers
|
4
|
+
# TODO: more info and desc configure
|
5
|
+
def auto_generate_desc
|
6
|
+
if processed[:enum].present?
|
7
|
+
if @enum_info.present?
|
8
|
+
@enum_info.each_with_index do |(info, value), index|
|
9
|
+
__desc.concat "<br/>#{index + 1}/ #{info}: #{value}"
|
10
|
+
end
|
11
|
+
else
|
12
|
+
processed[:enum].each_with_index do |value, index|
|
13
|
+
__desc.concat "<br/>#{index + 1}/ #{value}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
__desc
|
18
|
+
end
|
19
|
+
|
20
|
+
def recursive_obj_type(t) # ZRO use { prop_name: prop_type } to represent object structure
|
21
|
+
return processed_type(t) if !t.is_a?(Hash) || (t.keys & %i[ type one_of any_of all_of not ]).present?
|
22
|
+
|
23
|
+
_schema = {
|
24
|
+
type: 'object',
|
25
|
+
properties: { },
|
26
|
+
required: [ ]
|
27
|
+
}
|
28
|
+
t.each do |prop_name, prop_type|
|
29
|
+
@prop_name = prop_name
|
30
|
+
_schema[:required] << "#{prop_name}".delete('!') if prop_name['!']
|
31
|
+
_schema[:properties]["#{prop_name}".delete('!').to_sym] = recursive_obj_type prop_type
|
32
|
+
end
|
33
|
+
_schema.keep_if(&value_present)
|
34
|
+
end
|
35
|
+
|
36
|
+
def recursive_array_type(t)
|
37
|
+
return processed_type(t) unless t.is_a? Array
|
38
|
+
|
39
|
+
{
|
40
|
+
type: 'array',
|
41
|
+
# TODO: [[String], [Integer]] <= One Of? Object?(0=>[S], 1=>[I])
|
42
|
+
items: recursive_array_type(t.first)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def process_enum_lth_range
|
47
|
+
self[:_enum] = _enum.to_a if _enum.present? && _enum.is_a?(Range)
|
48
|
+
self[:_length] = _length.to_a if _length.present? && _length.is_a?(Range)
|
49
|
+
|
50
|
+
# generate_enums_by_enum_array
|
51
|
+
values = _enum || _value
|
52
|
+
self._enum = Array(values) if truly_present?(values)
|
53
|
+
end
|
54
|
+
|
55
|
+
def process_enum_info
|
56
|
+
# Support this writing for auto generating desc from enum.
|
57
|
+
# enum: {
|
58
|
+
# 'all_data': :all,
|
59
|
+
# 'one_page': :one
|
60
|
+
# }
|
61
|
+
if _enum.is_a? Hash
|
62
|
+
@enum_info = _enum
|
63
|
+
self._enum = _enum.values
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/open_api/config.rb
CHANGED
@@ -14,10 +14,18 @@ module OpenApi
|
|
14
14
|
true
|
15
15
|
end
|
16
16
|
|
17
|
+
cattr_accessor :doc_location do
|
18
|
+
['./app/**/*_doc.rb']
|
19
|
+
end
|
20
|
+
|
17
21
|
cattr_accessor :rails_routes_file do
|
18
22
|
nil
|
19
23
|
end
|
20
24
|
|
25
|
+
cattr_accessor :active_record_base do
|
26
|
+
ApplicationRecord
|
27
|
+
end
|
28
|
+
|
21
29
|
# Everything about OAS3 is on https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md
|
22
30
|
# Getting started: https://swagger.io/docs/specification/basic-structure/
|
23
31
|
cattr_accessor :open_api_docs do
|
data/lib/open_api/config_dsl.rb
CHANGED
@@ -9,8 +9,8 @@ module OpenApi
|
|
9
9
|
open_api_docs[name] = { root_controller: root_controller }
|
10
10
|
end
|
11
11
|
|
12
|
-
def info version:, title:, **addition
|
13
|
-
open_api_docs[@api][:info] = { version: version, title: title, **addition }
|
12
|
+
def info version:, title:, desc: '', **addition
|
13
|
+
open_api_docs[@api][:info] = { version: version, title: title, description: desc, **addition }
|
14
14
|
end
|
15
15
|
|
16
16
|
def server url, desc: ''
|
@@ -19,7 +19,7 @@ module OpenApi
|
|
19
19
|
|
20
20
|
def security_scheme scheme_name, other_info# = { }
|
21
21
|
other_info[:description] = other_info.delete(:desc) if other_info.key?(:desc)
|
22
|
-
(open_api_docs[@api][:
|
22
|
+
(open_api_docs[@api][:securitySchemes] ||= { })[scheme_name] = other_info
|
23
23
|
end
|
24
24
|
|
25
25
|
def base_auth scheme_name, other_info = { }
|
@@ -42,7 +42,7 @@ module OpenApi
|
|
42
42
|
class << self
|
43
43
|
alias global_security global_security_require
|
44
44
|
alias global_auth global_security_require
|
45
|
-
alias auth_scheme
|
45
|
+
alias auth_scheme security_scheme
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|