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.
@@ -1,15 +1,19 @@
1
- require 'open_api/dsl/common_dsl'
1
+ # frozen_string_literal: true
2
+
3
+ require 'open_api/dsl/helpers'
2
4
 
3
5
  module OpenApi
4
6
  module DSL
5
7
  class Components < Hash
6
- include DSL::CommonDSL
7
8
  include DSL::Helpers
8
9
 
9
- def schema component_key, type = nil, **schema_info
10
- schema = process_schema_info(type, schema_info, model: component_key)
11
- return puts ' ZRO'.red + " Syntax Error: component schema `#{component_key}` has no type!" if schema[:illegal?]
12
- self[:schemas][component_key.to_s.to_sym] = (schema[:combined] or SchemaObj.new(type = schema[:info], { })).process
10
+ def initialize
11
+ merge!(%i[ schemas responses parameters examples requestBodies securitySchemes ].map { |k| [ k, { } ] }.to_h)
12
+ end
13
+
14
+ def schema component_key, type = nil, **schema
15
+ return unless schema = process_schema_input(type, schema, component_key, model: component_key)
16
+ self[:schemas][component_key.to_s.to_sym] = schema.process
13
17
  end
14
18
 
15
19
  arrow_enable :schema
@@ -20,31 +24,37 @@ module OpenApi
20
24
 
21
25
  arrow_enable :example
22
26
 
23
- def param component_key, param_type, name, type, required, schema_info = { }
24
- self[:parameters][component_key] = ParamObj.new(name, param_type, type, required, schema_info).process
27
+ def param component_key, param_type, name, type, required, schema = { }
28
+ return unless schema = process_schema_input(type, schema, name)
29
+ self[:parameters][component_key] = ParamObj.new(name, param_type, type, required, schema).process
25
30
  end
26
31
 
27
- # [ header header! path path! query query! cookie cookie! ]
28
- def _param_agent component_key, name, type = nil, **schema_info
29
- schema = process_schema_info(type, schema_info)
30
- return puts ' ZRO'.red + " Syntax Error: param `#{name}` has no schema type!" if schema[:illegal?]
31
- param component_key, @param_type, name, schema[:type], @necessity, schema[:combined] || schema[:info]
32
+ %i[ header header! path path! query query! cookie cookie! ].each do |param_type|
33
+ define_method param_type do |component_key, name, type = nil, **schema|
34
+ param component_key, param_type, name, type, (param_type['!'] ? :req : :opt), schema
35
+ end
36
+ arrow_enable param_type
32
37
  end
33
38
 
34
- arrow_enable :_param_agent
35
-
36
39
  def request_body component_key, required, media_type, data: { }, desc: '', **options
37
- cur = self[:requestBodies][component_key]
38
- cur = RequestBodyObj.new(required, desc) unless cur.is_a?(RequestBodyObj)
39
- self[:requestBodies][component_key] = cur.add_or_fusion(media_type, { data: data, **options })
40
+ (self[:requestBodies][component_key] ||= RequestBodyObj.new(required, desc)).absorb(media_type, { data: data, **options })
40
41
  end
41
42
 
42
- # [ body body! ]
43
- def _request_body_agent component_key, media_type, data: { }, **options
44
- request_body component_key, @necessity, media_type, data: data, **options
43
+ %i[ body body! ].each do |method|
44
+ define_method method do |component_key, media_type, data: { }, **options|
45
+ request_body component_key, (method['!'] ? :req : :opt), media_type, data: data, **options
46
+ end
47
+ end
48
+
49
+ arrow_enable :body
50
+ arrow_enable :body!
51
+
52
+ def response component_key, desc, media_type = nil, data: { }
53
+ (self[:responses][component_key] ||= ResponseObj.new(desc)).absorb(desc, media_type, { data: data })
45
54
  end
46
55
 
47
- arrow_enable :_request_body_agent
56
+ alias_method :resp, :response
57
+ alias_method :error, :response
48
58
 
49
59
  arrow_enable :resp
50
60
  arrow_enable :response
@@ -71,8 +81,7 @@ module OpenApi
71
81
  arrow_enable :bearer_auth
72
82
 
73
83
  def api_key scheme_name, field:, in: 'header', **other_info
74
- _in = binding.local_variable_get(:in)
75
- security_scheme scheme_name, { type: 'apiKey', name: field, in: _in, **other_info }
84
+ security_scheme scheme_name, { type: 'apiKey', name: field, in: binding.local_variable_get(:in), **other_info }
76
85
  end
77
86
 
78
87
  arrow_enable :api_key
@@ -80,6 +89,7 @@ module OpenApi
80
89
  def process_objs
81
90
  self[:requestBodies].each { |key, body| self[:requestBodies][key] = body.process }
