zero-rails_openapi 1.7.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -359,11 +359,11 @@
359
359
 
360
360
 
361
361
  # method signature
362
- # `exp_by` (select_example_by): choose the example fields.
363
- examples(exp_by = :all, examples_hash)
362
+ # `exp_params` (select_example_by): choose the example fields.
363
+ examples(exp_params = :all, examples_hash)
364
364
  # usage
365
365
  # it defines 2 examples by using parameter :id and :name
366
- # if pass :all to `exp_by`, keys will be all the parameter's names.
366
+ # if pass :all to `exp_params`, keys will be all the parameter's names.
367
367
  examples [:id, :name], {
368
368
  :right_input => [ 1, 'user'], # == { id: 1, name: 'user' }
369
369
  :wrong_input => [ -1, '' ]
@@ -425,7 +425,7 @@
425
425
  :password! => { type: String, pattern: /[0-9]{6,10}/, desc: 'password' },
426
426
  # optional
427
427
  :remarks => { type: String, desc: 'remarks' },
428
- }, exp_by: %i[ name password ],
428
+ }, exp_params: %i[ name password ],
429
429
  examples: { # ↓ ↓
430
430
  :right_input => [ 'user1', '123456' ],
431
431
  :wrong_input => [ 'user2', 'abc' ]
@@ -448,7 +448,7 @@
448
448
 
449
449
  1. `media_type`: we provide some [mapping](lib/oas_objs/media_type_obj.rb) from symbols to real media-types.
450
450
  2. `schema_info`: as above (see param).
451
- 3. `exp_by` and `examples`: for the above example, the following has the same effect:
451
+ 3. `exp_params` and `examples`: for the above example, the following has the same effect:
452
452
  ```
453
453
  examples: {
454
454
  :right_input => { name: 'user1', password: '123456' },
@@ -1,25 +1,23 @@
1
1
  require 'open_api'
2
2
 
3
- OpenApi::Config.tap do |c|
3
+ OpenApi::Config.class_eval do
4
4
  # Config DSL
5
- c.instance_eval do
6
- open_api :zero_rails, base_doc_classes: [ApiDoc]
7
- info version: '0.0.1', title: 'Zero Rails APIs', description: 'API documentation of Zero-Rails Application.'
8
- server 'http://localhost:3000', desc: 'Main (production) server'
9
- server 'http://localhost:3000', desc: 'Internal staging server for testing'
10
- bearer_auth :Token
11
- global_auth :Token
12
- end
5
+ open_api :zero_rails, base_doc_classes: [ApiDoc]
6
+ info version: '0.0.1', title: 'Zero Rails APIs', description: 'API documentation of Zero-Rails Application.'
7
+ server 'http://localhost:3000', desc: 'Main (production) server'
8
+ server 'http://localhost:3000', desc: 'Internal staging server for testing'
9
+ bearer_auth :Token
10
+ global_auth :Token
13
11
 
14
12
  # [REQUIRED] The location where .json doc file will be output.
15
- c.file_output_path = 'public/open_api'
13
+ self.file_output_path = 'public/open_api'
16
14
 
17
15
  # [Optional] Use this txt instead of running `rails routes`.
18
- # c.rails_routes_file = 'config/routes.txt'
16
+ # self.rails_routes_file = 'config/routes.txt'
19
17
 
20
18
  # Everything about OAS3 is on https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md
21
19
  # Getting started: https://swagger.io/docs/specification/basic-structure/
22
- c.open_api_docs = {
20
+ self.open_api_docs = {
23
21
  blog_api: {
24
22
  # [REQUIRED] ZRO will scan all the descendants of the base_doc_classes, then generate their docs.
25
23
  base_doc_classes: [ApiController],
@@ -96,9 +94,7 @@ OpenApi::Config.tap do |c|
96
94
 
97
95
  end
98
96
 
99
- Object.const_set('Boolean', 'boolean') # Support `Boolean` writing in DSL
100
-
101
- OpenApi.write_docs generate_files: !Rails.env.production?
97
+ OpenApi.write_docs if: !Rails.env.production?
102
98
 
103
99
 
104
100
  __END__
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oas_objs/helpers'
2
4
 
3
5
  module OpenApi
@@ -17,29 +19,22 @@ module OpenApi
17
19
 
18
20
  def process
19
21
  {
20
- self.event_name => {
22
+ event_name => {
21
23
  processed_url => {
22
- self.http_method.downcase.to_sym => processed_block
24
+ http_method.downcase.to_sym => Api.new.run_dsl(&(self.block || -> { }))
23
25
  }
24
26
  }
25
27
  }
26
28
  end
27
29
 
28
30
  def processed_url
29
- self.callback_url.gsub(/{[^{}]*}/) do |exp|
31
+ callback_url.gsub(/{[^{}]*}/) do |exp|
30
32
  key_location, key_name = exp[1..-2].split
31
33
  connector = key_location == 'body' ? '#/' : '.'
32
34
  key_location = '$request.' + key_location
33
35
  ['{', key_location, connector, key_name, '}'].join
34
36
  end
35
37
  end
36
-
37
- def processed_block
38
- api = Api.new.merge! parameters: [ ], requestBody: '', responses: { }
39
- api.instance_exec(&(self.block || -> { }))
40
- api.process_objs
41
- api.delete_if { |_, v| v.blank? }
42
- end
43
38
  end
44
39
  end
45
40
  end
@@ -1,25 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OpenApi
2
4
  module DSL
3
5
  # https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/
4
6
  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaObject
5
7
  class CombinedSchema < Hash
6
- attr_accessor :processed
8
+ include Helpers
7
9
 
8
- def initialize(combined_schema)
9
- self.processed = { }
10
+ attr_accessor :processed, :mode, :schemas
10
11
 
12
+ def initialize(combined_schema)
11
13
  combined_schema.delete_if { |_, v| v.nil? }
12
- @mode = combined_schema.keys.first.to_s.sub('_not', 'not').camelize(:lower).to_sym
13
- @schemas = combined_schema.values.first
14
+ self.mode = combined_schema.keys.first.to_s.camelize(:lower).to_sym
15
+ self.schemas = combined_schema.values.first
14
16
  end
15
17
 
16
- def process(options = { inside_desc: false })
17
- processed[@mode] = @schemas.map do |schema|
18
- type = schema.is_a?(Hash) ? schema[:type] : schema
19
- schema = { } unless schema.is_a?(Hash)
20
- SchemaObj.new(type, schema).process(options)
21
- end
22
- processed
18
+ def process
19
+ self.processed = {
20
+ mode =>
21
+ schemas.map do |schema|
22
+ type = schema.is_a?(Hash) ? schema[:type] : schema
23
+ schema = { } unless schema.is_a?(Hash)
24
+ SchemaObj.new(type, schema).process
25
+ end
26
+ }
23
27
  end
24
28
  end
25
29
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oas_objs/helpers'
2
4
  require 'oas_objs/ref_obj'
3
5
 
@@ -7,7 +9,7 @@ module OpenApi
7
9
  class ExampleObj < Hash
8
10
  include Helpers
9
11
 
10
- attr_accessor :processed, :examples_hash, :example_value, :keys_of_value
12
+ attr_accessor :examples_hash, :example_value, :keys_of_value
11
13
 
12
14
  def initialize(exp, keys_of_value = nil, multiple: false)
13
15
  multiple ? self.examples_hash = exp : self.example_value = exp
@@ -15,11 +17,11 @@ module OpenApi
15
17
  end
16
18
 
17
19
  def process
18
- return self.processed = example_value if example_value
20
+ return example_value if example_value
21
+ return unless examples_hash
19
22
 
20
- self.processed =
21
- examples_hash.map do |(name, value)|
22
- value =
23
+ examples_hash.map do |(name, value)|
24
+ value =
23
25
  if keys_of_value.present? && value.is_a?(Array)
24
26
  { value: Hash[keys_of_value.zip(value)] }
25
27
  elsif value.is_a?(Symbol) && value['$']
@@ -28,8 +30,8 @@ module OpenApi
28
30
  { value: value }
29
31
  end
30
32
 
31
- { name => value }
32
- end
33
+ { name => value }
34
+ end
33
35
  end
34
36
  end
35
37
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OpenApi
2
4
  module Helpers
3
5
  def fusion
@@ -12,26 +14,8 @@ module OpenApi
12
14
  proc { |_, v| truly_present? v }
13
15
  end
14
16
 
15
- # assign.to
16
- def assign(value)
17
- @assign = value.is_a?(Symbol) ? send("_#{value}") : value
18
- self
19
- end
20
-
21
- # reducx.then_merge! => for Hash
22
- def reducx(*values)
23
- @assign = values.compact.reduce({ }, :merge!).keep_if &value_present
24
- self
25
- end
26
-
27
- def to_processed(who)
28
- return processed unless truly_present?(@assign)
29
- processed[who.to_sym] = @assign
30
- processed
31
- end
32
-
33
- def then_merge! # to_processed
34
- processed.tap { |it| it.merge! @assign if truly_present?(@assign) }
17
+ def reducing(*values)
18
+ values.compact.reduce(processed, :merge!).keep_if &value_present
35
19
  end
36
20
  end
37
21
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oas_objs/schema_obj'
2
4
  require 'oas_objs/example_obj'
3
5
 
@@ -8,21 +10,20 @@ module OpenApi
8
10
  attr_accessor :media_type, :schema, :examples
9
11
 
10
12
  def initialize(media_type, hash)
11
- examples_hash = hash.delete(:examples)
12
- exp_by = hash.delete(:exp_by)
13
- schema_type = hash.values_at(:type, :data).compact.first
14
- exp_by = schema_type.keys if exp_by == :all
15
-
16
- self.examples = ExampleObj.new(examples_hash, exp_by, multiple: true) if examples_hash.present?
17
- self.media_type = media_type_mapping media_type
18
- self.schema = SchemaObj.new(schema_type, hash)
13
+ examples_hash = hash.delete(:examples)
14
+ exp_params = schema_type.keys if (exp_params = hash.delete(:exp_params)) == :all
15
+ self.examples = ExampleObj.new(examples_hash, exp_params, multiple: true) if examples_hash.present?
16
+ self.media_type = media_type_mapping(media_type)
17
+ self.schema = SchemaObj.new(hash.values_at(:type, :data).compact.first,
18
+ hash.except(:type, :data))
19
19
  end
20
20
 
21
21
  def process
22
+ return { } if media_type.nil?
22
23
  schema_processed = schema.process
23
24
  result = schema_processed.values.join.blank? ? { } : { schema: schema_processed }
24
25
  result[:examples] = examples.process unless examples.nil?
25
- media_type.nil? ? { } : { media_type => result }
26
+ { media_type => result }
26
27
  end
27
28
 
28
29
  # https://swagger.io/docs/specification/media-types/
@@ -46,7 +47,6 @@ module OpenApi
46
47
  when :xlsx then 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
47
48
  when :ppt then 'application/vnd.ms-powerpoint'
48
49
  when :pptx then 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
49
- # when :pdf then 'application/pdf'
50
50
  when :form then 'multipart/form-data'; when :form_data then 'multipart/form-data'
51
51
  when :text then 'text/*'
52
52
  when :plain then 'text/plain then charset=utf-8'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oas_objs/helpers'
2
4
  require 'oas_objs/schema_obj'
3
5
 
@@ -10,25 +12,20 @@ module OpenApi
10
12
 
11
13
  def initialize(name, param_type, type, required, schema)
12
14
  self.processed = {
13
- name: name,
14
- in: param_type,
15
+ name: name.to_s.delete('!').to_sym,
16
+ in: param_type.to_s.delete('!'),
15
17
  required: required.to_s[/req/].present?
16
18
  }
17
- self.schema = schema.is_a?(CombinedSchema) ? schema : SchemaObj.new(type, schema)
18
- merge! schema
19
+ merge!(self.schema = schema)
19
20
  end
20
21
 
21
22
  def process
22
- assign(desc).to_processed :description
23
- assign(schema.process).to_processed :schema
23
+ processed[:schema] = schema.process
24
+ desc = schema.processed[:description]
25
+ processed[:description] = desc if desc
24
26
  processed
25
27
  end
26
28
 
27
- def desc
28
- return self[:desc] || self[:description] if (self[:desc!] || self[:description!]).blank?
29
- schema.__desc # not a copy of __desc, means desc() will change if schema.__desc changes.
30
- end
31
-
32
29
  def name
33
30
  processed[:name]
34
31
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oas_objs/helpers'
2
4
 
3
5
  module OpenApi
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oas_objs/media_type_obj'
2
4
  require 'oas_objs/helpers'
3
5
 
@@ -9,18 +11,20 @@ module OpenApi
9
11
  include Helpers
10
12
 
11
13
  attr_accessor :processed, :media_types
14
+
12
15
  def initialize(required, desc)
13
16
  self.media_types = [ ]
14
17
  self.processed = { required: required['req'].present?, description: desc }
15
18
  end
16
19
 
17
- def add_or_fusion(media_type, hash)
20
+ def absorb(media_type, hash)
18
21
  media_types << MediaTypeObj.new(media_type, hash)
19
22
  self
20
23
  end
21
24
 
22
25
  def process
23
- assign(media_types.map(&:process).reduce({ }, &fusion)).to_processed 'content'
26
+ content = media_types.map(&:process).reduce({ }, &fusion)
27
+ processed[:content] = content if content.present?
24
28
  processed
25
29
  end
26
30
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oas_objs/media_type_obj'
2
4
  require 'oas_objs/helpers'
3
5
 
@@ -13,14 +15,15 @@ module OpenApi
13
15
  self.processed = { description: desc }
14
16
  end
15
17
 
16
- def add_or_fusion(desc, media_type, hash)
18
+ def absorb(desc, media_type, hash)
17
19
  self.processed[:description] = desc if desc.present?
18
20
  media_types << MediaTypeObj.new(media_type, hash)
19
21
  self
20
22
  end
21
23
 
22
24
  def process
23
- assign(media_types.map(&:process).reduce({ }, &fusion)).to_processed 'content'
25
+ content = media_types.map(&:process).reduce({ }, &fusion)
26
+ processed[:content] = content if content.present?
24
27
  processed
25
28
  end
26
29
  end
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oas_objs/helpers'
2
- require 'open_api/config'
3
4
  require 'oas_objs/ref_obj'
4
5
  require 'oas_objs/example_obj'
5
6
  require 'oas_objs/schema_obj_helpers'
@@ -11,28 +12,26 @@ module OpenApi
11
12
  include SchemaObjHelpers
12
13
  include Helpers
13
14
 
14
- attr_accessor :processed, :type, :preprocessed
15
+ attr_accessor :processed, :type
15
16
 
16
- def initialize(type, schema_info)
17
- self.preprocessed = false
18
- self.processed = { }
19
- self.type = type
20
- merge! schema_info
17
+ def initialize(type = nil, schema)
18
+ self.merge!(schema)
19
+ self.processed = { type: nil, format: nil, **schema.except(:type, :range, :enum!, *SELF_MAPPING.values.flatten) }
20
+ self.type = type || self[:type]
21
21
  end
22
22
 
23
- def process(options = { inside_desc: false })
24
- processed.merge!(processed_type)
25
- reducx(additional_properties, enum_and_length, range, format, pattern_default_and_other, desc(options)).then_merge!
23
+ def process
24
+ processed.merge!(recg_schema_type)
25
+ reducing(additional_properties, enum, length, range, format, other, desc)
26
26
  end
27
27
 
28
- def desc(inside_desc:)
29
- result = __desc ? auto_generate_desc : _desc
30
- return unless inside_desc
28
+ def desc
29
+ return unless (result = @bang_enum.present? ? auto_generate_desc : _desc)
31
30
  { description: result }
32
31
  end
33
32
 
34
- def processed_type(type = self.type)
35
- t = type.class.in?([Hash, Array, Symbol]) ? type : type.to_s.downcase
33
+ def recg_schema_type(t = self.type)
34
+ t = t.class.in?([Hash, Array, Symbol]) ? t : t.to_s.downcase
36
35
  if t.is_a? Hash
37
36
  hash_type(t)
38
37
  elsif t.is_a? Array
@@ -56,93 +55,75 @@ module OpenApi
56
55
  end
57
56
 
58
57
  def additional_properties
59
- return { } if processed[:type] != 'object' || _addProp.nil?
58
+ return if processed[:type] != 'object' || _addProp.nil?
60
59
  {
61
- additionalProperties: SchemaObj.new(_addProp, { }).process(inside_desc: true)
60
+ additionalProperties: SchemaObj.new(_addProp, { }).process
62
61
  }
63
62
  end
64
63
 
65
- def enum_and_length
66
- process_enum_info
67
- process_range_enum_and_lth
68
-
69
- # generate length range fields by _lth array
70
- if (lth = _length || '').is_a?(Array)
71
- min, max = [lth.first&.to_i, lth.last&.to_i]
72
- elsif lth['ge']
73
- min = lth.to_s.split('_').last.to_i
74
- elsif lth['le']
75
- max = lth.to_s.split('_').last.to_i
64
+ def enum
65
+ self._enum = str_range_to_a(_enum) if _enum.is_a?(Range)
66
+ # Support this writing for auto generating desc from enum.
67
+ # enum!: {
68
+ # 'all_desc': :all,
69
+ # 'one_desc': :one
70
+ # }
71
+ if (@bang_enum = self[:enum!])
72
+ self._enum ||= @bang_enum.is_a?(Hash) ? @bang_enum.values : @bang_enum
76
73
  end
74
+ { enum: _enum }
75
+ end
77
76
 
78
- if processed[:type] == 'array'
79
- { minItems: min, maxItems: max }
77
+ def length
78
+ return unless _length
79
+ self._length = str_range_to_a(_length) if _length.is_a?(Range)
80
+
81
+ if _length.is_a?(Array)
82
+ min, max = [ _length.first&.to_i, _length.last&.to_i ]
80
83
  else
81
- { minLength: min, maxLength: max }
82
- end.merge!(enum: _enum).keep_if &value_present
84
+ min, max = _length[/ge_(.*)/, 1]&.to_i, _length[/le_(.*)/, 1]&.to_i
85
+ end
86
+
87
+ processed[:type] == 'array' ? { minItems: min, maxItems: max } : { minLength: min, maxLength: max }
83
88
  end
84
89
 
85
90
  def range
86
- range = _range || { }
91
+ (range = self[:range]) or return
87
92
  {
88
93
  minimum: range[:gt] || range[:ge],
89
- exclusiveMinimum: range[:gt].present? ? true : nil,
94
+ exclusiveMinimum: range[:gt].present? || nil,
90
95
  maximum: range[:lt] || range[:le],
91
- exclusiveMaximum: range[:lt].present? ? true : nil
92
- }.keep_if &value_present
96
+ exclusiveMaximum: range[:lt].present? || nil
97
+ }
93
98
  end
94
99
 
95
100
  def format
96
- result = { is: _is }
97
- # `format` that generated in process_type() may be overwrote here.
98
- result[:format] = _format || _is if processed[:format].blank? || _format.present?
99
- result
101
+ { format: self[:format] || self[:is_a] } unless processed[:format]
100
102
  end
101
103
 
102
- def pattern_default_and_other
104
+ def other
103
105
  {
104
106
  pattern: _pattern.is_a?(String) ? _pattern : _pattern&.inspect&.delete('/'),
105
- default: _default,
106
- example: _exp.present? ? ExampleObj.new(_exp).process : nil,
107
- examples: _exps.present? ? ExampleObj.new(_exps, self[:exp_by], multiple: true).process : nil,
108
- as: _as, permit: _permit, not_permit: _npermit, req_if: _req_if, opt_if: _opt_if, blankable: _blank
107
+ example: ExampleObj.new(self[:example]).process,
108
+ examples: ExampleObj.new(self[:examples], self[:exp_params], multiple: true).process
109
109
  }
110
110
  end
111
111
 
112
112
 
113
- { # SELF_MAPPING
113
+ SELF_MAPPING = {
114
114
  _enum: %i[ enum in values allowable_values ],
115
- _value: %i[ must_be value allowable_value ],
116
- _range: %i[ range number_range ],
117
115
  _length: %i[ length lth size ],
118
- _format: %i[ format fmt ],
119
- _pattern: %i[ pattern regexp pt reg ],
120
- _default: %i[ default dft default_value ],
116
+ _pattern: %i[ pattern regexp ],
121
117
  _desc: %i[ desc description d ],
122
- __desc: %i[ desc! description! d! ],
123
- _exp: %i[ example ],
124
- _exps: %i[ examples ],
125
118
  _addProp: %i[ additional_properties add_prop values_type ],
126
- _is: %i[ is_a is ], # NOT OAS Spec, see documentation/parameter.md
127
- _as: %i[ as to for map mapping ], # NOT OAS Spec, it's for zero-params_processor
128
- _permit: %i[ permit pmt ], # ditto
129
- _npermit: %i[ npmt not_permit unpermit ], # ditto
130
- _req_if: %i[ req_if req_when ], # ditto
131
- _opt_if: %i[ opt_if opt_when ], # ditto
132
- _blank: %i[ blank blankable ], # ditto
133
119
  }.each do |key, aliases|
134
- define_method key do
135
- return self[key] unless self[key].nil?
136
- aliases.each { |alias_name| self[key] = self[alias_name] if self[key].nil? }
137
- self[key]
138
- end
120
+ define_method(key) { self[key] ||= self.values_at(*aliases).compact.first }
139
121
  define_method("#{key}=") { |value| self[key] = value }
140
122
  end
141
123
  end
142
124
  end
143
125
  end
144
126
 
145
-
146
127
  __END__
147
128
 
148
129
  Schema Object Examples