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.
- checksums.yaml +4 -4
- data/README.md +404 -434
- data/README_zh.md +5 -5
- data/{documentation/examples → examples}/auto_gen_doc.rb +0 -0
- data/{documentation/examples → examples}/open_api.rb +11 -15
- data/{documentation/examples → examples}/output_example.json +0 -0
- data/lib/oas_objs/callback_obj.rb +5 -10
- data/lib/oas_objs/combined_schema.rb +16 -12
- data/lib/oas_objs/example_obj.rb +9 -7
- data/lib/oas_objs/helpers.rb +4 -20
- data/lib/oas_objs/media_type_obj.rb +10 -10
- data/lib/oas_objs/param_obj.rb +8 -11
- data/lib/oas_objs/ref_obj.rb +2 -0
- data/lib/oas_objs/request_body_obj.rb +6 -2
- data/lib/oas_objs/response_obj.rb +5 -2
- data/lib/oas_objs/schema_obj.rb +48 -67
- data/lib/oas_objs/schema_obj_helpers.rb +11 -35
- data/lib/open_api.rb +57 -6
- data/lib/open_api/config.rb +10 -37
- data/lib/open_api/config_dsl.rb +2 -0
- data/lib/open_api/dsl.rb +36 -49
- data/lib/open_api/dsl/api.rb +63 -68
- data/lib/open_api/dsl/components.rb +34 -24
- data/lib/open_api/dsl/helpers.rb +26 -52
- data/lib/open_api/router.rb +50 -0
- data/lib/open_api/support/tip.rb +26 -0
- data/lib/open_api/version.rb +3 -1
- data/zero-rails_openapi.gemspec +6 -6
- metadata +19 -23
- data/documentation/examples/auto_gen_desc.rb +0 -29
- data/documentation/examples/examples_controller.rb +0 -60
- data/documentation/examples/goods_doc.rb +0 -52
- data/documentation/parameter.md +0 -69
- data/lib/open_api/dsl/common_dsl.rb +0 -39
- data/lib/open_api/generator.rb +0 -116
@@ -1,52 +0,0 @@
|
|
1
|
-
class V2::GoodsDoc < ApiDoc
|
2
|
-
SCHEMA_DRY = { a: 1, b: 2 }
|
3
|
-
|
4
|
-
# skip: [ 'Token' ] do # you can also skip parameters
|
5
|
-
api :index, 'GET list of goods.', use: [ 'Token', :page, :rows ] do # use parameters write in AutoGenDoc#api_dry
|
6
|
-
desc 'listing goods',
|
7
|
-
view!: 'search view, allows:<br/>',
|
8
|
-
search_type!: 'search field, allows:<br/>'
|
9
|
-
|
10
|
-
# Single `query`
|
11
|
-
query :view, String, enum!: {
|
12
|
-
'all goods (default)': :all,
|
13
|
-
'only online': :online,
|
14
|
-
'only offline': :offline,
|
15
|
-
'expensive goods': :expensive,
|
16
|
-
'cheap goods': :cheap,
|
17
|
-
}, **SCHEMA_DRY # >>> Here is a little trick! <<<
|
18
|
-
# Batch `query`
|
19
|
-
do_query by: {
|
20
|
-
:search_type => { type: String, enum: %w[ name creator category price ] },
|
21
|
-
:value => String,
|
22
|
-
:export => { type: Boolean, desc: 'export as Excel format', examples: {
|
23
|
-
:right_input => true,
|
24
|
-
:wrong_input => 'wrong input'
|
25
|
-
}}
|
26
|
-
}
|
27
|
-
end
|
28
|
-
|
29
|
-
|
30
|
-
api :create, 'POST create a good', use: 'Token' do
|
31
|
-
form! data: {
|
32
|
-
:name! => { type: String, desc: 'good\'s name' },
|
33
|
-
:category_id! => { type: Integer, desc: 'sub_category\'s id', npmt: true, range: { ge: 1 }, as: :cate },
|
34
|
-
:price! => { type: Float, desc: 'good\'s price', range: { ge: 0 } },
|
35
|
-
# -- optional
|
36
|
-
:is_online => { type: Boolean, desc: 'it\'s online?' },
|
37
|
-
:remarks => { type: String, desc: 'remarks' },
|
38
|
-
:pic_path => { type: String, desc: 'picture url', is: :url },
|
39
|
-
},
|
40
|
-
exp_by: %i[ name category_id price ],
|
41
|
-
examples: {
|
42
|
-
:right_input => [ 'good1', 6, 5.7 ],
|
43
|
-
:wrong_input => [ 'good2', 0, -1 ]
|
44
|
-
}
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
|
-
api :show, 'GET the specified Good.', use: [ 'Token', :id ]
|
49
|
-
|
50
|
-
|
51
|
-
api :destroy, 'DELETE the specified Good.', use: [ 'Token', :id ]
|
52
|
-
end
|
data/documentation/parameter.md
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
### More Explanation for `param` and `schema_info`
|
2
|
-
|
3
|
-
#### param_type (param_location)
|
4
|
-
OpenAPI 3.0 distinguishes between the following parameter types based on the parameter location:
|
5
|
-
**header, path, query, cookie**. [more](https://swagger.io/docs/specification/describing-parameters/)
|
6
|
-
|
7
|
-
#### name (param_name)
|
8
|
-
The name of parameter. It can be Symbol or String.
|
9
|
-
|
10
|
-
If param_type is :path, it must correspond to the associated path segment form
|
11
|
-
the routing path, for example: if the API path is `/good/:id`, you have to declare a path parameter with name `id` to it.
|
12
|
-
|
13
|
-
#### type (schema_type)
|
14
|
-
Parameter's (schema) type. We call it `schema_type` because it is inside SchemaObj.
|
15
|
-
|
16
|
-
Support all [data types](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#dataTypes) defined in OAS.
|
17
|
-
|
18
|
-
In addition, you can use `format` in schema_info to define in fine detail the data type being used, like:
|
19
|
-
int32, float, date ...
|
20
|
-
All the types you can use as following:
|
21
|
-
- **String, 'binary', 'base64'**
|
22
|
-
- **Integer, Long, 'int32', 'int64', Float, Double**
|
23
|
-
- **File** (it will be converted to `{ type: 'string', format: Config.file_format }`)
|
24
|
-
- **Date, DateTime**
|
25
|
-
- **Boolean**
|
26
|
-
- **Array**: `Array[String]` or `[String]`
|
27
|
-
- Nested Array: `[[[Integer]]]`
|
28
|
-
- **Object**: you can use just `Object`, or use a hash to declare its properties `{ id!: Integer, name: String }`
|
29
|
-
(`!` bang key means it is required).
|
30
|
-
- Nested Object: `{ id!: Integer, name: { first: String, last: String } }`
|
31
|
-
- Nested Array and Object: `[[{ id!: Integer, name: { first: String, last: String } }]]`
|
32
|
-
- **:ComponentKey**: pass **Symbol** value to type will generate a Schema Reference Object link
|
33
|
-
to the component correspond to ComponentKey, like: :IdPath, :NameQuery
|
34
|
-
|
35
|
-
You can use `Object.const_set()` to define a constant that does not exist, but note that
|
36
|
-
the value you set could not be a Symbol (it will be explained as a Ref Object), should be a String.
|
37
|
-
|
38
|
-
#### required
|
39
|
-
:opt or :req
|
40
|
-
|
41
|
-
#### Schema Hash
|
42
|
-
|
43
|
-
The [[schema]](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#schemaObject) defining the type used for the parameter.
|
44
|
-
schema_info(optional) will be used to generate Schema Object inside Parameter Object.
|
45
|
-
[source code](https://github.com/zhandao/zero-rails_openapi/blob/master/lib/oas_objs/schema_obj.rb)
|
46
|
-
You can set the schema by following keys (all are optional), the words in parentheses are available aliases of the keys:
|
47
|
-
- **enum (values, allowable_values)**
|
48
|
-
Must be Array or Range(will be converted to Array)
|
49
|
-
- **must_be (value, allowable_value)**
|
50
|
-
Single value, could be a String, Array ...
|
51
|
-
- **range (number_range)**
|
52
|
-
Allow value in this continuous range. Set this field like this: `{ gt: 0, le: 5 }`
|
53
|
-
- **length (lth)**
|
54
|
-
Must be an Integer, Integer Array, Integer Range, or the following format Symbol: `:gt_`, `:ge_`, `:lt_`, `:le_`, examples: :ge_5 means "greater than or equal 5"; :lt_9 means "lower than 9".
|
55
|
-
- **format (fmt)**
|
56
|
-
- **is (is_a)**
|
57
|
-
1. It's not in OAS, just an addition field for better express.You can see it as `format`, but in fact they are quite different.
|
58
|
-
2. Look at this example: the `format` is set to "int32", but you also want to express that this
|
59
|
-
schema is an "id" format —— this cannot be expressed in the current OAS version.
|
60
|
-
3. So I suggest that the value of `format` should related to data type, `is` should be an entity.
|
61
|
-
4. ZRO defaults to identify whether `is` patterns matched the name, then automatically generate `is`.
|
62
|
-
for example the parameter name "user_email" will generate "is: email". Default `is` options are:
|
63
|
-
[email phone password uuid uri url time date], to overwrite it you can set it in initializer `c.is_options = %w[]`.
|
64
|
-
5. If type is Object, for describing each property's schema, the only way is use ref type, like: `{ id: :Id, name: :Name }`
|
65
|
-
- **pattern (regexp, pr, reg)**
|
66
|
-
Regexp or Time Format
|
67
|
-
- **default (dft, default_value)**
|
68
|
-
- **as** # TODO
|
69
|
-
- **example & examples** # TODO
|
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'oas_objs/schema_obj'
|
2
|
-
require 'oas_objs/combined_schema'
|
3
|
-
require 'oas_objs/param_obj'
|
4
|
-
require 'oas_objs/response_obj'
|
5
|
-
require 'oas_objs/request_body_obj'
|
6
|
-
require 'oas_objs/ref_obj'
|
7
|
-
require 'oas_objs/example_obj'
|
8
|
-
require 'oas_objs/callback_obj'
|
9
|
-
require 'open_api/dsl/helpers'
|
10
|
-
|
11
|
-
module OpenApi
|
12
|
-
module DSL
|
13
|
-
module CommonDSL
|
14
|
-
%i[ header header! path path! query query! cookie cookie! ].each do |param_type|
|
15
|
-
define_method param_type do |*args|
|
16
|
-
@necessity = param_type['!'] ? :req : :opt
|
17
|
-
@param_type = param_type.to_s.delete('!') # OR: caller[0][/`.*'/][1..-2].to_sym
|
18
|
-
_param_agent *args
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
%i[ body body! ].each do |method|
|
23
|
-
define_method method do |*args|
|
24
|
-
@necessity = method['!'] ? :req : :opt
|
25
|
-
_request_body_agent *args
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# `code`: when defining components, `code` means `component_key`
|
30
|
-
def response code, desc, media_type = nil, data: { }, type: nil
|
31
|
-
self[:responses][code] = ResponseObj.new(desc) unless (self[:responses] ||= { })[code].is_a?(ResponseObj)
|
32
|
-
self[:responses][code].add_or_fusion(desc, media_type, { data: type || data })
|
33
|
-
end
|
34
|
-
|
35
|
-
alias_method :resp, :response
|
36
|
-
alias_method :error, :response
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
data/lib/open_api/generator.rb
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
require 'open_api/config'
|
2
|
-
require 'colorize'
|
3
|
-
|
4
|
-
module OpenApi
|
5
|
-
module Generator
|
6
|
-
module_function
|
7
|
-
|
8
|
-
def self.included(base)
|
9
|
-
base.extend ClassMethods
|
10
|
-
end
|
11
|
-
|
12
|
-
module ClassMethods
|
13
|
-
def generate_docs(doc_name = nil)
|
14
|
-
return puts ' ZRO'.red + ' No documents have been configured!' if Config.docs.keys.blank?
|
15
|
-
|
16
|
-
# TODO
|
17
|
-
# :nocov:
|
18
|
-
Dir['./app/controllers/**/*_controller.rb'].each do |file|
|
19
|
-
file.sub('./app/controllers/', '').sub('.rb', '').camelize.constantize
|
20
|
-
end
|
21
|
-
# :nocov:
|
22
|
-
Dir[*Array(Config.doc_location)].each { |file| require file }
|
23
|
-
(doc_name || Config.docs.keys).map { |name| { name => generate_doc(name) } }.reduce({ }, :merge!)
|
24
|
-
end
|
25
|
-
|
26
|
-
def generate_doc(doc_name)
|
27
|
-
settings = Config.docs[doc_name]
|
28
|
-
doc = { openapi: '3.0.0', **settings.slice(:info, :servers) }.merge!(
|
29
|
-
security: settings[:global_security], tags: [ ], paths: { },
|
30
|
-
components: {
|
31
|
-
securitySchemes: settings[:securitySchemes] || { },
|
32
|
-
schemas: { }, parameters: { }, requestBodies: { }
|
33
|
-
}
|
34
|
-
)
|
35
|
-
|
36
|
-
[*(bdc = settings[:base_doc_classes]), *bdc.flat_map(&:descendants)].each do |ctrl|
|
37
|
-
doc_info = ctrl.instance_variable_get('@doc_info')
|
38
|
-
next if doc_info.nil?
|
39
|
-
|
40
|
-
doc[:paths].merge!(ctrl.instance_variable_get('@api_info') || { })
|
41
|
-
doc[:tags] << doc_info[:tag]
|
42
|
-
doc[:components].deep_merge!(doc_info[:components] || { })
|
43
|
-
OpenApi.routes_index[ctrl.instance_variable_get('@route_base')] = doc_name
|
44
|
-
end
|
45
|
-
|
46
|
-
doc[:components].delete_if { |_, v| v.blank? }
|
47
|
-
doc[:tags] = doc[:tags].sort { |a, b| a[:name] <=> b[:name] }
|
48
|
-
doc[:paths] = doc[:paths].sort.to_h
|
49
|
-
|
50
|
-
OpenApi.docs[doc_name] = doc#.delete_if { |_, v| v.blank? }
|
51
|
-
end
|
52
|
-
|
53
|
-
def write_docs(generate_files: true)
|
54
|
-
docs = generate_docs
|
55
|
-
puts ' ZRO loaded.'.green if ENV['RAILS_ENV']
|
56
|
-
return unless generate_files
|
57
|
-
# :nocov:
|
58
|
-
output_path = Config.file_output_path
|
59
|
-
FileUtils.mkdir_p output_path
|
60
|
-
max_length = docs.keys.map(&:size).sort.last
|
61
|
-
docs.each do |doc_name, doc|
|
62
|
-
puts ' ZRO'.green + " `#{doc_name.to_s.rjust(max_length)}.json` has been generated."
|
63
|
-
File.open("#{output_path}/#{doc_name}.json", 'w') { |file| file.write JSON.pretty_generate doc }
|
64
|
-
end
|
65
|
-
# :nocov:
|
66
|
-
end
|
67
|
-
end
|
68
|
-
# end of module
|
69
|
-
|
70
|
-
def routes
|
71
|
-
@routes ||=
|
72
|
-
if (file = Config.rails_routes_file)
|
73
|
-
File.read(file)
|
74
|
-
else
|
75
|
-
# :nocov:
|
76
|
-
# ref https://github.com/rails/rails/blob/master/railties/lib/rails/tasks/routes.rake
|
77
|
-
require './config/routes'
|
78
|
-
all_routes = Rails.application.routes.routes
|
79
|
-
require 'action_dispatch/routing/inspector'
|
80
|
-
inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
|
81
|
-
inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, nil)
|
82
|
-
# :nocov:
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def routes_list
|
87
|
-
@routes_list ||= routes.split("\n").drop(1).map do |line|
|
88
|
-
next unless line['#']
|
89
|
-
infos = line.match(/[A-Z|].*/).to_s.split(' ') # => [GET, /api/v1/examples/:id, api/v1/examples#index]
|
90
|
-
|
91
|
-
{
|
92
|
-
http_verb: infos[0].downcase, # => "get" / "get|post"
|
93
|
-
path: infos[1][0..-11].split('/').map do |item|
|
94
|
-
item[':'] ? "{#{item[1..-1]}}" : item
|
95
|
-
end.join('/'), # => "/api/v1/examples/{id}"
|
96
|
-
action_path: infos[2] # => "api/v1/examples#index"
|
97
|
-
} rescue next
|
98
|
-
end.compact.group_by { |api| api[:action_path].split('#').first } # => { "api/v1/examples" => [..] }, group by paths
|
99
|
-
end
|
100
|
-
|
101
|
-
def get_actions_by_route_base(route_base)
|
102
|
-
routes_list[route_base]&.map do |action_info|
|
103
|
-
action_info[:action_path].split('#').last
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def find_path_httpverb_by(route_base, action)
|
108
|
-
routes_list[route_base]&.map do |action_info|
|
109
|
-
if action_info[:action_path].split('#').last == action.to_s
|
110
|
-
return [ action_info[:path], action_info[:http_verb].split('|').first ]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
nil
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|