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.
@@ -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