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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f5f1c4106f4106c1a3e69249e35f4efc02d4ca5d5981d4e32442c380f4228556
4
- data.tar.gz: 7dc942b0a36759a0953a255b18a7c6c6312159365e9b829b48f223d0d83cd685
3
+ metadata.gz: 176e67a6d2a121ef7841ec614a2104f7f5f201c863ea88eb805e450b9ab13ee4
4
+ data.tar.gz: bd9be253deb822db45e2bf1c0548467652155dbf39255d49709da7b7d5a56726
5
5
  SHA512:
6
- metadata.gz: 275731f780c2e5eb3dcae314a5b7832504f053092eee8aa3eb1e8b85d7cce0f4fec33bf8f46626cb307d6c7c98e6ff0ee13bf100d6a4610099323d069968d7ae
7
- data.tar.gz: f3f21b6e62503946e26471f9199809247d0ee3fafa13a2190e57aadb038b47cb19757ed08be4f2054e2b0151c2ed9f1cf4d737c007e133479395aa936d2a338b
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` 方法,用于更有效地组织字段。
@@ -6,7 +6,7 @@ zh-CN:
6
6
  errors:
7
7
  required: "未提供"
8
8
  format: "格式不正确"
9
- allowable: "不在允许的值范围内,允许的值包括 [%{allowable_values}]"
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
@@ -1,4 +1,6 @@
1
1
  require_relative 'config'
2
2
  require_relative 'application'
3
+ require_relative 'entity'
3
4
  require_relative 'swagger_doc'
5
+ require_relative 'scope/base'
4
6
  require_relative 'load_i18n'
@@ -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.concat(["$#{method}"]), discard_missing: discard_missing }.compact
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.concat([method]) }.compact,
30
+ **{ execution: execution, stage: :render, scope: @scope }.compact,
33
31
  **user_options,
34
32
  )
35
33
  end
@@ -39,7 +39,8 @@ module Meta
39
39
  end
40
40
 
41
41
  def generate_operation_doc(schemas)
42
- meta.generate_operation_doc(schemas, scope: ["$#{method}"])
42
+ # 不再自动添加 $post 等 scope
43
+ meta.generate_operation_doc(schemas, scope: [])
43
44
  end
44
45
  end
45
46
  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
- @schema_builder.schema_name(self.name) if self.name
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
- 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
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
- def lock(key, value)
21
- locked(key => value)
22
- end
22
+ def lock(key, value)
23
+ locked(key => value)
24
+ end
23
25
 
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
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, scope: nil, discard_missing: nil, exclude: nil)
39
+ def initialize(builder, **locked_options)
39
40
  @object_schema_builder = builder
40
- @locked_options = ObjectSchema::USER_OPTIONS_CHECKER.check({ scope: scope, discard_missing: discard_missing, exclude: exclude }.compact)
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 = ObjectSchema::USER_OPTIONS_CHECKER.check(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 = common_options.merge(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
- property name, property_options
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, params: false, &block)
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 to_schema(locked_options = nil)
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
- def apply_array_scope?(options, block)
164
- options[:type] == 'array' && (options[:items] || block)
165
- end
181
+ def apply_array_scope?(options, block)
182
+ options[:type] == 'array' && (options[:items] || block)
183
+ end
166
184
 
167
- def apply_object_scope?(options, block)
168
- (options[:type] == 'object' || block) && (options[:properties] || block)
169
- end
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 = SCHEMA_BUILDER_OPTIONS.check(options)
14
+ options = SchemaOptions::BaseBuildOptions.check(options)
15
+ SchemaOptions.fix_type_option!(options)
23
16
 
24
- if apply_array_schema?(options, block)
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
- def apply_array_schema?(options, block)
40
- options[:type] == 'array'
41
- end
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
- def apply_object_schema?(options, block)
44
- (options[:type] == 'object' || options[:type].nil?) && (options[:properties] || block)
45
- end
47
+ def apply_array_schema?(options, block)
48
+ options[:type] == 'array'
49
+ end
46
50
 
47
- def apply_ref_schema?(options, block)
48
- options[:ref] != nil
49
- end
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 = OPTIONS_CHECKER.check(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 = USER_OPTIONS_CHECKER.check(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[:allowable] if options[:allowable]
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 = USER_OPTIONS_CHECKER.check(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 = USER_OPTIONS_CHECKER.check(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
- schema_name = properties.schema_name(stage)
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
- schema = { type: 'object' }
79
- schema[:description] = options[:description] if options[:description]
67
+ # 将调用转移到 Scopes 模块下
68
+ resolve_name_helper(base_schema_name, user_scopes, defined_scopes)
69
+ end
80
70
 
81
- properties, required_keys = @properties.to_swagger_doc(**user_options)
82
- schema[:properties] = properties unless properties.empty?
83
- schema[:required] = required_keys unless required_keys.empty?
84
- schema
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
- locked_scopes = scope
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
- def filter_by(stage:, user_scope: false)
111
- @properties.transform_values do |property|
112
- property.find_schema(stage: stage, scope: user_scope)
113
- end.filter do |name, schema|
114
- schema.filter?
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.sort
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