runapi-core 0.5.2 → 0.6.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: 3716e984b8648825f23f5dba6a64e015ff70bf1c1f5711f96f0c41f57f8184f9
4
- data.tar.gz: cbe52d93ce9609dd55da08d45bba595c8f95cebd80c24db7774a9be0765c3fb8
3
+ metadata.gz: ad3b0781bacf4ae45f6fe15948391c47a7ba0e8a37ea9481af61c1faef44a881
4
+ data.tar.gz: 619dd3f7627899141c3072acf45aaeeaeafd4b312d974a4b5fd0982953967911
5
5
  SHA512:
6
- metadata.gz: 9499a9cc754db6ff4eaca48fb10f34216adee0beda90582724c0030c0caa486ee93effcbe8e04cab95fd3d3d253639f454b102b786019b27d085767df2d8f0ad
7
- data.tar.gz: 644da423fc25d2eabb33d974012f18f46aa8f445efa34e901e9e3587107afdf37c43af091e8b82f2672c8538d4f4a8896a9df29c668762341fd7f697f702909a
6
+ metadata.gz: 63b482c33a6a4fc50ea8a85f147735a155b6d7c6365be3e3cd71e032bde911da9864d072ba22ed324278a2dcc9033445b3ddf66d11c0245563091305870b832e
7
+ data.tar.gz: eaff58ff1a0ab6c1d5c613666acf32b78d74a73a1a998d5bd8842aacad5ee3bb34eadf142f64a52dbe2f24ab75ad7c5c4b529e422eece2bae32fe3f2628ae622
@@ -40,224 +40,6 @@ module RunApi
40
40
  params[key.to_s] if params.key?(key.to_s)
41
41
  end
42
42
 
