zero-rails_openapi 1.4.0 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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? /sController|sDoc/
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
- api :index, 'GET list of goods.', builder: :index, # jbuilder templates is set in initializers/open_api.rb
5
- use: [ 'Token', :page, :rows ] do # use parameters write in AutoGenDoc#api_dry
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', builder: :success_or_not, use: 'Token' do
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.', builder: :show, use: [ 'Token', :id ]
48
+ api :show, 'GET the specified Good.', use: [ 'Token', :id ]
50
49
 
51
50
 
52
- api :destroy, 'DELETE the specified Good.', builder: :success_or_not, use: [ 'Token', :id ]
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
- security_schemes: {
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
@@ -1,6 +1,5 @@
1
1
  module OpenApi
2
2
  module Helpers
3
- # TODO: comment-block doc
4
3
  def truly_present?(obj)
5
4
  obj == false || obj.present?
6
5
  end
@@ -24,11 +24,8 @@ module OpenApi
24
24
  end
25
25
 
26
26
  def desc
27
- if __desc.present?
28
- schema.preprocess_with_desc __desc, self[:name]
29
- else
30
- _desc
31
- end
27
+ return _desc unless __desc.present?
28
+ schema.preprocess_with_desc __desc, self[:name]
32
29
  end
33
30
 
34
31
 
@@ -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 processed_enum_and_length,
32
- processed_range,
33
- processed_is_and_format(param_name),
34
- {
35
- pattern: _pattern&.inspect&.delete('/'),
36
- default: _default.nil? ? nil : '_default',
37
- examples: self[:examples].present? ? ExampleObj.new(self[:examples], self[:exp_by]).process : nil
38
- },
39
- { as: _as, permit: _permit, not_permit: _npermit, req_if: _req_if, opt_if: _opt_if }
40
- then_merge!
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 options).then_merge!
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 = process_desc : _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
- # For supporting this:
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 t
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 recursive_obj_type(t) # DSL use { prop_name: prop_type } to represent object structure
110
- return processed_type(t) if !t.is_a?(Hash) || (t.keys & %i[ type one_of any_of all_of not ]).present?
111
-
112
- _schema = {
113
- type: 'object',
114
- properties: { },
115
- required: [ ]
116
- }
117
- t.each do |prop_name, prop_type|
118
- @prop_name = 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
- processed_type t
95
+ recursive_obj_type(t)
134
96
  end
135
97
  end
136
98
 
137
99
  def processed_enum_and_length
138
- # Support this writing for auto generating desc from enum.
139
- # enum: {
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.merge!(format: _format || _is) if processed[:format].blank? || _format.present?
184
- it.merge! is: _is
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 if _is.nil?
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 if self[key].nil?
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
@@ -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
@@ -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][:security_schemes] ||= { })[scheme_name] = other_info
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 security_scheme
45
+ alias auth_scheme security_scheme
46
46
  end
47
47
  end
48
48
  end