zero-rails_openapi 1.7.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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