zero-rails_openapi 1.3.3 → 1.4.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.
@@ -1,4 +1,5 @@
1
1
  require 'oas_objs/schema_obj'
2
+ require 'oas_objs/combined_schema'
2
3
  require 'oas_objs/param_obj'
3
4
  require 'oas_objs/response_obj'
4
5
  require 'oas_objs/request_body_obj'
@@ -6,8 +6,13 @@ module OpenApi
6
6
  include DSL::CommonDSL
7
7
  include DSL::Helpers
8
8
 
9
- def schema component_key, type, schema_hash# = { }
10
- (self[:schemas] ||= { })[component_key] = SchemaObj.new(type, schema_hash).process
9
+ def schema component_key, type = nil, one_of: nil, all_of: nil, any_of: nil, not: nil, **schema_hash
10
+ (schema_hash = type) and (type = type.delete(:type)) if type.is_a?(Hash) && type.key?(:type)
11
+ type = schema_hash[:type] if type.nil?
12
+
13
+ combined_schema = one_of || all_of || any_of || (_not = binding.local_variable_get(:not))
14
+ combined_schema = CombinedSchema.new(one_of: one_of, all_of: all_of, any_of: any_of, _not: _not) if combined_schema
15
+ (self[:schemas] ||= { })[component_key] = combined_schema&.process || SchemaObj.new(type, schema_hash).process
11
16
  end
12
17
  arrow_enable :schema
13
18
 
@@ -16,19 +21,22 @@ module OpenApi
16
21
  end
17
22
 
18
23
  def param component_key, param_type, name, type, required, schema_hash = { }
19
- (self[:parameters] ||= { })[component_key] =
20
- ParamObj.new(name, param_type, type, required, schema_hash).process
24
+ (self[:parameters] ||= { })[component_key] = ParamObj.new(name, param_type, type, required, schema_hash).process
21
25
  end
22
26
 
23
- def _param_agent component_key, name, type, schema_hash = { }
27
+ def _param_agent component_key, name, type = nil, one_of: nil, all_of: nil, any_of: nil, not: nil, **schema_hash
28
+ (schema_hash = type) and (type = type.delete(:type)) if type.is_a?(Hash) && type.key?(:type)
29
+ type = schema_hash[:type] if type.nil?
30
+
31
+ combined_schema = one_of || all_of || any_of || (_not = binding.local_variable_get(:not))
32
+ schema_hash = CombinedSchema.new(one_of: one_of, all_of: all_of, any_of: any_of, _not: _not) if combined_schema
24
33
  param component_key,
25
34
  "#{@param_type}".delete('!'), name, type, (@param_type['!'] ? :req : :opt), schema_hash
26
35
  end
27
36
  arrow_enable :_param_agent
28
37
 
29
38
  def request_body component_key, required, media_type, desc = '', schema_hash = { }
30
- (self[:requestBodies] ||= { })[component_key] =
31
- RequestBodyObj.new(required, media_type, desc, schema_hash).process
39
+ (self[:requestBodies] ||= { })[component_key] = RequestBodyObj.new(required, media_type, desc, schema_hash).process
32
40
  end
33
41
 
34
42
  def _request_body_agent component_key, media_type, desc = '', schema_hash = { }
@@ -40,6 +48,26 @@ module OpenApi
40
48
  arrow_enable :resp # alias_method 竟然也会指向旧的方法?
41
49
  arrow_enable :response
42
50
 
51
+ def security_scheme scheme_name, other_info# = { }
52
+ other_info[:description] = other_info.delete(:desc) if other_info[:desc]
53
+ (self[:securitySchemes] ||= { })[scheme_name] = other_info
54
+ end
55
+ arrow_enable :security_scheme
56
+
57
+ alias auth_scheme security_scheme
58
+
59
+ def base_auth scheme_name, other_info = { }
60
+ security_scheme scheme_name, { type: 'http', scheme: 'basic' }.merge(other_info)
61
+ end
62
+
63
+ def bearer_auth scheme_name, format = 'JWT', other_info = { }
64
+ security_scheme scheme_name, { type: 'http', scheme: 'bearer', bearerFormat: format }.merge(other_info)
65
+ end
66
+
67
+ def api_key scheme_name, field:, in:, **other_info
68
+ _in = binding.local_variable_get(:in)
69
+ security_scheme scheme_name, { type: 'apiKey', name: field, in: _in }.merge(other_info)
70
+ end
43
71
 
44
72
  def _process_objs
45
73
  self[:responses]&.each do |code, obj|
@@ -10,16 +10,16 @@ module OpenApi
10
10
  # (1) BuilderSupport module: https://github.com/zhandao/zero-rails/blob/master/app/models/concerns/builder_support.rb
11
11
  # (2) config in model: https://github.com/zhandao/zero-rails/tree/master/app/models/good.rb
12
12
  # (3) jbuilder file: https://github.com/zhandao/zero-rails/blob/mster/app/views/api/v1/goods/index.json.jbuilder
13
- # in a word, BuilderSupport let you control the `output fields and nested association infos` very easily.
13
+ # In a word, BuilderSupport let you control the `output fields and nested association infos` very easily.
14
14
  if model.respond_to? :show_attrs