82
91
  self[:responses].each { |code, response| self[:responses][code] = response.process }
92
+ self.delete_if { |_, v| v.blank? }
83
93
  end
84
94
  end
85
95
  end
@@ -1,65 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'oas_objs/schema_obj'
4
+ require 'oas_objs/combined_schema'
5
+ require 'oas_objs/param_obj'
6
+ require 'oas_objs/response_obj'
7
+ require 'oas_objs/request_body_obj'
8
+ require 'oas_objs/ref_obj'
9
+ require 'oas_objs/example_obj'
10
+ require 'oas_objs/callback_obj'
11
+
1
12
  module OpenApi
2
13
  module DSL
3
14
  module Helpers
4
- def self.included(base)
5
- base.extend ClassMethods
6
- end
15
+ extend ActiveSupport::Concern
7
16
 
8
- # :nocov:
9
17
  def load_schema(model) # TODO: test
10
- # About `show_attrs`, see:
11
- # (1) BuilderSupport module: https://github.com/zhandao/zero-rails/blob/master/app/models/concerns/builder_support.rb
12
- # (2) config in model: https://github.com/zhandao/zero-rails/tree/master/app/models/good.rb
13
- # (3) jbuilder file: https://github.com/zhandao/zero-rails/blob/master/app/views/api/v1/goods/index.json.jbuilder
14
- # In a word, BuilderSupport let you control the `output fields and nested association infos` very easily.
15
- if model.respond_to? :show_attrs
16
- _load_schema_based_on_show_attr(model)
17
- else
18
- model.columns.map { |column| _type_mapping(column) }
19
- end.compact.reduce({ }, :merge!) rescue ''
20
- end
21
-
22
- def _type_mapping(column)
23
- type = column.sql_type_metadata.type.to_s.camelize
24
- type = 'DateTime' if type == 'Datetime'
25
- { column.name.to_sym => Object.const_get(type) }
26
- end
27
-
28
- def _load_schema_based_on_show_attr(model)
29
- columns = model.column_names.map(&:to_sym)
30
- model.show_attrs.map do |attr|
31
- if columns.include?(attr)
32
- index = columns.index(attr)
33
- _type_mapping(model.columns[index])
34
- elsif attr[/_info/]
35
- # TODO: 如何获知关系是 many?因为不能只判断结尾是否 ‘s’
36
- assoc_model = Object.const_get(attr.to_s.split('_').first.singularize.camelize)
37
- { attr => load_schema(assoc_model) }
38
- end rescue next
39
- end
40
- end
41
- # :nocov:
42
-
43
- def fill_in_parameters(param_obj)
44
- index = self[:parameters].map(&:name).index(param_obj.name)
45
- index.present? ? self[:parameters][index] = param_obj : self[:parameters] << param_obj
18
+ return unless Config.model_base && model.try(:superclass) == Config.model_base
19
+ model.columns.map do |column|
20
+ type = column.sql_type_metadata.type.to_s.camelize
21
+ type = 'DateTime' if type == 'Datetime'
22
+ [ column.name.to_sym, Object.const_get(type) ]
23
+ end.to_h rescue ''
46
24
  end
47
25
 
48
26
  def _combined_schema(one_of: nil, all_of: nil, any_of: nil, not: nil, **other)
49
27
  input = (_not = binding.local_variable_get(:not)) || one_of || all_of || any_of
50
- CombinedSchema.new(one_of: one_of, all_of: all_of, any_of: any_of, _not: _not) if input
28
+ CombinedSchema.new(one_of: one_of, all_of: all_of, any_of: any_of, not: _not) if input
51
29
  end
52
30
 
53
- def process_schema_info(schema_type, schema_info, model: nil)
54
- combined_schema = _combined_schema(schema_info)
55
- type = schema_info[:type] ||= schema_type
56
- schema_info = load_schema(model) if model.try(:superclass) == (Config.active_record_base || ApplicationRecord)
57
- {
58
- illegal?: type.nil? && combined_schema.nil?,
59
- combined: combined_schema,
60
- info: schema_info,
61
- type: type
62
- }
31
+ def process_schema_input(schema_type, schema, name, model: nil)
32
+ schema = { type: schema } unless schema.is_a?(Hash)
33
+ combined_schema = _combined_schema(schema)
34
+ type = schema[:type] ||= schema_type
35
+ return Tip.param_no_type(name) if type.nil? && combined_schema.nil?
36
+ combined_schema || SchemaObj.new(type, load_schema(model) || schema)
63
37
  end
64
38
 
65
39
  # Arrow Writing:
@@ -75,7 +49,7 @@ module OpenApi
75
49
  end
76
50
  end
77
51
 
