meta-api 0.1.1 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/config/locales/zh-CN.yml +1 -1
- data/lib/meta/api.rb +2 -0
- data/lib/meta/application/metadata.rb +2 -4
- data/lib/meta/application/route.rb +2 -1
- data/lib/meta/entity.rb +7 -2
- data/lib/meta/json_schema/builders/object_schema_builder.rb +69 -51
- data/lib/meta/json_schema/builders/schema_builder_tool.rb +31 -22
- data/lib/meta/json_schema/schemas/base_schema.rb +3 -23
- data/lib/meta/json_schema/schemas/object_schema.rb +38 -39
- data/lib/meta/json_schema/schemas/properties.rb +19 -20
- data/lib/meta/json_schema/schemas/ref_schema.rb +2 -8
- data/lib/meta/json_schema/schemas/scoping_schema.rb +6 -3
- data/lib/meta/json_schema/schemas/staging_schema.rb +4 -1
- data/lib/meta/json_schema/support/schema_options.rb +87 -18
- data/lib/meta/json_schema/support/scope_matcher.rb +8 -5
- data/lib/meta/json_schema/support/validators.rb +2 -2
- data/lib/meta/route_dsl/meta_builder.rb +0 -3
- data/lib/meta/route_dsl/parameters_builder.rb +4 -3
- data/lib/meta/route_dsl/route_builder.rb +0 -1
- data/lib/meta/route_dsl/uniformed_params_builder.rb +3 -3
- data/lib/meta/scope/base.rb +152 -0
- data/lib/meta/scope/utils.rb +56 -0
- data/lib/meta/utils/kwargs/builder.rb +17 -74
- data/lib/meta/utils/kwargs/checker.rb +27 -26
- data/lib/meta/utils/kwargs/consumers.rb +64 -0
- data/lib/meta/utils/kwargs/extras_consumers.rb +33 -0
- data/lib/meta/utils/kwargs/helpers.rb +36 -0
- data/meta-api.gemspec +1 -1
- metadata +7 -3
- data/lib/meta/utils/kwargs/check.rb +0 -91
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 176e67a6d2a121ef7841ec614a2104f7f5f201c863ea88eb805e450b9ab13ee4
|
4
|
+
data.tar.gz: bd9be253deb822db45e2bf1c0548467652155dbf39255d49709da7b7d5a56726
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d1e5c80f3699d00bd529b74da7881eb559c089ab0bbb088560599453eea2972dc38dd082d991fc0be5e2815509987e8a3176d4d3fb0b041d35ae4ec5682c665
|
7
|
+
data.tar.gz: 59fcbd0be04d0d94a768f5b63e46a5ffcbc3a0a67ed89959ff957f8be4d1f9b7d04551cc090f6cc3a031effe45cebcb369d4a10f31fbc7a5811e2760a7f0b335
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# 更新日志
|
2
2
|
|
3
|
+
## 0.1.2(2024 年 6 月 5 日)
|
4
|
+
|
5
|
+
1. 实现新的基于常量的 Scope 场景化模式。
|
6
|
+
2. 属性的 `scope:` 选项如果传递的是字符串数组,则恢复为 `或` 模式。
|
7
|
+
3. 取消 HTTP method 自动生成对应的 Scope常量。
|
8
|
+
4. 修复 `locked(discard_missing: true)` 时生成文档报错。
|
9
|
+
5. 属性新增 `enum:` 选项,`allowable:` 作为其别名存在。
|
10
|
+
|
3
11
|
## 0.1.1(2024 年 6 月 1 日)
|
4
12
|
|
5
13
|
1. 添加 `Meta::Entity.with_common_options` 方法,用于更有效地组织字段。
|
data/config/locales/zh-CN.yml
CHANGED
@@ -6,7 +6,7 @@ zh-CN:
|
|
6
6
|
errors:
|
7
7
|
required: "未提供"
|
8
8
|
format: "格式不正确"
|
9
|
-
|
9
|
+
enum: "不在允许的值范围内,允许的值包括 [%{allowable_values}]"
|
10
10
|
type_convert:
|
11
11
|
basic: "类型转化失败,期望得到一个 `%{target_type}` 类型,但值 `%{value}` 无法转化"
|
12
12
|
array: "转化为数组类型时期望对象拥有 `to_a` 方法"
|
data/lib/meta/api.rb
CHANGED
@@ -13,23 +13,21 @@ module Meta
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def parse_request_body(execution, discard_missing: false)
|
16
|
-
method = execution.request.request_method.downcase.to_sym
|
17
16
|
request_body.filter(
|
18
17
|
execution.params(:raw),
|
19
18
|
**Meta.config.json_schema_user_options,
|
20
19
|
**Meta.config.json_schema_param_stage_user_options,
|
21
|
-
**{ execution: execution, stage: :param, scope: @scope
|
20
|
+
**{ execution: execution, stage: :param, scope: @scope, discard_missing: discard_missing }.compact
|
22
21
|
)
|
23
22
|
rescue JsonSchema::ValidationErrors => e
|
24
23
|
raise Errors::ParameterInvalid.new(e.errors)
|
25
24
|
end
|
26
25
|
|
27
26
|
def render_entity(execution, entity_schema, value, user_options)
|
28
|
-
method = execution.request.request_method.downcase.to_sym
|
29
27
|
entity_schema.filter(value,
|
30
28
|
**Meta.config.json_schema_user_options,
|
31
29
|
**Meta.config.json_schema_render_stage_user_options,
|
32
|
-
**{ execution: execution, stage: :render, scope: @scope
|
30
|
+
**{ execution: execution, stage: :render, scope: @scope }.compact,
|
33
31
|
**user_options,
|
34
32
|
)
|
35
33
|
end
|
data/lib/meta/entity.rb
CHANGED
@@ -14,7 +14,12 @@ module Meta
|
|
14
14
|
def inherited(base)
|
15
15
|
base.instance_eval do
|
16
16
|
@schema_builder = JsonSchema::ObjectSchemaBuilder.new
|
17
|
-
|
17
|
+
if self.name
|
18
|
+
mod_names = self.name.split("::")
|
19
|
+
mod_names.shift if mod_names.first == "Entities"
|
20
|
+
schema_name = mod_names.join('_')
|
21
|
+
@schema_builder.schema_name(schema_name)
|
22
|
+
end
|
18
23
|
end
|
19
24
|
end
|
20
25
|
|
@@ -23,4 +28,4 @@ module Meta
|
|
23
28
|
end
|
24
29
|
end
|
25
30
|
end
|
26
|
-
end
|
31
|
+
end
|
@@ -7,37 +7,38 @@ module Meta
|
|
7
7
|
class ObjectSchemaBuilder
|
8
8
|
extend Forwardable
|
9
9
|
|
10
|
-
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
10
|
+
class Locked
|
11
|
+
# 定义一些 locked 的别名方法
|
12
|
+
module LockedMethodAlias
|
13
|
+
# 我在这里说明一下 lock_scope 的逻辑。
|
14
|
+
# 1. lock_scope 实际上是将 scope 传递到当前的 ObjectSchema 和它的子 Schema 中。
|
15
|
+
# 2. lock_scope 会叠加,也就是如果子 schema 也有 lock_scope,那么子 Schema 会将两个 Schema 合并起来。
|
16
|
+
# 3. 调用 filter(scope:) 和 to_schema_doc(scope:) 时,可以传递 scope 参数,这个 scope 遇到 lock_scope 时会合并。
|
17
|
+
# 4. 这也就是说,在路由级别定义的 scope 宏会传递到下面的 Schema 中去。
|
18
|
+
def add_scope(scope)
|
19
|
+
lock_scope(scope)
|
20
|
+
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
def lock(key, value)
|
23
|
+
locked(key => value)
|
24
|
+
end
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
def method_missing(method, *args)
|
27
|
+
if method =~ /^lock_(\w+)$/
|
28
|
+
key = Regexp.last_match(1)
|
29
|
+
lock(key.to_sym, *args)
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
30
33
|
end
|
31
34
|
end
|
32
|
-
end
|
33
35
|
|
34
|
-
class Locked
|
35
36
|
attr_reader :object_schema_builder, :locked_options
|
36
37
|
|
37
38
|
# locked_options 是用户传递的参数,这个参数会被合并到 object_schema_builder 的 locked_options 中。
|
38
|
-
def initialize(builder,
|
39
|
+
def initialize(builder, **locked_options)
|
39
40
|
@object_schema_builder = builder
|
40
|
-
@locked_options =
|
41
|
+
@locked_options = SchemaOptions::UserOptions::Filter.check(locked_options.compact)
|
41
42
|
end
|
42
43
|
|
43
44
|
def to_schema
|
@@ -45,7 +46,7 @@ module Meta
|
|
45
46
|
end
|
46
47
|
|
47
48
|
def locked(options)
|
48
|
-
options =
|
49
|
+
options = SchemaOptions::UserOptions::Filter.check(options)
|
49
50
|
options = ObjectSchema.merge_user_options(locked_options, options)
|
50
51
|
Locked.new(self.object_schema_builder, **options)
|
51
52
|
end
|
@@ -63,9 +64,25 @@ module Meta
|
|
63
64
|
end
|
64
65
|
|
65
66
|
def property(name, options = {}, &block)
|
66
|
-
options =
|
67
|
+
options = merge_options(options)
|
67
68
|
object_schema_builder.property(name, options, &block)
|
68
69
|
end
|
70
|
+
|
71
|
+
def merge_options(options)
|
72
|
+
self.class.merge_options(common_options, options)
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.merge_options(common_options, options)
|
76
|
+
common_options.merge(options) do |key, oldVal, newVal|
|
77
|
+
if key == :scope
|
78
|
+
# 合并 common_options 和 options 中的 scope 选项
|
79
|
+
Scope::Utils.and(oldVal, newVal)
|
80
|
+
else
|
81
|
+
# 关于 param、render 内部选项的合并问题暂不考虑
|
82
|
+
newVal
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
69
86
|
end
|
70
87
|
|
71
88
|
attr_reader :properties
|
@@ -73,6 +90,8 @@ module Meta
|
|
73
90
|
def initialize(options = {}, &)
|
74
91
|
raise 'type 选项必须是 object' if !options[:type].nil? && options[:type] != 'object'
|
75
92
|
|
93
|
+
@schema_cache = {} # 用于缓存已经生成的 schema,重复利用
|
94
|
+
|
76
95
|
@properties = {} # 所有的属性已经生成
|
77
96
|
@required = []
|
78
97
|
@validations = {}
|
@@ -82,7 +101,13 @@ module Meta
|
|
82
101
|
@options = options
|
83
102
|
|
84
103
|
properties&.each do |name, property_options|
|
85
|
-
|
104
|
+
if property_options.is_a?(Hash)
|
105
|
+
property name, property_options
|
106
|
+
elsif property_options.is_a?(BaseSchema)
|
107
|
+
@properties[name.to_sym] = property_options
|
108
|
+
else
|
109
|
+
raise ArgumentError, "属性 #{name} 的类型不正确"
|
110
|
+
end
|
86
111
|
end
|
87
112
|
|
88
113
|
instance_exec(&) if block_given?
|
@@ -99,6 +124,7 @@ module Meta
|
|
99
124
|
|
100
125
|
def property(name, options = {}, &block)
|
101
126
|
@properties[name.to_sym] = Properties.build_property(options, ->(options) { SchemaBuilderTool.build(options, &block) })
|
127
|
+
# @properties[name.to_sym] = SchemaBuilderTool.build(options, &block)
|
102
128
|
end
|
103
129
|
|
104
130
|
alias expose property
|
@@ -123,7 +149,7 @@ module Meta
|
|
123
149
|
end
|
124
150
|
|
125
151
|
def render(options = {}, &block)
|
126
|
-
with_common_options(**options,
|
152
|
+
with_common_options(**options, param: false, &block)
|
127
153
|
end
|
128
154
|
|
129
155
|
def merge(schema_builder)
|
@@ -132,41 +158,33 @@ module Meta
|
|
132
158
|
@properties.merge!(schema_builder.properties)
|
133
159
|
end
|
134
160
|
|
135
|
-
def
|
161
|
+
def within(*properties)
|
162
|
+
to_schema.properties.within(*properties)
|
163
|
+
end
|
164
|
+
alias_method :[], :within
|
165
|
+
|
166
|
+
def to_schema(locked_options = {})
|
167
|
+
locked_options = SchemaOptions::UserOptions::Filter.check(locked_options.compact)
|
168
|
+
return @schema_cache[locked_options] if @schema_cache[locked_options]
|
169
|
+
|
136
170
|
properties = @schema_name ? NamedProperties.new(@properties, @schema_name) : Properties.new(@properties)
|
137
|
-
ObjectSchema.new(properties: properties, options: @options, locked_options: locked_options)
|
171
|
+
@schema_cache[locked_options] = ObjectSchema.new(properties: properties, options: @options, locked_options: locked_options)
|
138
172
|
end
|
139
173
|
|
140
174
|
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(',')} 未在实体中定义"
|
155
|
-
end
|
156
|
-
|
157
175
|
Locked.new(self, **options)
|
158
176
|
end
|
159
|
-
include LockedMethodAlias
|
177
|
+
include Locked::LockedMethodAlias
|
160
178
|
|
161
179
|
private
|
162
180
|
|
163
|
-
|
164
|
-
|
165
|
-
|
181
|
+
def apply_array_scope?(options, block)
|
182
|
+
options[:type] == 'array' && (options[:items] || block)
|
183
|
+
end
|
166
184
|
|
167
|
-
|
168
|
-
|
169
|
-
|
185
|
+
def apply_object_scope?(options, block)
|
186
|
+
(options[:type] == 'object' || block) && (options[:properties] || block)
|
187
|
+
end
|
170
188
|
end
|
171
189
|
end
|
172
190
|
end
|
@@ -10,18 +10,16 @@ module Meta
|
|
10
10
|
module JsonSchema
|
11
11
|
class SchemaBuilderTool
|
12
12
|
class << self
|
13
|
-
SCHEMA_BUILDER_OPTIONS = Utils::KeywordArgs::Builder.build do
|
14
|
-
permit_extras true
|
15
|
-
|
16
|
-
key :ref, alias_names: [:using], normalizer: ->(entity) {
|
17
|
-
entity
|
18
|
-
}
|
19
|
-
key :dynamic_ref, alias_names: [:dynamic_using], normalizer: ->(value) { value.is_a?(Proc) ? { resolve: value } : value }
|
20
|
-
end
|
21
13
|
def build(options = {}, &block)
|
22
|
-
options =
|
14
|
+
options = SchemaOptions::BaseBuildOptions.check(options)
|
15
|
+
SchemaOptions.fix_type_option!(options)
|
23
16
|
|
24
|
-
if
|
17
|
+
if apply_staging_schema?(options)
|
18
|
+
# 原则上,SchemaBuilderTool 不处理 param render scope 选项,这几个选项只会在 property 宏中出现,
|
19
|
+
# 并且交由 StagingSchema 和 ScopingSchema 专业处理。
|
20
|
+
# 只不过,经过后置修复后可能包含了 param 和 render 选项
|
21
|
+
StagingSchema.build_from_options(options)
|
22
|
+
elsif apply_array_schema?(options, block)
|
25
23
|
ArraySchemaBuilder.new(options, &block).to_schema
|
26
24
|
elsif apply_ref_schema?(options, block)
|
27
25
|
RefSchemaBuilder.new(options).to_schema
|
@@ -36,21 +34,32 @@ module Meta
|
|
36
34
|
|
37
35
|
private
|
38
36
|
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
def apply_staging_schema?(options)
|
38
|
+
if options.key?(:param)
|
39
|
+
return true if options[:param] == false || !options[:param].empty?
|
40
|
+
end
|
41
|
+
if options.key?(:render)
|
42
|
+
return true if options[:render] == false || !options[:render].empty?
|
43
|
+
end
|
44
|
+
false
|
45
|
+
end
|
42
46
|
|
43
|
-
|
44
|
-
|
45
|
-
|
47
|
+
def apply_array_schema?(options, block)
|
48
|
+
options[:type] == 'array'
|
49
|
+
end
|
46
50
|
|
47
|
-
|
48
|
-
|
49
|
-
|
51
|
+
def apply_object_schema?(options, block)
|
52
|
+
options[:properties] || block
|
53
|
+
end
|
54
|
+
|
55
|
+
def apply_ref_schema?(options, block)
|
56
|
+
options[:ref] != nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def apply_dynamic_schema?(options, block)
|
60
|
+
options[:dynamic_ref] != nil
|
61
|
+
end
|
50
62
|
|
51
|
-
def apply_dynamic_schema?(options, block)
|
52
|
-
options[:dynamic_ref] != nil
|
53
|
-
end
|
54
63
|
end
|
55
64
|
end
|
56
65
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../../utils/kwargs/check'
|
4
3
|
require_relative '../support/schema_options'
|
5
4
|
|
6
5
|
module Meta
|
@@ -20,22 +19,6 @@ module Meta
|
|
20
19
|
# 的地方都会抛出异常(NoMethodError: undefined method `[]' for nil:NilClass)。这种模式
|
21
20
|
# 的案例很多,包括 StagingSchema、RefSchema 等。
|
22
21
|
class BaseSchema
|
23
|
-
OPTIONS_CHECKER = Utils::KeywordArgs::Builder.build do
|
24
|
-
key :type, :items, :description, :presenter, :value, :default, :properties, :convert
|
25
|
-
key :validate, :required, :format, :allowable
|
26
|
-
key :before, :after
|
27
|
-
key :if
|
28
|
-
end
|
29
|
-
|
30
|
-
USER_OPTIONS_CHECKER = Utils::KeywordArgs::Builder.build do
|
31
|
-
key :execution, :object_value, :type_conversion, :validation, :user_data
|
32
|
-
key :stage, validator: ->(value) { raise ArgumentError, "stage 只能取值为 :param 或 :render" unless [:param, :render].include?(value) }
|
33
|
-
|
34
|
-
# 以下是 ObjectSchema 需要的选项
|
35
|
-
# extra_properties 只能取值为 :ignore、:raise_error
|
36
|
-
key :discard_missing, :extra_properties, :exclude, :scope
|
37
|
-
end
|
38
|
-
|
39
22
|
# `options` 包含了转换器、验证器、文档、选项。
|
40
23
|
#
|
41
24
|
# 由于本对象可继承,基于不同的继承可分别表示基本类型、对象和数组,所以该属
|
@@ -47,10 +30,7 @@ module Meta
|
|
47
30
|
|
48
31
|
def initialize(options = {})
|
49
32
|
raise ArgumentError, 'options 必须是 Hash 类型' unless options.is_a?(Hash)
|
50
|
-
options =
|
51
|
-
raise '不允许 BaseSchema 直接接受 array 类型,必须通过继承使用 ArraySchema' if options[:type] == 'array' && self.class == BaseSchema
|
52
|
-
|
53
|
-
@options = SchemaOptions.normalize(options).freeze
|
33
|
+
@options = SchemaOptions::BaseBuildOptions.check(options)
|
54
34
|
end
|
55
35
|
|
56
36
|
def filter?
|
@@ -58,7 +38,7 @@ module Meta
|
|
58
38
|
end
|
59
39
|
|
60
40
|
def filter(value, user_options = {})
|
61
|
-
user_options =
|
41
|
+
user_options = SchemaOptions::UserOptions::Filter.check(user_options)
|
62
42
|
|
63
43
|
value = value_callback(user_options) if options[:value]
|
64
44
|
value = before_callback(value, user_options) if options[:before]
|
@@ -139,7 +119,7 @@ module Meta
|
|
139
119
|
schema = {}
|
140
120
|
schema[:type] = options[:type] if options[:type]
|
141
121
|
schema[:description] = options[:description] if options[:description]
|
142
|
-
schema[:enum] = options[:
|
122
|
+
schema[:enum] = options[:enum] if options[:enum]
|
143
123
|
|
144
124
|
schema
|
145
125
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../../utils/kwargs/check'
|
4
3
|
require_relative 'named_properties'
|
5
4
|
|
6
5
|
module Meta
|
@@ -11,27 +10,6 @@ module Meta
|
|
11
10
|
# scope:、discard_missing:、exclude: 等
|
12
11
|
attr_reader :locked_options
|
13
12
|
|
14
|
-
# stage 和 scope 选项在两个 CHECKER 下都用到了
|
15
|
-
USER_OPTIONS_CHECKER = Utils::KeywordArgs::Builder.build do
|
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
|
27
|
-
key :scope, normalizer: ->(value) {
|
28
|
-
raise ArgumentError, 'scope 选项不可传递 nil' if value.nil?
|
29
|
-
value = [value] unless value.is_a?(Array)
|
30
|
-
value
|
31
|
-
}
|
32
|
-
key :schema_docs_mapping, :defined_scopes_mapping
|
33
|
-
end
|
34
|
-
|
35
13
|
def initialize(properties:, options: {}, locked_options: {})
|
36
14
|
raise ArgumentError, 'properties 必须是 Properties 实例' unless properties.is_a?(Properties)
|
37
15
|
|
@@ -39,16 +17,29 @@ module Meta
|
|
39
17
|
|
40
18
|
@properties = properties || Properties.new({}) # property 包含 stage,stage 包含 scope、schema
|
41
19
|
@properties = Properties.new(@properties) if @properties.is_a?(Hash)
|
42
|
-
@locked_options =
|
20
|
+
@locked_options = SchemaOptions::UserOptions::Filter.check(locked_options || {}, extras_handler: :ignore)
|
21
|
+
@locked_to_doc_options = SchemaOptions::UserOptions::ToDoc.check(locked_options || {}, extras_handler: :ignore)
|
43
22
|
end
|
44
23
|
|
45
24
|
def filter(object_value, user_options = {})
|
46
25
|
# 合并 user_options
|
47
|
-
user_options =
|
26
|
+
user_options = SchemaOptions::UserOptions::Filter.check(user_options)
|
48
27
|
user_options = self.class.merge_user_options(user_options, locked_options) if locked_options
|
49
28
|
super
|
50
29
|
end
|
51
30
|
|
31
|
+
def to_schema_doc(user_options = {})
|
32
|
+
user_options = SchemaOptions::UserOptions::ToDoc.check(user_options)
|
33
|
+
user_options = self.class.merge_user_options(user_options, @locked_to_doc_options) if @locked_to_doc_options
|
34
|
+
|
35
|
+
schema = { type: 'object' }
|
36
|
+
schema[:description] = options[:description] if options[:description]
|
37
|
+
properties, required_keys = @properties.to_swagger_doc(**user_options)
|
38
|
+
schema[:properties] = properties unless properties.empty?
|
39
|
+
schema[:required] = required_keys unless required_keys.empty?
|
40
|
+
schema
|
41
|
+
end
|
42
|
+
|
52
43
|
def naming?
|
53
44
|
properties.is_a?(NamedProperties)
|
54
45
|
end
|
@@ -57,31 +48,39 @@ module Meta
|
|
57
48
|
properties.defined_scopes(stage: stage, defined_scopes_mapping: defined_scopes_mapping)
|
58
49
|
end
|
59
50
|
|
51
|
+
# 解析当前 Schema 的名称
|
52
|
+
#
|
53
|
+
# 名称解析的规则是:
|
54
|
+
# 1. 结合基础的 schema_name,如果它是参数,就加上 Params 后缀;如果它是返回值,就加上 Entity 后缀
|
55
|
+
# 2. 而后跟上 locked_scope. 这里,会把多余的 scope 去掉。比如,一个实体内部只处理 Admin 的 scope,但是外部传进来了
|
56
|
+
# Admin 和 Detail,那么名称只会包括 Admin
|
60
57
|
def resolve_name(stage, user_scopes, defined_scopes)
|
61
58
|
raise ArgumentError, 'stage 不能为 nil' if stage.nil?
|
62
59
|
|
63
60
|
# 先合成外面传进来的 scope
|
64
61
|
locked_scopes = (locked_options || {})[:scope] || []
|
65
62
|
user_scopes = (user_scopes + locked_scopes).uniq
|
66
|
-
scopes = user_scopes & defined_scopes
|
67
63
|
|
68
64
|
# 再根据 stage 和 scope 生成为当前的 Schema 生成一个合适的名称,要求保证唯一性
|
69
|
-
|
70
|
-
schema_name += "__#{scopes.join('__')}" unless scopes.empty?
|
71
|
-
schema_name
|
72
|
-
end
|
73
|
-
|
74
|
-
def to_schema_doc(user_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
|
65
|
+
base_schema_name = properties.schema_name(stage)
|
77
66
|
|
78
|
-
|
79
|
-
|
67
|
+
# 将调用转移到 Scopes 模块下
|
68
|
+
resolve_name_helper(base_schema_name, user_scopes, defined_scopes)
|
69
|
+
end
|
80
70
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
71
|
+
# 帮助实体解析名称,这里主要是考虑 scope 的作用
|
72
|
+
#
|
73
|
+
# base_schema_name: 具有 Params 或 Entity 后缀的基础名称
|
74
|
+
# user_scopes: 用户传进来的 scope 数组
|
75
|
+
# candidate_scopes: 从实体中找出的能够参与命名的备选的 scope 数组
|
76
|
+
def resolve_name_helper(base_schema_name, user_scopes, candidate_scopes)
|
77
|
+
# 从备选的 scope 中获取到被利用到的
|
78
|
+
scopes = candidate_scopes.filter { |candidate_scope| candidate_scope.match?(user_scopes) }
|
79
|
+
scope_names = scopes.map(&:scope_name)
|
80
|
+
|
81
|
+
# 合成新的名字
|
82
|
+
schema_name_parts = [base_schema_name] + scope_names
|
83
|
+
schema_name_parts.join('__')
|
85
84
|
end
|
86
85
|
|
87
86
|
def locked_scope
|
@@ -44,12 +44,6 @@ module Meta
|
|
44
44
|
value = resolve_property_value(object_value, name, property_schema)
|
45
45
|
|
46
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
|
53
47
|
object[name] = property_schema.filter(value, **user_options, object_value: object_value)
|
54
48
|
rescue JsonSchema::ValidationErrors => e
|
55
49
|
cause = e.cause || e if cause.nil? # 将第一次出现的错误作为 cause
|
@@ -75,16 +69,9 @@ module Meta
|
|
75
69
|
end
|
76
70
|
end
|
77
71
|
|
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
72
|
# user_options 包括 stage, scope, schema_docs_mapping, defined_scopes_mapping
|
85
73
|
def to_swagger_doc(scope: [], stage: nil, **user_options)
|
86
|
-
|
87
|
-
properties = filter_by(stage: stage, user_scope: locked_scopes)
|
74
|
+
properties = filter_by(stage: stage, user_scope: scope)
|
88
75
|
required_keys = properties.filter do |key, property_schema|
|
89
76
|
property_schema.options[:required]
|
90
77
|
end.keys
|
@@ -94,6 +81,12 @@ module Meta
|
|
94
81
|
[properties, required_keys]
|
95
82
|
end
|
96
83
|
|
84
|
+
def defined_scopes(stage:, defined_scopes_mapping:)
|
85
|
+
@properties.each_with_object([]) do |(name, property), defined_scopes|
|
86
|
+
defined_scopes.concat(property.defined_scopes(stage: stage, defined_scopes_mapping: defined_scopes_mapping))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
97
90
|
# 程序中有些地方用到了这三个方法
|
98
91
|
def_delegators :@properties, :empty?, :key?, :[], :each
|
99
92
|
|
@@ -101,19 +94,25 @@ module Meta
|
|
101
94
|
self.class.new(@properties.merge(properties.instance_eval { @properties }))
|
102
95
|
end
|
103
96
|
|
97
|
+
def within(*properties)
|
98
|
+
self.class.new(@properties.slice(*properties))
|
99
|
+
end
|
100
|
+
|
104
101
|
def self.build_property(*args)
|
105
102
|
StagingSchema.build_from_options(*args)
|
106
103
|
end
|
107
104
|
|
108
105
|
private
|
109
106
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
107
|
+
# 根据所示的关键字参数,过滤出符合条件的属性
|
108
|
+
# 注意,这里会结合自身的 locked_scope 考量
|
109
|
+
def filter_by(stage:, user_scope: false)
|
110
|
+
@properties.transform_values do |property|
|
111
|
+
property.find_schema(stage: stage, scope: user_scope)
|
112
|
+
end.filter do |name, schema|
|
113
|
+
schema.filter?
|
114
|
+
end
|
115
115
|
end
|
116
|
-
end
|
117
116
|
|
118
117
|
def resolve_property_value(object_value, name, property_schema)
|
119
118
|
if property_schema.value?
|
@@ -47,19 +47,13 @@ module Meta
|
|
47
47
|
end
|
48
48
|
|
49
49
|
defined_scopes_mapping[schema_name] = []
|
50
|
+
# 求解 defined_scopes,最终结果去重 + 排序
|
50
51
|
defined_scopes = object_schema.properties.each.map do |name, property|
|
51
52
|
property.defined_scopes(stage: stage, defined_scopes_mapping: defined_scopes_mapping)
|
52
|
-
end.flatten.uniq.
|
53
|
+
end.flatten.uniq.sort_by(&:name)
|
53
54
|
defined_scopes_mapping[schema_name] = defined_scopes
|
54
55
|
defined_scopes
|
55
56
|
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
# # TODO: 这种带有组合方式的 Schema,让我联想到,每次 BaseSchema 新增一个方法都要在子 Schema 中加一遍,很烦!
|
60
|
-
# def defined_scopes
|
61
|
-
# schema.defined_scopes
|
62
|
-
# end
|
63
57
|
end
|
64
58
|
end
|
65
59
|
end
|