15
- columns = model.columns.map(&:name).map(&:to_sym)
15
+ columns = model.column_names.map(&:to_sym)
16
16
  model.show_attrs.map do |attr|
17
- if columns.include? attr
18
- index = columns.index attr
17
+ if columns.include?(attr)
18
+ index = columns.index(attr)
19
19
  type = model.columns[index].sql_type_metadata.type.to_s.camelize
20
20
  type = 'DateTime' if type == 'Datetime'
21
21
  { attr => Object.const_get(type) }
22
- elsif attr.match? /_info/
22
+ elsif attr.match?(/_info/)
23
23
  # TODO: 如何获知关系是 many?因为不能只判断结尾是否 ‘s’
24
24
  assoc_model = Object.const_get(attr.to_s.split('_').first.singularize.camelize)
25
25
  { attr => load_schema(assoc_model) }
@@ -27,10 +27,9 @@ module OpenApi
27
27
  end
28
28
  else
29
29
  model.columns.map do |column|
30
- name = column.name.to_sym
31
30
  type = column.sql_type_metadata.type.to_s.camelize
32
31
  type = 'DateTime' if type == 'Datetime'
33
- { name => Object.const_get(type) }
32
+ { column.name.to_sym => Object.const_get(type) }
34
33
  end
35
34
  end.compact.reduce({ }, :merge) rescue ''
36
35
  end
@@ -43,8 +42,8 @@ module OpenApi
43
42
  # the key-value (arrow) writing is easy to understand.
44
43
  def arrow_writing_support
45
44
  proc do |args, executor|
46
- _args = args.size == 1 && args.first.is_a?(Hash) ? args[0].to_a.flatten : args
47
- send executor, *_args
45
+ _args = (args.size == 1 && args.first.is_a?(Hash)) ? args[0].to_a.flatten : args
46
+ send(executor, *_args)
48
47
  end
49
48
  end
50
49
 
@@ -7,25 +7,28 @@ module OpenApi
7
7
  end
8
8
 
9
9
  module ClassMethods
10
- def generate_docs(api_name = nil)
11
- Dir['./app/controllers/**/*.rb'].each { |file| require file }
10
+ def generate_docs(doc_name = nil)
11
+ Dir['./app/controllers/**/*_controller.rb'].each do |file|
12
+ # Do Not `require`!
13
+ # It causes problems, such as making `skip_before_action` not working.
14
+ file.sub('./app/controllers/', '').sub('.rb', '').camelize.constantize
15
+ end
12
16
  # TODO: _doc should be configured
13
17
  Dir['./app/**/*_doc.rb'].each { |file| require file }
14
- if api_name.present?
15
- [{ api_name => generate_doc(api_name) }]
18
+ if doc_name.present?
19
+ [{ doc_name => generate_doc(doc_name) }]
16
20
  else
17
21
  Config.docs.keys.map { |api_key| { api_key => generate_doc(api_key) } }.reduce({ }, :merge)
18
22
  end
19
23
  end
20
24
 
21
- def generate_doc(api_name)
22
- settings = Config.docs[api_name]
25
+ def generate_doc(doc_name)
26
+ settings = Config.docs[doc_name]
23
27
  doc = { openapi: '3.0.0' }.merge(settings.slice :info, :servers).merge(
24
- # TODO: rename to just `security`
25
28
  security: settings[:global_security], tags: [ ], paths: { },
26
29
  components: {
27
- securitySchemes: settings[:global_security_schemes],
28
- schemas: { }
30
+ securitySchemes: settings[:security_schemes] || { },
31
+ schemas: { }, parameters: { }, requestBodies: { }
29
32
  }
30
33
  )
31
34
 
@@ -35,14 +38,13 @@ module OpenApi
35
38
  doc[:paths].merge! ctrl.instance_variable_get('@_api_infos') || { }
36
39
  doc[:tags] << ctrl_infos[:tag]
37
40
  doc[:components].merge! ctrl_infos[:components] || { }
38
- ($api_paths_index ||= { })[ctrl.instance_variable_get('@_ctrl_path')] = api_name
41
+ OpenApi.paths_index[ctrl.instance_variable_get('@_ctrl_path')] = doc_name
39
42
  end
40
43
  doc[:components].delete_if { |_, v| v.blank? }
41
44
  doc[:tags] = doc[:tags].sort { |a, b| a[:name] <=> b[:name] }
42
45
  doc[:paths] = doc[:paths].sort.to_h
43
46
 
44
- ($open_apis ||= { })[api_name] ||=
45
- ActiveSupport::HashWithIndifferentAccess.new(doc.delete_if { |_, v| v.blank? })
47
+ OpenApi.docs[doc_name] ||= ActiveSupport::HashWithIndifferentAccess.new(doc.delete_if { |_, v| v.blank? })
46
48
  end
47
49
 
48
50
  def write_docs(generate_files: true)
@@ -51,12 +53,11 @@ module OpenApi
51
53
  output_path = Config.file_output_path
52
54
  FileUtils.mkdir_p output_path