78
- module ClassMethods
52
+ class_methods do
79
53
  def arrow_enable method
80
54
  alias_method :"_#{method}", method
81
55
  define_method method do |*args|
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenApi
4
+ module Router
5
+ module_function
6
+
7
+ def routes
8
+ @routes ||=
9
+ if (file = Config.rails_routes_file)
10
+ File.read(file)
11
+ else
12
+ # :nocov:
13
+ # ref https://github.com/rails/rails/blob/master/railties/lib/rails/tasks/routes.rake
14
+ require './config/routes'
15
+ all_routes = Rails.application.routes.routes
16
+ require 'action_dispatch/routing/inspector'
17
+ inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
18
+ inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, nil)
19
+ # :nocov:
20
+ end
21
+ end
22
+
23
+ def routes_list
24
+ @routes_list ||= routes.split("\n").drop(1).map do |line|
25
+ next unless line['#']
26
+ infos = line.match(/[A-Z|].*/).to_s.split(' ') # => [GET, /api/v1/examples/:id, api/v1/examples#index]
27
+
28
+ {
29
+ http_verb: infos[0].downcase, # => "get" / "get|post"
30
+ path: infos[1][0..-11].split('/').map do |item|
31
+ item[':'] ? "{#{item[1..-1]}}" : item
32
+ end.join('/'), # => "/api/v1/examples/{id}"
33
+ action_path: infos[2] # => "api/v1/examples#index"
34
+ } rescue next
35
+ end.compact.group_by { |api| api[:action_path].split('#').first } # => { "api/v1/examples" => [..] }, group by paths
36
+ end
37
+
38
+ def get_actions_by_route_base(route_base)
39
+ routes_list[route_base]&.map { |action_info| action_info[:action_path].split('#').last }
40
+ end
41
+
42
+ def find_path_httpverb_by(route_base, action)
43
+ routes_list[route_base]&.map do |action_info|
44
+ if action_info[:action_path].split('#').last == action.to_s
45
+ return [ action_info[:path], action_info[:http_verb].split('|').first ]
46
+ end
47
+ end ; nil
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenApi
4
+ module Tip
5
+ extend self
6
+
7
+ def no_config; puts ' OpenApi'.red + ' No documents have been configured!' end
8
+ def loaded; puts ' OpenApi'.green + ' loaded' if ENV['RAILS_ENV'] end
9
+
10
+ def generated(name)
11
+ puts ' OpenApi'.green + " `#{name}.json` has been generated."
12
+ end
13
+
14
+ def schema_no_type(component_key)
15
+ puts ' OpenApi'.red + " Syntax Error: component schema `#{component_key}` has no type!"
16
+ end
17
+
18
+ def param_no_type(name)
19
+ puts ' OpenApi'.red + " Syntax Error: param `#{name}` has no schema type!"
20
+ end
21
+
22
+ def no_route(action_path)
23
+ puts ' OpenApi'.red + " Route mapping failed: #{action_path}"
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OpenApi
2
- VERSION = '1.7.0'
4
+ VERSION = '2.0.0'
3
5
  end
@@ -23,15 +23,15 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.required_ruby_version = '>= 2.3.0'
25
25
 
26
- spec.add_development_dependency 'bundler', '~> 1.16.a'
27
- spec.add_development_dependency 'rake', '~> 10.0'
28
- spec.add_development_dependency 'rspec', '~> 3.0'
26
+ spec.add_development_dependency 'bundler'
27
+ spec.add_development_dependency 'rake'
28
+ spec.add_development_dependency 'rspec'
29
29
  spec.add_development_dependency 'simplecov'
30
30
  spec.add_development_dependency 'pry'
31
31
 
32
- spec.add_runtime_dependency 'colorize'
33
- spec.add_runtime_dependency 'activesupport', '>= 4.1'
34
- spec.add_runtime_dependency 'rails', '>= 4.1'
32
+ spec.add_dependency 'colorize'
33
+ spec.add_dependency 'activesupport', '>= 4.1'
34
+ spec.add_dependency 'rails', '>= 4.1'
35
35
 
36
36
  # spec.post_install_message = ""
37
37
  end
metadata CHANGED
@@ -1,57 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zero-rails_openapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - zhandao
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-12-17 00:00:00.000000000 Z
11
+ date: 2019-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 1.16.a
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 1.16.a
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '3.0'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '3.0'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: simplecov
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -143,13 +143,9 @@ files:
143
143
  - Rakefile
144
144
  - bin/console
145
145
  - bin/setup
