zero-rails_openapi 1.3.3 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.