53
55
  max_length = docs.keys.map(&:size).sort.last
54
- puts '[ZRO] * * * * * *'
56
+ # puts '[ZRO] * * * * * *'
55
57
  docs.each do |doc_name, doc|
56
- puts "[ZRO] `%#{max_length}s.json` has been generated." % "#{doc_name}"
58
+ puts "[ZRO] `#{doc_name.to_s.rjust(max_length)}.json` has been generated."
57
59
  File.open("#{output_path}/#{doc_name}.json", 'w') { |file| file.write JSON.pretty_generate doc }
58
60
  end
59
- # pp $open_apis
60
61
  end
61
62
  end
62
63
 
@@ -69,30 +70,37 @@ module OpenApi
69
70
  FileUtils.mkdir_p dir_path
70
71
  file_path = "#{dir_path}/#{action}.json.jbuilder"
71
72
 
72
- if Config.overwrite_jbuilder_file || !File.exists?(file_path)
73
+ if Config.overwrite_jbuilder_file || !File.exist?(file_path)
73
74
  File.open(file_path, 'w') { |file| file.write Config.jbuilder_templates[builder] }
74
75
  puts "[ZRO] JBuilder file has been generated: #{path}/#{action}"
75
76
  end
76
77
  end
77
78
 
78
79
  def self.routes_list
79
- # ref https://github.com/rails/rails/blob/master/railties/lib/rails/tasks/routes.rake
80
- require './config/routes'
81
- all_routes = Rails.application.routes.routes
82
- require 'action_dispatch/routing/inspector'
83
- inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
80
+ routes =
81
+ if (f = Config.rails_routes_file)
82
+ File.read(f)
83
+ else
84
+ # ref https://github.com/rails/rails/blob/master/railties/lib/rails/tasks/routes.rake
85
+ require './config/routes'
86
+ all_routes = Rails.application.routes.routes
87
+ require 'action_dispatch/routing/inspector'
88
+ inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
89
+ inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, nil)
90
+ end
91
+
92
+ @routes_list ||= routes.split("\n").drop(1).map do |line|
93
+ next unless line.match?('#')
94
+ infos = line.match(/[A-Z|].*/).to_s.split(' ') # => [GET, /api/v1/examples/:id, api/v1/examples#index]
84
95
 
85
- @routes_list ||=
86
- inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, nil).split("\n").drop(1).map do |line|
87
- infos = line.match(/[A-Z].*/).to_s.split(' ') # => [GET, /api/v1/examples/:id, api/v1/examples#index]
88
96
  {
89
- http_verb: infos[0].downcase, # => "get"
97
+ http_verb: infos[0].downcase, # => "get" / "get|post"
90
98
  path: infos[1][0..-11].split('/').map do |item|
91
99
  item[':'] ? "{#{item[1..-1]}}" : item
92
100
  end.join('/'), # => "/api/v1/examples/{id}"
93
101
  action_path: infos[2] # => "api/v1/examples#index"
94
102
  } rescue next
95
- end.compact.group_by {|api| api[:action_path].split('#').first } # => { "api/v1/examples" => [..] }, group by paths
103
+ end.compact.group_by { |api| api[:action_path].split('#').first } # => { "api/v1/examples" => [..] }, group by paths
96
104
  end
97
105
 
98
106
  def self.get_actions_by_ctrl_path(ctrl_path)
@@ -104,7 +112,7 @@ module OpenApi
104
112
  def self.find_path_httpverb_by(ctrl_path, action)
105
113
  routes_list[ctrl_path]&.map do |action_info|
106
114
  if action_info[:action_path].split('#').last == action.to_s
107
- return [ action_info[:path], action_info[:http_verb] ]
115
+ return [ action_info[:path], action_info[:http_verb].split('|').first ]
108
116
  end
109
117
  end
110
118
  nil
@@ -1,3 +1,3 @@
1
1
  module OpenApi
2
- VERSION = '1.3.3'
2
+ VERSION = '1.4.0'
3
3
  end
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.3.3
4
+ version: 1.4.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-21 00:00:00.000000000 Z
11
+ date: 2017-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -92,6 +92,7 @@ extra_rdoc_files: []
92
92
  files:
93
93
  - ".gitignore"
94
94
  - ".rspec"
95
+ - ".rubocop.yml"
95
96
  - ".travis.yml"
96
97
  - CODE_OF_CONDUCT.md
97
98
  - Gemfile
@@ -109,6 +110,7 @@ files:
109
110
  - documentation/examples/open_api.rb
110
111
  - documentation/examples/output_example.json
111
112
  - documentation/parameter.md
113
+ - lib/oas_objs/combined_schema.rb
112
114
  - lib/oas_objs/example_obj.rb
113
115
  - lib/oas_objs/helpers.rb
114
116
  - lib/oas_objs/media_type_obj.rb
@@ -148,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
150
  version: '0'
149
151
  requirements: []
150
152
  rubyforge_project:
151
- rubygems_version: 2.6.12
153
+ rubygems_version: 2.6.13
152
154
  signing_key:
153
155
  specification_version: 4
154
156
  summary: Generate the OpenAPI Specification 3 documentation for Rails application.