146
- - documentation/examples/auto_gen_desc.rb
147
- - documentation/examples/auto_gen_doc.rb
148
- - documentation/examples/examples_controller.rb
149
- - documentation/examples/goods_doc.rb
150
- - documentation/examples/open_api.rb
151
- - documentation/examples/output_example.json
152
- - documentation/parameter.md
146
+ - examples/auto_gen_doc.rb
147
+ - examples/open_api.rb
148
+ - examples/output_example.json
153
149
  - lib/oas_objs/callback_obj.rb
154
150
  - lib/oas_objs/combined_schema.rb
155
151
  - lib/oas_objs/example_obj.rb
@@ -166,10 +162,10 @@ files:
166
162
  - lib/open_api/config_dsl.rb
167
163
  - lib/open_api/dsl.rb
168
164
  - lib/open_api/dsl/api.rb
169
- - lib/open_api/dsl/common_dsl.rb
170
165
  - lib/open_api/dsl/components.rb
171
166
  - lib/open_api/dsl/helpers.rb
172
- - lib/open_api/generator.rb
167
+ - lib/open_api/router.rb
168
+ - lib/open_api/support/tip.rb
173
169
  - lib/open_api/version.rb
174
170
  - zero-rails_openapi.gemspec
175
171
  homepage: https://github.com/zhandao/zero-rails_openapi
@@ -1,29 +0,0 @@
1
- class V1::GoodsDoc < BaseDoc
2
- api :index, 'GET list of goods.' do
3
- desc 'listing goods',
4
- view!: 'search view, allows:<br/>',
5
- # '1/ all goods (default):all<br/>' \
6
- # '2/ only online:online<br/>' \
7
- # '3/ only offline:offline<br/>' \
8
- # '4/ expensive goods:expensive<br/>' \
9
- # '5/ cheap goods:cheap<br/>',
10
- search_type!: 'search field, allows:<br/>'
11
- # '1/ name<br/>2/ creator,<br/>3/ category<br/>4/ price<br/>'
12
-
13
- # Instead of:
14
- # query :view, String, enum: %w[ all online offline expensive cheap ]
15
- query :view, String, enum!: {
16
- 'all goods (default)': :all,
17
- 'only online': :online,
18
- 'only offline': :offline,
19
- 'expensive goods': :expensive,
20
- 'cheap goods': :cheap
21
- }
22
- query :search_type, String, enum: %w[ name creator category price ]
23
- # Same as:
24
- # query :search_type, String, desc!: 'search field, allows:<br/>',
25
- # enum: %w[ name creator category price ]
26
-
27
- # TODO: Support `desc: '', auto_desc: true or %i[ enum must_be ]`
28
- end
29
- end
@@ -1,60 +0,0 @@
1
- class Api::V1::ExamplesController < Api::V1::BaseController
2
- doc_tag name: 'ExampleTagName', desc: 'ExamplesController\'s APIs'
3
-
4
- components do
5
- schema :DogSchema => [ String, dft: 'doge' ]
6
- schema :PetSchema => [ not: [ Integer, Boolean ] ]
7
- query! :UidQuery => [ :uid, String, desc: 'user uid' ]
8
- path! :IdPath => [ :id, Integer, desc: 'product id' ]
9
- resp :BadRqResp => [ 'bad request', :json ]
10
- end
11
-
12
-
13
- api_dry %i[ index show ], 'common parts of :index and :show' do
14
- header! :Token, String
15
- response 1000, 'data export', :pdf, type: File
16
- end
17
-
18
-
19
- api :index, 'GET examples', use: :Token do
20
- this_api_is_invalid! 'do not use!'
21
- desc '**GET** list of examples,<br/>and get the status 200.',
22
- id: 'user id',
23
- email: 'email addr\'s desc'
24
- email = 'a@b.c'
25
-
26
- query! :count, Integer, enum: 0..5, length: [1, 2], pattern: /^[0-9]$/, range: { gt: 0, le: 5 }
27
- query! :done, Boolean, must_be: false, default: true, desc: 'must be false'
28
- query :email, String, lth: :ge_3, default: email # is_a: :email
29
- file :pdf, 'upload a file: the media type should be application/pdf'
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
-
37
- response :success, 'success response', :json#, data: :Pet
38
- security :Token
39
-
40
- resp 200, '', :json, data: {
41
- a: String
42
- }
43
- end
44
-
45
-
46
- api :show, skip: :Token do
47
- param_ref :IdPath, :UidQuery
48
- response_ref 400 => :BadRqResp
49
- end
50
-
51
-
52
- api :create do
53
- form! data: {
54
- :name! => String, # <= schema_type is `String`
55
- :password! => { type: String, pattern: /[0-9]{6,10}/, desc: 'password' },
56
- # optional
57
- :remarks => { type: String, desc: 'remarks' }, # <= schema_type is `String`, and schema_info is { desc: '..' }
58
- }
59
- end
60
- end