meta-api 0.0.9 → 0.1.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/lib/meta/application/metadata.rb +7 -14
- data/lib/meta/application/responses.rb +32 -0
- data/lib/meta/application/route.rb +1 -1
- data/lib/meta/config.rb +0 -1
- data/lib/meta/entity.rb +4 -36
- data/lib/meta/json_schema/builders/object_schema_builder.rb +112 -46
- data/lib/meta/json_schema/builders/schema_builder_tool.rb +2 -6
- data/lib/meta/json_schema/schemas/array_schema.rb +12 -1
- data/lib/meta/json_schema/schemas/base_schema.rb +15 -7
- data/lib/meta/json_schema/schemas/named_properties.rb +37 -0
- data/lib/meta/json_schema/schemas/object_schema.rb +42 -24
- data/lib/meta/json_schema/schemas/properties.rb +20 -3
- data/lib/meta/json_schema/schemas/ref_schema.rb +39 -9
- data/lib/meta/json_schema/schemas/scoping_schema.rb +18 -19
- data/lib/meta/json_schema/schemas/staging_schema.rb +10 -9
- data/lib/meta/json_schema/support/json_object.rb +37 -0
- data/lib/meta/json_schema/support/scope_matcher.rb +29 -0
- data/lib/meta/json_schema/support/type_converter.rb +3 -24
- data/lib/meta/route_dsl/meta_builder.rb +10 -1
- data/lib/meta/swagger_doc.rb +1 -24
- data/lib/meta/utils/kwargs/builder.rb +5 -5
- data/lib/meta/utils/route_dsl_builders.rb +1 -1
- data/meta-api.gemspec +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5f1c4106f4106c1a3e69249e35f4efc02d4ca5d5981d4e32442c380f4228556
|
4
|
+
data.tar.gz: 7dc942b0a36759a0953a255b18a7c6c6312159365e9b829b48f223d0d83cd685
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 275731f780c2e5eb3dcae314a5b7832504f053092eee8aa3eb1e8b85d7cce0f4fec33bf8f46626cb307d6c7c98e6ff0ee13bf100d6a4610099323d069968d7ae
|
7
|
+
data.tar.gz: f3f21b6e62503946e26471f9199809247d0ee3fafa13a2190e57aadb038b47cb19757ed08be4f2054e2b0151c2ed9f1cf4d737c007e133479395aa936d2a338b
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# 更新日志
|
2
2
|
|
3
|
+
## 0.1.1(2024 年 6 月 1 日)
|
4
|
+
|
5
|
+
1. 添加 `Meta::Entity.with_common_options` 方法,用于更有效地组织字段。
|
6
|
+
2. 临时性地添加 `Meta::Entity.merge` 方法,作为合并其他的实体的暂时性实现。
|
7
|
+
3. scope 分为全局 scope 和局部 scope.
|
8
|
+
|
9
|
+
## 0.1.0(2023 年 8 月 5 日)
|
10
|
+
|
11
|
+
1. 删除 `on:` 选项。
|
12
|
+
2. `type_conversion` 为 `false` 时不影响对象和数组类型的转化。
|
13
|
+
3. 修复 `ref:` 嵌套造成的文档问题。
|
14
|
+
4. 将 HTTP Method 的 scope 添加 `$` 符号前缀,如 `$get`、`$post` 等。
|
15
|
+
5. `Meta.config` 去掉了 `default_locked_scope` 的配置项。
|
16
|
+
|
3
17
|
## 0.0.9(2023 年 7 月 22 日)
|
4
18
|
|
5
19
|
1. JsonSchema 添加 before:、after: 选项,用于在过滤前后执行一些操作。
|
@@ -1,13 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'parameters'
|
4
|
+
require_relative 'responses'
|
4
5
|
|
5
6
|
module Meta
|
6
7
|
class Metadata
|
7
8
|
module ExecutionMethods
|
8
9
|
def parse_parameters(execution)
|
9
10
|
parameters.filter(execution.request)
|
10
|
-
|
11
|
+
rescue JsonSchema::ValidationErrors => e
|
12
|
+
raise Errors::ParameterInvalid.new(e.errors)
|
11
13
|
end
|
12
14
|
|
13
15
|
def parse_request_body(execution, discard_missing: false)
|
@@ -16,7 +18,7 @@ module Meta
|
|
16
18
|
execution.params(:raw),
|
17
19
|
**Meta.config.json_schema_user_options,
|
18
20
|
**Meta.config.json_schema_param_stage_user_options,
|
19
|
-
**{ execution: execution, stage: :param, scope: @scope.concat([method]), discard_missing: discard_missing }.compact
|
21
|
+
**{ execution: execution, stage: :param, scope: @scope.concat(["$#{method}"]), discard_missing: discard_missing }.compact
|
20
22
|
)
|
21
23
|
rescue JsonSchema::ValidationErrors => e
|
22
24
|
raise Errors::ParameterInvalid.new(e.errors)
|
@@ -70,7 +72,7 @@ module Meta
|
|
70
72
|
@tags = tags
|
71
73
|
@parameters = parameters.is_a?(Parameters) ? parameters : Parameters.new(parameters)
|
72
74
|
@request_body = request_body
|
73
|
-
@responses = responses
|
75
|
+
@responses = responses.is_a?(Responses) ? responses : Responses.new(responses)
|
74
76
|
@scope = scope
|
75
77
|
end
|
76
78
|
|
@@ -88,7 +90,7 @@ module Meta
|
|
88
90
|
operation_object[:parameters] = parameters.to_swagger_doc
|
89
91
|
|
90
92
|
if request_body
|
91
|
-
schema = request_body.to_schema_doc(stage: :param, scope: self.scope + scope,
|
93
|
+
schema = request_body.to_schema_doc(stage: :param, scope: self.scope + scope, schema_docs_mapping: schemas)
|
92
94
|
if schema || true
|
93
95
|
operation_object[:requestBody] = {
|
94
96
|
content: {
|
@@ -100,16 +102,7 @@ module Meta
|
|
100
102
|
end
|
101
103
|
end
|
102
104
|
|
103
|
-
operation_object[:responses] = responses.
|
104
|
-
{
|
105
|
-
description: '', # description 属性必须存在
|
106
|
-
content: schema ? {
|
107
|
-
'application/json' => {
|
108
|
-
schema: schema.to_schema_doc(stage: :render, scope: self.scope + scope, schemas: schemas)
|
109
|
-
}
|
110
|
-
} : nil
|
111
|
-
}.compact
|
112
|
-
end unless responses.empty?
|
105
|
+
operation_object[:responses] = responses.to_swagger_doc(schemas, scope: self.scope + scope)
|
113
106
|
|
114
107
|
operation_object.compact
|
115
108
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Meta
|
4
|
+
class Responses
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader :responses
|
8
|
+
|
9
|
+
def initialize(responses = {})
|
10
|
+
@responses = responses || {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def_delegators :@responses, :[], :empty?, :length, :keys
|
14
|
+
|
15
|
+
def to_swagger_doc(schemas, scope:)
|
16
|
+
if responses.empty?
|
17
|
+
{ '200' => { description: '' } }
|
18
|
+
else
|
19
|
+
responses.transform_values do |schema|
|
20
|
+
{
|
21
|
+
description: '', # description 属性必须存在
|
22
|
+
content: schema ? {
|
23
|
+
'application/json' => {
|
24
|
+
schema: schema.to_schema_doc(stage: :render, scope: scope, schema_docs_mapping: schemas)
|
25
|
+
}
|
26
|
+
} : nil
|
27
|
+
}.compact
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/meta/config.rb
CHANGED
data/lib/meta/entity.rb
CHANGED
@@ -14,45 +14,13 @@ module Meta
|
|
14
14
|
def inherited(base)
|
15
15
|
base.instance_eval do
|
16
16
|
@schema_builder = JsonSchema::ObjectSchemaBuilder.new
|
17
|
-
@schema_builder.schema_name(
|
18
|
-
generate_schema_name(locked_scope, stage)
|
19
|
-
})
|
17
|
+
@schema_builder.schema_name(self.name) if self.name
|
20
18
|
end
|
21
19
|
end
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
def method_missing(method, *args)
|
26
|
-
if method =~ /^lock_(\w+)$/
|
27
|
-
schema_builder.send(method, *args)
|
28
|
-
else
|
29
|
-
super
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def generate_schema_name(stage, locked_scopes)
|
36
|
-
# 匿名类不考虑自动生成名称
|
37
|
-
return nil unless self.name
|
38
|
-
|
39
|
-
schema_name = self.name.gsub('::', '_')
|
40
|
-
schema_name = schema_name.delete_suffix('Entity') unless schema_name == 'Entity'
|
41
|
-
|
42
|
-
# 先考虑 stage
|
43
|
-
case stage
|
44
|
-
when :param
|
45
|
-
schema_name += 'Params'
|
46
|
-
when :render
|
47
|
-
schema_name += 'Entity'
|
48
|
-
end
|
49
|
-
|
50
|
-
# 再考虑 locked_scope
|
51
|
-
scope_suffix = locked_scopes.join('_')
|
52
|
-
schema_name = "#{schema_name}_#{scope_suffix}" unless scope_suffix.empty?
|
53
|
-
|
54
|
-
schema_name
|
21
|
+
def method_missing(method, *args, **kwargs, &)
|
22
|
+
schema_builder.send(method, *args, **kwargs, &)
|
55
23
|
end
|
56
24
|
end
|
57
25
|
end
|
58
|
-
end
|
26
|
+
end
|
@@ -5,10 +5,75 @@ require_relative '../schemas/properties'
|
|
5
5
|
module Meta
|
6
6
|
module JsonSchema
|
7
7
|
class ObjectSchemaBuilder
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
module LockedMethodAlias
|
11
|
+
# 我在这里说明一下 lock_scope 的逻辑。
|
12
|
+
# 1. lock_scope 实际上是将 scope 传递到当前的 ObjectSchema 和它的子 Schema 中。
|
13
|
+
# 2. lock_scope 会叠加,也就是如果子 schema 也有 lock_scope,那么子 Schema 会将两个 Schema 合并起来。
|
14
|
+
# 3. 调用 filter(scope:) 和 to_schema_doc(scope:) 时,可以传递 scope 参数,这个 scope 遇到 lock_scope 时会合并。
|
15
|
+
# 4. 这也就是说,在路由级别定义的 scope 宏会传递到下面的 Schema 中去。
|
16
|
+
def add_scope(scope)
|
17
|
+
lock_scope(scope)
|
18
|
+
end
|
19
|
+
|
20
|
+
def lock(key, value)
|
21
|
+
locked(key => value)
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(method, *args)
|
25
|
+
if method =~ /^lock_(\w+)$/
|
26
|
+
key = Regexp.last_match(1)
|
27
|
+
lock(key.to_sym, *args)
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Locked
|
35
|
+
attr_reader :object_schema_builder, :locked_options
|
36
|
+
|
37
|
+
# locked_options 是用户传递的参数,这个参数会被合并到 object_schema_builder 的 locked_options 中。
|
38
|
+
def initialize(builder, scope: nil, discard_missing: nil, exclude: nil)
|
39
|
+
@object_schema_builder = builder
|
40
|
+
@locked_options = ObjectSchema::USER_OPTIONS_CHECKER.check({ scope: scope, discard_missing: discard_missing, exclude: exclude }.compact)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_schema
|
44
|
+
object_schema_builder.to_schema(locked_options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def locked(options)
|
48
|
+
options = ObjectSchema::USER_OPTIONS_CHECKER.check(options)
|
49
|
+
options = ObjectSchema.merge_user_options(locked_options, options)
|
50
|
+
Locked.new(self.object_schema_builder, **options)
|
51
|
+
end
|
52
|
+
include LockedMethodAlias
|
53
|
+
end
|
54
|
+
|
55
|
+
class WithCommonOptions
|
56
|
+
attr_reader :object_schema_builder, :common_options
|
57
|
+
|
58
|
+
def initialize(builder, common_options, &)
|
59
|
+
@object_schema_builder = builder
|
60
|
+
@common_options = common_options
|
61
|
+
|
62
|
+
instance_exec(&) if block_given?
|
63
|
+
end
|
64
|
+
|
65
|
+
def property(name, options = {}, &block)
|
66
|
+
options = common_options.merge(options)
|
67
|
+
object_schema_builder.property(name, options, &block)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
attr_reader :properties
|
72
|
+
|
8
73
|
def initialize(options = {}, &)
|
9
74
|
raise 'type 选项必须是 object' if !options[:type].nil? && options[:type] != 'object'
|
10
75
|
|
11
|
-
@properties = {}
|
76
|
+
@properties = {} # 所有的属性已经生成
|
12
77
|
@required = []
|
13
78
|
@validations = {}
|
14
79
|
|
@@ -23,22 +88,12 @@ module Meta
|
|
23
88
|
instance_exec(&) if block_given?
|
24
89
|
end
|
25
90
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
# 三、可以传递一个字符串。
|
31
|
-
def schema_name(schema_name_resolver)
|
32
|
-
if schema_name_resolver.is_a?(Proc)
|
33
|
-
@schema_name_resolver = schema_name_resolver
|
34
|
-
elsif schema_name_resolver.is_a?(Hash)
|
35
|
-
@schema_name_resolver = proc { |stage, locked_scopes| schema_name_resolver[stage] }
|
36
|
-
elsif schema_name_resolver.is_a?(String)
|
37
|
-
@schema_name_resolver = proc { |stage, locked_scopes| schema_name_resolver }
|
38
|
-
elsif schema_name_resolver.nil?
|
39
|
-
@schema_name_resolver = proc { nil }
|
91
|
+
def schema_name(schema_base_name = nil)
|
92
|
+
if schema_base_name
|
93
|
+
raise TypeError, "schema_base_name 必须是一个 String,当前是:#{schema_base_name.class}" unless schema_base_name.is_a?(String)
|
94
|
+
@schema_name = schema_base_name
|
40
95
|
else
|
41
|
-
|
96
|
+
@schema_name
|
42
97
|
end
|
43
98
|
end
|
44
99
|
|
@@ -55,52 +110,63 @@ module Meta
|
|
55
110
|
instance_exec(&proc)
|
56
111
|
end
|
57
112
|
|
58
|
-
def
|
59
|
-
|
113
|
+
def with_common_options(common_options, &block)
|
114
|
+
WithCommonOptions.new(self, common_options, &block)
|
60
115
|
end
|
61
116
|
|
62
|
-
def
|
63
|
-
|
117
|
+
def scope(scope, options = {}, &)
|
118
|
+
with_common_options(**options, scope: scope, &)
|
64
119
|
end
|
65
120
|
|
66
|
-
def
|
67
|
-
|
121
|
+
def params(options = {}, &block)
|
122
|
+
with_common_options(**options, render: false, &block)
|
68
123
|
end
|
69
124
|
|
70
|
-
|
125
|
+
def render(options = {}, &block)
|
126
|
+
with_common_options(**options, params: false, &block)
|
127
|
+
end
|
71
128
|
|
72
|
-
def
|
73
|
-
|
129
|
+
def merge(schema_builder)
|
130
|
+
schema_builder = schema_builder.schema_builder if schema_builder.respond_to?(:schema_builder)
|
131
|
+
|
132
|
+
@properties.merge!(schema_builder.properties)
|
74
133
|
end
|
75
134
|
|
76
|
-
def
|
77
|
-
|
135
|
+
def to_schema(locked_options = nil)
|
136
|
+
properties = @schema_name ? NamedProperties.new(@properties, @schema_name) : Properties.new(@properties)
|
137
|
+
ObjectSchema.new(properties: properties, options: @options, locked_options: locked_options)
|
78
138
|
end
|
79
139
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
140
|
+
def locked(options)
|
141
|
+
defined_scopes_mapping = {}
|
142
|
+
# TODO: 将 properties 搞成 Properties 可以吗?
|
143
|
+
defined_scopes = properties.map do |key, property|
|
144
|
+
property.defined_scopes(stage: :param, defined_scopes_mapping: defined_scopes_mapping)
|
145
|
+
end.flatten.uniq
|
146
|
+
|
147
|
+
user_scopes = options[:scope] || []
|
148
|
+
user_scopes = [user_scopes] unless user_scopes.is_a?(Array)
|
149
|
+
|
150
|
+
# 判断 user_scopes 中提供的局部 scope 是否在 defined_scopes 中
|
151
|
+
local_user_scopes = user_scopes.reject { |scope| scope.start_with?('$') }
|
152
|
+
if (local_user_scopes - defined_scopes).any?
|
153
|
+
extra_scopes = local_user_scopes - defined_scopes
|
154
|
+
raise ArgumentError, "scope #{extra_scopes.join(',')} 未在实体中定义"
|
86
155
|
end
|
156
|
+
|
157
|
+
Locked.new(self, **options)
|
87
158
|
end
|
159
|
+
include LockedMethodAlias
|
88
160
|
|
89
|
-
|
90
|
-
attr_reader :builder, :locked_options
|
161
|
+
private
|
91
162
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
end
|
163
|
+
def apply_array_scope?(options, block)
|
164
|
+
options[:type] == 'array' && (options[:items] || block)
|
165
|
+
end
|
96
166
|
|
97
|
-
|
98
|
-
|
99
|
-
# 信息,故而 schema_name 相关的影响就消失不见了。
|
100
|
-
def to_schema
|
101
|
-
builder.to_schema(locked_options)
|
102
|
-
end
|
167
|
+
def apply_object_scope?(options, block)
|
168
|
+
(options[:type] == 'object' || block) && (options[:properties] || block)
|
103
169
|
end
|
104
170
|
end
|
105
171
|
end
|
106
|
-
end
|
172
|
+
end
|
@@ -14,11 +14,7 @@ module Meta
|
|
14
14
|
permit_extras true
|
15
15
|
|
16
16
|
key :ref, alias_names: [:using], normalizer: ->(entity) {
|
17
|
-
|
18
|
-
entity.locked(scope: Meta.config.default_locked_scope)
|
19
|
-
else
|
20
|
-
entity
|
21
|
-
end
|
17
|
+
entity
|
22
18
|
}
|
23
19
|
key :dynamic_ref, alias_names: [:dynamic_using], normalizer: ->(value) { value.is_a?(Proc) ? { resolve: value } : value }
|
24
20
|
end
|
@@ -34,7 +30,7 @@ module Meta
|
|
34
30
|
elsif apply_object_schema?(options, block)
|
35
31
|
ObjectSchemaBuilder.new(options, &block).to_schema
|
36
32
|
else
|
37
|
-
|
33
|
+
BaseSchema.new(options)
|
38
34
|
end
|
39
35
|
end
|
40
36
|
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module Meta
|
4
4
|
module JsonSchema
|
5
5
|
class ArraySchema < BaseSchema
|
6
|
+
extend Forwardable
|
7
|
+
|
6
8
|
attr_reader :items
|
7
9
|
|
8
10
|
def initialize(items, options = {})
|
@@ -22,10 +24,19 @@ module Meta
|
|
22
24
|
schema
|
23
25
|
end
|
24
26
|
|
27
|
+
def_delegator :@items, :defined_scopes
|
28
|
+
|
25
29
|
private
|
26
30
|
|
27
31
|
def filter_internal(array_value, user_options)
|
28
|
-
|
32
|
+
if array_value.respond_to?(:each_with_index)
|
33
|
+
array_value = array_value
|
34
|
+
elsif array_value.respond_to?(:to_a)
|
35
|
+
array_value = array_value.to_a
|
36
|
+
else
|
37
|
+
raise ValidationError.new('参数应该传递一个数组或者数组 Like 的对象(实现了 each_with_index 或者 to_a 方法)')
|
38
|
+
end
|
39
|
+
|
29
40
|
array_value.each_with_index.map do |item, index|
|
30
41
|
begin
|
31
42
|
@items.filter(item, user_options)
|
@@ -89,7 +89,7 @@ module Meta
|
|
89
89
|
end
|
90
90
|
|
91
91
|
# 返回能够处理 scope 和 stage 的 schema(可以是 self),否则应返回 UnsupportedStageSchema 或 nil.
|
92
|
-
def find_schema(
|
92
|
+
def find_schema(stage:, scope:)
|
93
93
|
staged(stage)&.scoped(scope)
|
94
94
|
end
|
95
95
|
|
@@ -103,15 +103,22 @@ module Meta
|
|
103
103
|
self
|
104
104
|
end
|
105
105
|
|
106
|
+
# defined_scopes_mapping 是一个 Hash,用于缓存已经计算出的 scopes,用于避免重复计算。其主要针对的是具有命名系统的 Schema,如 Meta::Entity
|
107
|
+
def defined_scopes(stage:, defined_scopes_mapping:)
|
108
|
+
[]
|
109
|
+
end
|
110
|
+
|
106
111
|
# 执行 if: 选项,返回 true 或 false
|
107
|
-
def if?(
|
108
|
-
|
112
|
+
def if?(object_value, execution = nil)
|
113
|
+
if_block = options[:if]
|
114
|
+
return true if if_block.nil?
|
109
115
|
|
110
|
-
|
116
|
+
args_length = if_block.lambda? ? if_block.arity : 1
|
117
|
+
args = args_length > 0 ? [object_value] : []
|
111
118
|
if execution
|
112
|
-
execution.instance_exec(&options[:if])
|
119
|
+
execution.instance_exec(*args, &options[:if])
|
113
120
|
else
|
114
|
-
options[:if]&.call
|
121
|
+
options[:if]&.call(*args)
|
115
122
|
end
|
116
123
|
end
|
117
124
|
|
@@ -123,7 +130,8 @@ module Meta
|
|
123
130
|
#
|
124
131
|
# 选项:
|
125
132
|
# - stage: 传递 :param 或 :render
|
126
|
-
# -
|
133
|
+
# - schema_docs_mapping: 用于保存已经生成的 Schema
|
134
|
+
# - defined_scopes_mapping: 用于缓存已经定义的 scopes
|
127
135
|
# - presenter: 兼容 Grape 框架的实体类
|
128
136
|
def to_schema_doc(**user_options)
|
129
137
|
return Presenters.to_schema_doc(options[:presenter], options) if options[:presenter]
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require_relative 'properties'
|
5
|
+
|
6
|
+
module Meta
|
7
|
+
module JsonSchema
|
8
|
+
class NamedProperties < Properties
|
9
|
+
attr_reader :schema_base_name
|
10
|
+
|
11
|
+
def initialize(properties, schema_base_name)
|
12
|
+
super(properties)
|
13
|
+
|
14
|
+
raise TypeError, "schema_name 必须是一个 String,当前是:#{schema_base_name.class}" unless schema_base_name.is_a?(String)
|
15
|
+
raise ArgumentError, 'schema_name 不能为 nil 或空字符串' if schema_base_name.nil? || schema_base_name.empty?
|
16
|
+
|
17
|
+
# 修正 base_name,确保其不包含 Entity 后缀
|
18
|
+
schema_base_name = schema_base_name.delete_suffix('Entity') if schema_base_name&.end_with?('Entity')
|
19
|
+
@schema_base_name = schema_base_name
|
20
|
+
end
|
21
|
+
|
22
|
+
def schema_name(stage)
|
23
|
+
if stage == :render
|
24
|
+
"#{schema_base_name}Entity"
|
25
|
+
elsif stage == :param
|
26
|
+
"#{schema_base_name}Params"
|
27
|
+
else
|
28
|
+
raise ArgumentError, "stage 必须是 :render 或 :param,当前是:#{stage}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def merge(other_properties)
|
33
|
+
raise UnsupportedError, 'NamedProperties 不支持 merge 操作'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,61 +1,79 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../../utils/kwargs/check'
|
4
|
+
require_relative 'named_properties'
|
4
5
|
|
5
6
|
module Meta
|
6
7
|
module JsonSchema
|
7
8
|
class ObjectSchema < BaseSchema
|
8
|
-
attr_reader :properties
|
9
|
+
attr_reader :properties
|
10
|
+
# 只有 ObjectSchema 对象才有 locked_options,因为 locked_options 多是用来锁定属性的行为的,包括:
|
11
|
+
# scope:、discard_missing:、exclude: 等
|
12
|
+
attr_reader :locked_options
|
9
13
|
|
14
|
+
# stage 和 scope 选项在两个 CHECKER 下都用到了
|
10
15
|
USER_OPTIONS_CHECKER = Utils::KeywordArgs::Builder.build do
|
11
|
-
|
12
|
-
|
16
|
+
key :stage
|
17
|
+
key :scope, normalizer: ->(value) {
|
18
|
+
raise ArgumentError, 'scope 选项不可传递 nil' if value.nil?
|
19
|
+
value = [value] unless value.is_a?(Array)
|
20
|
+
value
|
21
|
+
}
|
22
|
+
key :discard_missing, :exclude, :extra_properties, :type_conversion, :validation
|
23
|
+
key :execution, :user_data, :object_value
|
24
|
+
end
|
25
|
+
TO_SCHEMA_DOC_CHECKER = Utils::KeywordArgs::Builder.build do
|
26
|
+
key :stage
|
13
27
|
key :scope, normalizer: ->(value) {
|
14
28
|
raise ArgumentError, 'scope 选项不可传递 nil' if value.nil?
|
15
29
|
value = [value] unless value.is_a?(Array)
|
16
30
|
value
|
17
31
|
}
|
32
|
+
key :schema_docs_mapping, :defined_scopes_mapping
|
18
33
|
end
|
19
34
|
|
20
|
-
def initialize(properties
|
35
|
+
def initialize(properties:, options: {}, locked_options: {})
|
36
|
+
raise ArgumentError, 'properties 必须是 Properties 实例' unless properties.is_a?(Properties)
|
37
|
+
|
21
38
|
super(options)
|
22
39
|
|
23
40
|
@properties = properties || Properties.new({}) # property 包含 stage,stage 包含 scope、schema
|
24
41
|
@properties = Properties.new(@properties) if @properties.is_a?(Hash)
|
25
42
|
@locked_options = USER_OPTIONS_CHECKER.check(locked_options || {})
|
26
|
-
@schema_name_resolver = schema_name_resolver || proc { nil }
|
27
|
-
end
|
28
|
-
|
29
|
-
# 复制一个新的 ObjectSchema,只有 options 不同
|
30
|
-
def dup(options)
|
31
|
-
self.class.new(
|
32
|
-
properties: properties,
|
33
|
-
options: options,
|
34
|
-
locked_options: locked_options,
|
35
|
-
schema_name_resolver: @schema_name_resolver
|
36
|
-
)
|
37
43
|
end
|
38
44
|
|
39
45
|
def filter(object_value, user_options = {})
|
40
46
|
# 合并 user_options
|
41
47
|
user_options = USER_OPTIONS_CHECKER.check(user_options)
|
42
|
-
user_options = merge_user_options(user_options, locked_options) if locked_options
|
48
|
+
user_options = self.class.merge_user_options(user_options, locked_options) if locked_options
|
43
49
|
super
|
44
50
|
end
|
45
51
|
|
46
|
-
|
47
|
-
|
48
|
-
|
52
|
+
def naming?
|
53
|
+
properties.is_a?(NamedProperties)
|
54
|
+
end
|
55
|
+
|
56
|
+
def defined_scopes(stage:, defined_scopes_mapping:)
|
57
|
+
properties.defined_scopes(stage: stage, defined_scopes_mapping: defined_scopes_mapping)
|
49
58
|
end
|
50
59
|
|
51
|
-
def resolve_name(stage)
|
60
|
+
def resolve_name(stage, user_scopes, defined_scopes)
|
61
|
+
raise ArgumentError, 'stage 不能为 nil' if stage.nil?
|
62
|
+
|
63
|
+
# 先合成外面传进来的 scope
|
52
64
|
locked_scopes = (locked_options || {})[:scope] || []
|
53
|
-
|
65
|
+
user_scopes = (user_scopes + locked_scopes).uniq
|
66
|
+
scopes = user_scopes & defined_scopes
|
67
|
+
|
68
|
+
# 再根据 stage 和 scope 生成为当前的 Schema 生成一个合适的名称,要求保证唯一性
|
69
|
+
schema_name = properties.schema_name(stage)
|
70
|
+
schema_name += "__#{scopes.join('__')}" unless scopes.empty?
|
71
|
+
schema_name
|
54
72
|
end
|
55
73
|
|
56
74
|
def to_schema_doc(user_options = {})
|
57
|
-
user_options =
|
58
|
-
user_options = merge_user_options(user_options, locked_options) if locked_options
|
75
|
+
user_options = TO_SCHEMA_DOC_CHECKER.check(user_options)
|
76
|
+
user_options = self.class.merge_user_options(user_options, locked_options) if locked_options
|
59
77
|
|
60
78
|
schema = { type: 'object' }
|
61
79
|
schema[:description] = options[:description] if options[:description]
|
@@ -80,7 +98,7 @@ module Meta
|
|
80
98
|
@properties.filter(object_value, user_options)
|
81
99
|
end
|
82
100
|
|
83
|
-
def merge_user_options(user_options, locked_options)
|
101
|
+
def self.merge_user_options(user_options, locked_options)
|
84
102
|
user_options.merge(locked_options) do |key, user_value, locked_value|
|
85
103
|
if key == :scope
|
86
104
|
user_value + locked_value
|
@@ -11,7 +11,11 @@ module Meta
|
|
11
11
|
@properties = properties
|
12
12
|
end
|
13
13
|
|
14
|
+
# user_options 包括 stage, scope, extra_properties, discard_missing, exclude、execution、user_data
|
14
15
|
def filter(object_value, user_options = {})
|
16
|
+
# 首先,要将 object_value 转化为 ObjectWrapper
|
17
|
+
object_value = JsonObject.wrap(object_value)
|
18
|
+
|
15
19
|
# 第一步,根据 user_options[:scope] 需要过滤一些字段
|
16
20
|
stage = user_options[:stage]
|
17
21
|
# 传递一个数字;因为 scope 不能包含数字,这里传递一个数字,使得凡是配置 scope 的属性都会被过滤
|
@@ -26,7 +30,7 @@ module Meta
|
|
26
30
|
next false if exclude && exclude.include?(name)
|
27
31
|
|
28
32
|
# 通过 if 选项过滤
|
29
|
-
next false unless property_schema.if?(user_options)
|
33
|
+
next false unless property_schema.if?(object_value, user_options[:execution])
|
30
34
|
|
31
35
|
# 默认返回 true
|
32
36
|
next true
|
@@ -40,6 +44,12 @@ module Meta
|
|
40
44
|
value = resolve_property_value(object_value, name, property_schema)
|
41
45
|
|
42
46
|
begin
|
47
|
+
# 如果 property_schema 是 RefSchema,则局部的 scope 不会传递下去
|
48
|
+
if property_schema.is_a?(RefSchema)
|
49
|
+
# 只接受全局的 scope,其以 $ 符合开头
|
50
|
+
new_scopes = user_options[:scope].find_all { |scope| scope.start_with?('$') }
|
51
|
+
user_options = user_options.merge(scope: new_scopes)
|
52
|
+
end
|
43
53
|
object[name] = property_schema.filter(value, **user_options, object_value: object_value)
|
44
54
|
rescue JsonSchema::ValidationErrors => e
|
45
55
|
cause = e.cause || e if cause.nil? # 将第一次出现的错误作为 cause
|
@@ -65,6 +75,13 @@ module Meta
|
|
65
75
|
end
|
66
76
|
end
|
67
77
|
|
78
|
+
def defined_scopes(stage:, defined_scopes_mapping:)
|
79
|
+
@properties.each_with_object([]) do |(name, property), defined_scopes|
|
80
|
+
defined_scopes.concat(property.defined_scopes(stage: stage, defined_scopes_mapping: defined_scopes_mapping))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# user_options 包括 stage, scope, schema_docs_mapping, defined_scopes_mapping
|
68
85
|
def to_swagger_doc(scope: [], stage: nil, **user_options)
|
69
86
|
locked_scopes = scope
|
70
87
|
properties = filter_by(stage: stage, user_scope: locked_scopes)
|
@@ -78,7 +95,7 @@ module Meta
|
|
78
95
|
end
|
79
96
|
|
80
97
|
# 程序中有些地方用到了这三个方法
|
81
|
-
def_delegators :@properties, :empty?, :key?, :[]
|
98
|
+
def_delegators :@properties, :empty?, :key?, :[], :each
|
82
99
|
|
83
100
|
def merge(properties)
|
84
101
|
self.class.new(@properties.merge(properties.instance_eval { @properties }))
|
@@ -101,7 +118,7 @@ module Meta
|
|
101
118
|
def resolve_property_value(object_value, name, property_schema)
|
102
119
|
if property_schema.value?
|
103
120
|
nil
|
104
|
-
elsif object_value.is_a?(Hash) || object_value.is_a?(
|
121
|
+
elsif object_value.is_a?(Hash) || object_value.is_a?(JsonObject)
|
105
122
|
object_value.key?(name.to_s) ? object_value[name.to_s] : object_value[name.to_sym]
|
106
123
|
else
|
107
124
|
raise "不应该还有其他类型了,已经在类型转换中将其转换为 Meta::JsonSchema::ObjectWrapper 了"
|
@@ -5,31 +5,61 @@ require_relative 'base_schema'
|
|
5
5
|
module Meta
|
6
6
|
module JsonSchema
|
7
7
|
class RefSchema < BaseSchema
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :object_schema
|
9
|
+
|
10
|
+
def initialize(object_schema, options = {})
|
11
|
+
raise ArgumentError, 'object_schema 必须是一个 ObjectSchema' unless object_schema.is_a?(ObjectSchema)
|
9
12
|
|
10
|
-
def initialize(schema, options = {})
|
11
13
|
super(options)
|
12
|
-
@
|
14
|
+
@object_schema = object_schema
|
13
15
|
end
|
14
16
|
|
15
17
|
def filter(value, user_options = {})
|
16
18
|
value = super
|
17
|
-
|
19
|
+
object_schema.filter(value, user_options)
|
18
20
|
end
|
19
21
|
|
20
22
|
def to_schema_doc(user_options)
|
21
|
-
|
23
|
+
raise '引用的 ObjectSchema 没有包含命名逻辑,无法生成文档' unless object_schema.naming?
|
22
24
|
|
23
|
-
#
|
24
|
-
|
25
|
+
# 首先,要求出 defined_scopes
|
26
|
+
defined_scopes = self.defined_scopes(stage: user_options[:stage], defined_scopes_mapping: user_options[:defined_scopes_mapping])
|
27
|
+
# 然后,求出 schema_name
|
28
|
+
schema_name = object_schema.resolve_name(user_options[:stage], user_options[:scope], defined_scopes)
|
29
|
+
# 接着将 Schema 写进 schemas 选项中去
|
30
|
+
schema_components = user_options[:schema_docs_mapping] || {}
|
25
31
|
unless schema_components.key?(schema_name)
|
26
32
|
schema_components[schema_name] = nil # 首先设置 schemas 防止出现无限循环
|
27
|
-
schema_components[schema_name] =
|
33
|
+
schema_components[schema_name] = object_schema.to_schema_doc(**user_options) # 原地修改 schemas,无妨
|
28
34
|
end
|
29
35
|
|
30
|
-
#
|
36
|
+
# 最后,返回这个 $ref 结构
|
31
37
|
{ '$ref': "#/components/schemas/#{schema_name}" }
|
32
38
|
end
|
39
|
+
|
40
|
+
def defined_scopes(stage:, defined_scopes_mapping:)
|
41
|
+
defined_scopes_mapping ||= {}
|
42
|
+
|
43
|
+
if object_schema.properties.respond_to?(:schema_name)
|
44
|
+
# 只有命名实体才会被缓存
|
45
|
+
schema_name = object_schema.properties.schema_name(stage)
|
46
|
+
return defined_scopes_mapping[schema_name] if defined_scopes_mapping.key?(schema_name)
|
47
|
+
end
|
48
|
+
|
49
|
+
defined_scopes_mapping[schema_name] = []
|
50
|
+
defined_scopes = object_schema.properties.each.map do |name, property|
|
51
|
+
property.defined_scopes(stage: stage, defined_scopes_mapping: defined_scopes_mapping)
|
52
|
+
end.flatten.uniq.sort
|
53
|
+
defined_scopes_mapping[schema_name] = defined_scopes
|
54
|
+
defined_scopes
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# # TODO: 这种带有组合方式的 Schema,让我联想到,每次 BaseSchema 新增一个方法都要在子 Schema 中加一遍,很烦!
|
60
|
+
# def defined_scopes
|
61
|
+
# schema.defined_scopes
|
62
|
+
# end
|
33
63
|
end
|
34
64
|
end
|
35
65
|
end
|
@@ -1,39 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../support/scope_matcher'
|
4
|
+
|
3
5
|
module Meta
|
4
6
|
module JsonSchema
|
5
7
|
class ScopingSchema < BaseSchema
|
6
|
-
attr_reader :
|
7
|
-
|
8
|
-
def initialize(required_scope: [], schema:)
|
9
|
-
raise ArgumentError, 'required_scope 选项不可传递 nil' if required_scope.nil?
|
10
|
-
required_scope = [required_scope] unless required_scope.is_a?(Array)
|
8
|
+
attr_reader :scope_matcher, :schema
|
11
9
|
|
12
|
-
|
10
|
+
def initialize(scope_matcher_options: , schema:)
|
11
|
+
@scope_matcher = ScopeMatcher.new(scope_matcher_options)
|
13
12
|
@schema = schema
|
14
13
|
end
|
15
14
|
|
16
15
|
def scoped(user_scopes)
|
17
|
-
|
16
|
+
@scope_matcher.match?(user_scopes) ? schema : unsupported_schema(user_scopes)
|
17
|
+
end
|
18
18
|
|
19
|
-
|
19
|
+
def defined_scopes(**kwargs)
|
20
|
+
current = scope_matcher.defined_scopes
|
21
|
+
deep = schema.defined_scopes(**kwargs)
|
22
|
+
(current + deep).uniq
|
20
23
|
end
|
21
24
|
|
22
|
-
|
23
|
-
permit_extras true
|
25
|
+
private
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
required_scopes = [] if required_scopes.nil?
|
28
|
-
required_scopes = [required_scopes] unless required_scopes.is_a?(Array)
|
29
|
-
required_scopes
|
30
|
-
}
|
27
|
+
def unsupported_schema(user_scopes)
|
28
|
+
UnsupportedSchema.new(:scope, user_scopes)
|
31
29
|
end
|
30
|
+
|
32
31
|
def self.build_from_options(options, build_schema)
|
33
|
-
options =
|
34
|
-
|
32
|
+
options = options.dup
|
33
|
+
scope_matcher_options = options.delete(:scope)
|
35
34
|
schema = build_schema.call(options)
|
36
|
-
schema = ScopingSchema.new(
|
35
|
+
schema = ScopingSchema.new(scope_matcher_options: scope_matcher_options, schema: schema) if scope_matcher_options
|
37
36
|
schema
|
38
37
|
end
|
39
38
|
end
|
@@ -9,16 +9,14 @@ module Meta
|
|
9
9
|
module JsonSchema
|
10
10
|
# 内含 param_schema, render_schema, default_schema,分别用于不同的阶段。
|
11
11
|
class StagingSchema < BaseSchema
|
12
|
-
attr_reader :param_schema, :render_schema
|
12
|
+
attr_reader :param_schema, :render_schema
|
13
13
|
|
14
|
-
def initialize(param_schema:, render_schema
|
14
|
+
def initialize(param_schema:, render_schema:)
|
15
15
|
raise ArgumentError, 'param_schema 选项重复提交为 StagingSchema' if param_schema.is_a?(StagingSchema)
|
16
16
|
raise ArgumentError, 'render_schema 选项重复提交为 StagingSchema' if render_schema.is_a?(StagingSchema)
|
17
|
-
raise ArgumentError, 'default_schema 选项重复提交为 StagingSchema' if default_schema.is_a?(StagingSchema)
|
18
17
|
|
19
18
|
@param_schema = param_schema
|
20
19
|
@render_schema = render_schema
|
21
|
-
@default_schema = default_schema
|
22
20
|
end
|
23
21
|
|
24
22
|
def filter(value, user_options = {})
|
@@ -27,7 +25,7 @@ module Meta
|
|
27
25
|
elsif user_options[:stage] == :render
|
28
26
|
render_schema.filter(value, user_options)
|
29
27
|
else
|
30
|
-
|
28
|
+
raise ArgumentError, "stage 选项必须是 :param 或 :render"
|
31
29
|
end
|
32
30
|
end
|
33
31
|
|
@@ -37,10 +35,14 @@ module Meta
|
|
37
35
|
elsif stage == :render
|
38
36
|
render_schema
|
39
37
|
else
|
40
|
-
|
38
|
+
raise ArgumentError, "stage 选项必须是 :param 或 :render"
|
41
39
|
end
|
42
40
|
end
|
43
41
|
|
42
|
+
def defined_scopes(stage:, **kwargs)
|
43
|
+
staged(stage).defined_scopes(stage: stage, **kwargs)
|
44
|
+
end
|
45
|
+
|
44
46
|
def self.build_from_options(options, build_schema = ->(opts) { BaseSchema.new(opts) })
|
45
47
|
param_opts, render_opts, common_opts = SchemaOptions.divide_to_param_and_render(options)
|
46
48
|
if param_opts == common_opts && render_opts == common_opts
|
@@ -48,9 +50,8 @@ module Meta
|
|
48
50
|
else
|
49
51
|
StagingSchema.new(
|
50
52
|
param_schema: param_opts ? ScopingSchema.build_from_options(param_opts, build_schema) : UnsupportedSchema.new(:stage, :param),
|
51
|
-
render_schema: render_opts ? ScopingSchema.build_from_options(render_opts, build_schema) : UnsupportedSchema.new(:stage, :render)
|
52
|
-
|
53
|
-
)
|
53
|
+
render_schema: render_opts ? ScopingSchema.build_from_options(render_opts, build_schema) : UnsupportedSchema.new(:stage, :render)
|
54
|
+
)
|
54
55
|
end
|
55
56
|
end
|
56
57
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# 将 Ruby 类型包装成 JsonObject 类型,以便可以通过 [key] 访问。同时,保留其他方法的调用,将其转发到原始对象上。
|
4
|
+
module Meta
|
5
|
+
module JsonSchema
|
6
|
+
class JsonObject
|
7
|
+
def initialize(target)
|
8
|
+
@target = target
|
9
|
+
end
|
10
|
+
|
11
|
+
def __target__
|
12
|
+
@target
|
13
|
+
end
|
14
|
+
|
15
|
+
def key?(key)
|
16
|
+
@target.respond_to?(key)
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](key)
|
20
|
+
@target.__send__(key)
|
21
|
+
end
|
22
|
+
|
23
|
+
def method_missing(method, *args)
|
24
|
+
@target.__send__(method, *args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.wrap(target)
|
28
|
+
case target
|
29
|
+
when JsonObject, Hash
|
30
|
+
target
|
31
|
+
else
|
32
|
+
new(target)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Meta
|
4
|
+
module JsonSchema
|
5
|
+
class ScopeMatcher
|
6
|
+
attr_reader :defined_scopes
|
7
|
+
|
8
|
+
def initialize(query_clause)
|
9
|
+
query_clause = [query_clause] if query_clause.is_a?(String) || query_clause.is_a?(Symbol)
|
10
|
+
query_clause = { some_of: query_clause } if query_clause.is_a?(Array)
|
11
|
+
|
12
|
+
@match_type, @defined_scopes = query_clause.first
|
13
|
+
end
|
14
|
+
|
15
|
+
def match?(scopes)
|
16
|
+
return false if scopes.empty?
|
17
|
+
|
18
|
+
case @match_type
|
19
|
+
when :some_of
|
20
|
+
(@defined_scopes & scopes).any?
|
21
|
+
when :all_of
|
22
|
+
(@defined_scopes - scopes).empty?
|
23
|
+
else
|
24
|
+
raise "Unknown match type: #{@match_type}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,31 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bigdecimal'
|
4
|
+
require_relative 'json_object'
|
4
5
|
|
5
6
|
module Meta
|
6
7
|
module JsonSchema
|
7
|
-
class ObjectWrapper
|
8
|
-
def initialize(target)
|
9
|
-
@target = target
|
10
|
-
end
|
11
|
-
|
12
|
-
def __target__
|
13
|
-
@target
|
14
|
-
end
|
15
|
-
|
16
|
-
def key?(key)
|
17
|
-
@target.respond_to?(key)
|
18
|
-
end
|
19
|
-
|
20
|
-
def [](key)
|
21
|
-
@target.__send__(key)
|
22
|
-
end
|
23
|
-
|
24
|
-
def method_missing(method, *args)
|
25
|
-
@target.__send__(method, *args)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
8
|
module TypeConverter
|
30
9
|
# 定义客户类型对应的 Ruby 类型
|
31
10
|
@definity_types = {
|
@@ -34,7 +13,7 @@ module Meta
|
|
34
13
|
'number' => [Integer, Float, BigDecimal],
|
35
14
|
'string' => [String],
|
36
15
|
'array' => [Array],
|
37
|
-
'object' => [Hash,
|
16
|
+
'object' => [Hash, JsonObject]
|
38
17
|
}
|
39
18
|
|
40
19
|
# 定义从 Ruby 类型转化为对应类型的逻辑
|
@@ -101,7 +80,7 @@ module Meta
|
|
101
80
|
raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.object', value: value, real_type: I18n.t(:'json_schema.type_names.array'))
|
102
81
|
end
|
103
82
|
|
104
|
-
|
83
|
+
JsonObject.new(value)
|
105
84
|
end
|
106
85
|
}
|
107
86
|
|
@@ -43,7 +43,16 @@ module Meta
|
|
43
43
|
codes.each { |code| @meta[:responses][code] = entity_schema }
|
44
44
|
end
|
45
45
|
|
46
|
-
|
46
|
+
def scope(scope)
|
47
|
+
scope = [scope] unless scope.is_a?(Array)
|
48
|
+
unless scope.all? { |s| s.start_with?('$') }
|
49
|
+
raise ArgumentError, 'namespace 和 route 中声明的 scope 必须是全局 scope(以 $ 开头)'
|
50
|
+
end
|
51
|
+
|
52
|
+
@meta[:scope] = scope
|
53
|
+
end
|
54
|
+
|
55
|
+
[:tags, :title, :description].each do |method_name|
|
47
56
|
define_method(method_name) do |value|
|
48
57
|
@meta[method_name] = value
|
49
58
|
end
|
data/lib/meta/swagger_doc.rb
CHANGED
@@ -5,7 +5,7 @@ module Meta
|
|
5
5
|
class << self
|
6
6
|
def generate(application, info: {}, servers: [])
|
7
7
|
paths_and_routes = get_paths_and_routes!(application)
|
8
|
-
|
8
|
+
generate_from_paths_and_routes(paths_and_routes, info: info, servers: servers)
|
9
9
|
end
|
10
10
|
|
11
11
|
def generate_from_paths_and_routes(paths_and_routes, info: {}, servers: [])
|
@@ -62,28 +62,5 @@ module Meta
|
|
62
62
|
store_routes
|
63
63
|
end
|
64
64
|
end
|
65
|
-
|
66
|
-
class Path
|
67
|
-
def initialize(parts = [])
|
68
|
-
@parts = parts.freeze
|
69
|
-
end
|
70
|
-
|
71
|
-
def append(part)
|
72
|
-
part = part[1..-1] if part.start_with?('/')
|
73
|
-
parts = part.split('/')
|
74
|
-
|
75
|
-
self.class.new(@parts + parts)
|
76
|
-
end
|
77
|
-
|
78
|
-
def to_s
|
79
|
-
'/' + @parts.join('/')
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.from_string(path)
|
83
|
-
path = path[1..-1] if path.start_with?('/')
|
84
|
-
parts = path.split('/')
|
85
|
-
self.class.new(parts)
|
86
|
-
end
|
87
|
-
end
|
88
65
|
end
|
89
66
|
end
|
@@ -58,20 +58,20 @@ module Meta
|
|
58
58
|
def initialize(name:, normalizer: DEFAULT_TRANSFORMER, validator: nil, default: nil, alias_names: [])
|
59
59
|
@key_name = name
|
60
60
|
@consumer_names = [name] + alias_names
|
61
|
-
|
62
|
-
@normalizer =
|
61
|
+
@default_value = default
|
62
|
+
@normalizer = normalizer
|
63
63
|
@validator = validator
|
64
64
|
end
|
65
65
|
|
66
66
|
def consume(final_args, args)
|
67
67
|
@consumer_names.each do |name|
|
68
|
-
return
|
68
|
+
return if consume_name(final_args, args, name)
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
|
+
final_args[@key_name] = @default_value unless @default_value.nil?
|
71
72
|
end
|
72
73
|
|
73
74
|
def consume_name(final_args, args, consumer_name)
|
74
|
-
# TODO: default 未起作用
|
75
75
|
if args.key?(consumer_name)
|
76
76
|
value = @normalizer.call(args.delete(consumer_name))
|
77
77
|
@validator.call(value) if @validator
|
@@ -10,7 +10,7 @@ module Meta
|
|
10
10
|
final_options[:parameters] = options1[:parameters].merge(options2[:parameters])
|
11
11
|
end
|
12
12
|
if options1[:request_body].is_a?(Meta::JsonSchema::ObjectSchema) && options2[:request_body].is_a?(Meta::JsonSchema::ObjectSchema)
|
13
|
-
final_options[:request_body] = options1[:request_body].
|
13
|
+
final_options[:request_body] = options1[:request_body].properties.merge(options2[:request_body].properties)
|
14
14
|
end
|
15
15
|
if options1[:responses] && options2[:responses]
|
16
16
|
final_options[:responses] = options1[:responses].merge(options2[:responses])
|
data/meta-api.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: meta-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yetrun
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hash_to_struct
|
@@ -42,6 +42,7 @@ files:
|
|
42
42
|
- lib//meta/application/metadata.rb
|
43
43
|
- lib//meta/application/parameters.rb
|
44
44
|
- lib//meta/application/path_matching_mod.rb
|
45
|
+
- lib//meta/application/responses.rb
|
45
46
|
- lib//meta/application/route.rb
|
46
47
|
- lib//meta/config.rb
|
47
48
|
- lib//meta/entity.rb
|
@@ -56,6 +57,7 @@ files:
|
|
56
57
|
- lib//meta/json_schema/schemas/array_schema.rb
|
57
58
|
- lib//meta/json_schema/schemas/base_schema.rb
|
58
59
|
- lib//meta/json_schema/schemas/dynamic_schema.rb
|
60
|
+
- lib//meta/json_schema/schemas/named_properties.rb
|
59
61
|
- lib//meta/json_schema/schemas/object_schema.rb
|
60
62
|
- lib//meta/json_schema/schemas/properties.rb
|
61
63
|
- lib//meta/json_schema/schemas/ref_schema.rb
|
@@ -63,8 +65,10 @@ files:
|
|
63
65
|
- lib//meta/json_schema/schemas/staging_schema.rb
|
64
66
|
- lib//meta/json_schema/schemas/unsupported_schema.rb
|
65
67
|
- lib//meta/json_schema/support/errors.rb
|
68
|
+
- lib//meta/json_schema/support/json_object.rb
|
66
69
|
- lib//meta/json_schema/support/presenters.rb
|
67
70
|
- lib//meta/json_schema/support/schema_options.rb
|
71
|
+
- lib//meta/json_schema/support/scope_matcher.rb
|
68
72
|
- lib//meta/json_schema/support/type_converter.rb
|
69
73
|
- lib//meta/json_schema/support/validators.rb
|
70
74
|
- lib//meta/load_i18n.rb
|