43
- def validate_optional!(params, key, allowed)
44
- value = param(params, key)
45
- return unless value
46
-
47
- unless allowed.include?(value)
48
- raise Core::ValidationError, "Invalid #{key}: #{value}. Must be one of: #{allowed.join(", ")}"
49
- end
50
- end
51
-
52
- # ---- Contract validation ------------------------------------------
53
- # Validates request params against the generated contract: model
54
- # membership, then declared cross-field rules, then per-field
55
- # required/enum/integer/min/max/length. `schema` is one action entry from the generated
56
- # per-package CONTRACT (CONTRACT["<action>"]).
57
-
58
- def validate_contract!(schema, params)
59
- model = param_value(params, "model")
60
- models = schema["models"] || []
61
- if models.empty?
62
- fields = schema.dig("fields_by_model", "_") || {}
63
- else
64
- unless models.include?(model)
65
- raise Core::ValidationError, "model must be one of: #{models.sort.join(", ")}"
66
- end
67
-
68
- fields = schema.dig("fields_by_model", model) || {}
69
- end
70
-
71
- Array(schema["rules"]).each { |rule| enforce_contract_rule!(params, rule) }
72
- fields.each do |field, rules|
73
- validate_schema_field!(params, field, rules)
74
- end
75
- end
76
-
77
- def validate_schema_field!(params, field, rules)
78
- value = param_value(params, field)
79
- if !value.nil? && (rules.key?("min_items") || rules.key?("max_items"))
80
- validate_schema_item_count!(field, value, rules)
81
- end
82
-
83
- present = field_present?(params, field)
84
- raise Core::ValidationError, "#{field} is required" if rules["required"] && !present
85
- return unless present
86
-
87
- if (enum = rules["enum"]) && !enum_value_allowed?(enum, value)
88
- raise Core::ValidationError, "#{field} must be one of: #{enum.join(", ")}"
89
- end
90
-
91
- validate_schema_integer!(field, value, rules) if rules["type"] == "integer"
92
- validate_schema_range!(field, value, rules) if rules.key?("min") || rules.key?("max")
93
- end
94
-
95
- def validate_schema_item_count!(field, value, rules)
96
- raise Core::ValidationError, "#{field} must be an array" unless value.is_a?(Array)
97
-
98
- min = rules["min_items"]
99
- max = rules["max_items"]
100
- return if (min.nil? || value.size >= min) && (max.nil? || value.size <= max)
101
-
102
- raise Core::ValidationError, item_count_message(field, min, max)
103
- end
104
-
105
- def item_count_message(field, min, max)
106
- if min && max
107
- "#{field} must contain between #{min} and #{max} items"
108
- elsif min
109
- "#{field} must contain at least #{min} items"
110
- else
111
- "#{field} must contain at most #{max} items"
112
- end
113
- end
114
-
115
- # Mirrors GatewayEntry#validate_schema_integer!: a type: integer field
116
- # rejects non-integer numbers (e.g. 11.5), which min/max alone admit.
117
- def validate_schema_integer!(field, value, rules)
118
- return if value.is_a?(Integer)
119
-
120
- min = rules["min"]
121
- max = rules["max"]
122
- detail = (min && max) ? " between #{min} and #{max}" : ""
123
- raise Core::ValidationError, "#{field} must be an integer#{detail}"
124
- end
125
-
126
- def validate_schema_range!(field, value, rules)
127
- if rules["length"]
128
- measured = value.to_s.length
129
- unit = "characters"
130
- else
131
- raise Core::ValidationError, "#{field} must be a number" unless value.is_a?(Numeric)
132
-
133
- measured = value
134
- unit = nil
135
- end
136
-
137
- min = rules["min"]
138
- max = rules["max"]
139
- return if (min.nil? || measured >= min) && (max.nil? || measured <= max)
140
-
141
- raise Core::ValidationError, schema_range_message(field, min, max, unit)
142
- end
143
-
144
- def schema_range_message(field, min, max, unit)
145
- suffix = unit ? " #{unit}" : ""
146
- if min && max
147
- "#{field} must be between #{min} and #{max}#{suffix}"
148
- elsif min
149
- "#{field} must be at least #{min}#{suffix}"
150
- else
151
- "#{field} must be at most #{max}#{suffix}"
152
- end
153
- end
154
-
155
- def enum_value_allowed?(enum, value)
156
- enum.any? do |allowed|
157
- if allowed == true || allowed == false
158
- value == allowed
159
- elsif allowed.is_a?(Numeric)
160
- value.is_a?(Numeric) && value == allowed
161
- elsif value.is_a?(Numeric)
162
- allowed == value
163
- else
164
- allowed.to_s == value.to_s
165
- end
166
- end
167
- end
168
-
169
- def enforce_contract_rule!(params, rule)
170
- conditions = rule["when"] || {}
171
- return unless conditions.all? { |field, condition| rule_condition_met?(params, field, condition) }
172
-
173
- # Sorted so the message matches every other SDK; Go maps have no declaration order.
174
- context = conditions.sort_by { |field, _| field.to_s }
175
- .map { |field, condition| rule_condition_label(field, condition) }.join(" and ")
176
- qualifier = context.empty? ? "" : " when #{context}"
177
-
178
- Array(rule["required"]).each do |field|
179
- next if field_present?(params, field)
180
-
181
- raise Core::ValidationError, "#{field} is required#{qualifier}"
182
- end
183
-
184
- required_any = Array(rule["required_any"])
185
- if required_any.any? && required_any.none? { |field| field_present?(params, field) }
186
- raise Core::ValidationError, "one of #{required_any.join(", ")} is required#{qualifier}"
187
- end
188
-
189
- Array(rule["forbidden"]).each do |field|
190
- next unless field_present?(params, field)
191
-
192
- raise Core::ValidationError, "#{field} is not allowed#{qualifier}"
193
- end
194
-
195
- (rule["enum"] || {}).sort_by { |field, _| field.to_s }.each do |field, allowed|
196
- next unless field_present?(params, field)
197
-
198
- value = param_value(params, field)
199
- next if Array(allowed).any? { |candidate| candidate.to_s == value.to_s }
200
-
201
- raise Core::ValidationError, "#{field} must be one of: #{Array(allowed).join(", ")}#{qualifier}"
202
- end
203
- end
204
-
205
- # A `when` entry is either `{present: true|false}` or a scalar the
206
- # supplied value must equal. Rules never resolve declared defaults.
207
- def rule_condition_met?(params, field, condition)
208
- if condition.is_a?(Hash) && (condition.key?("present") || condition.key?(:present))
209
- expected = condition["present"].nil? ? condition[:present] : condition["present"]
210
- return field_present?(params, field) == (expected == true)
211
- end
212
- return false unless param_key?(params, field)
213
-
214
- param_value(params, field).to_s == condition.to_s
215
- end
216
-
217
- def rule_condition_label(field, condition)
218
- if condition.is_a?(Hash) && (condition.key?("present") || condition.key?(:present))
219
- expected = condition["present"].nil? ? condition[:present] : condition["present"]
220
- return (expected == true) ? "#{field} is present" : "#{field} is absent"
221
- end
222
-
223
- "#{field} is #{condition}"
224
- end
225
-
226
- def field_present?(params, field)
227
- return false unless param_key?(params, field)
228
-
229
- value = param_value(params, field)
230
- case value
231
- when false then true
232
- when Array then value.any? { |item| present_value?(item) }
233
- else present_value?(value)
234
- end
235
- end
236
-
237
- def param_key?(params, field)
238
- params.key?(field.to_sym) || params.key?(field.to_s)
239
- end
240
-
241
- # Indifferent value lookup for a string-or-symbol field across params that
242
- # may be keyed either way.
243
- def param_value(params, field)
244
- if params.key?(field.to_sym)
245
- params[field.to_sym]
246
- elsif params.key?(field.to_s)
247
- params[field.to_s]
248
- end
249
- end
250
-
251
- def present_value?(value)
252
- case value
253
- when nil, false then false
254
- when true then true
255
- when String then !value.strip.empty?
256
- when Array, Hash then !value.empty?
257
- else true
258
- end
259
- end
260
-
261
43
  def default_response_class
262
44
  if self.class.const_defined?(:RESPONSE_CLASS, false)
263
45
  self.class::RESPONSE_CLASS
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RunApi
4
4
  module Core
5
- VERSION = "0.5.2"
5
+ VERSION = "0.6.0"
6
6
  end
7
7
 
8
8
  VERSION = Core::VERSION
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: runapi-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - RunAPI