haveapi 0.28.4 → 0.29.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/README.md +139 -0
- data/Rakefile +1 -0
- data/haveapi.gemspec +2 -1
- data/lib/haveapi/action.rb +26 -9
- data/lib/haveapi/actions/default.rb +5 -2
- data/lib/haveapi/actions/paginable.rb +8 -4
- data/lib/haveapi/authentication/oauth2/provider.rb +1 -1
- data/lib/haveapi/authentication/token/config.rb +10 -3
- data/lib/haveapi/authentication/token/provider.rb +22 -21
- data/lib/haveapi/context.rb +4 -1
- data/lib/haveapi/i18n.rb +125 -0
- data/lib/haveapi/locales/cs.yml +167 -0
- data/lib/haveapi/locales/en.yml +168 -0
- data/lib/haveapi/metadata.rb +25 -3
- data/lib/haveapi/model_adapters/active_record.rb +40 -26
- data/lib/haveapi/output_formatter.rb +2 -2
- data/lib/haveapi/parameters/metadata_i18n.rb +179 -0
- data/lib/haveapi/parameters/resource.rb +18 -7
- data/lib/haveapi/parameters/typed.rb +27 -20
- data/lib/haveapi/params.rb +76 -7
- data/lib/haveapi/resource.rb +1 -1
- data/lib/haveapi/resources/action_state.rb +47 -27
- data/lib/haveapi/server.rb +156 -16
- data/lib/haveapi/spec/api_builder.rb +25 -0
- data/lib/haveapi/spec/spec_methods.rb +10 -0
- data/lib/haveapi/tasks/i18n.rb +198 -0
- data/lib/haveapi/validator_chain.rb +1 -1
- data/lib/haveapi/validators/acceptance.rb +5 -2
- data/lib/haveapi/validators/confirmation.rb +5 -2
- data/lib/haveapi/validators/exclusion.rb +2 -2
- data/lib/haveapi/validators/format.rb +5 -2
- data/lib/haveapi/validators/inclusion.rb +2 -2
- data/lib/haveapi/validators/length.rb +5 -5
- data/lib/haveapi/validators/numericality.rb +20 -22
- data/lib/haveapi/validators/presence.rb +4 -2
- data/lib/haveapi/version.rb +1 -1
- data/lib/haveapi.rb +1 -0
- data/spec/authentication/oauth2_spec.rb +10 -0
- data/spec/i18n_spec.rb +520 -0
- data/spec/params_spec.rb +183 -0
- metadata +29 -3
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
module HaveAPI::Parameters
|
|
2
|
+
module MetadataI18n
|
|
3
|
+
def metadata_i18n_catalog_items(context, i18n_path)
|
|
4
|
+
%i[label description].filter_map do |kind|
|
|
5
|
+
fallback = metadata_i18n_fallback(kind)
|
|
6
|
+
keys = metadata_i18n_keys(context, i18n_path, kind, fallback)
|
|
7
|
+
value = HaveAPI.localize(fallback).to_s.strip
|
|
8
|
+
|
|
9
|
+
next if keys.empty? || value.empty?
|
|
10
|
+
|
|
11
|
+
{
|
|
12
|
+
param: HaveAPI::Params.i18n_segment(@name),
|
|
13
|
+
kind: metadata_i18n_key_suffix(kind),
|
|
14
|
+
keys:,
|
|
15
|
+
value:
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def localized_label(context, i18n_path)
|
|
23
|
+
localized_metadata(context, i18n_path, :label, @label)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def localized_description(context, i18n_path)
|
|
27
|
+
localized_metadata(context, i18n_path, :description, @desc)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def localized_metadata(context, i18n_path, kind, fallback)
|
|
31
|
+
keys = metadata_i18n_keys(context, i18n_path, kind, fallback)
|
|
32
|
+
default = HaveAPI.localize(fallback)
|
|
33
|
+
|
|
34
|
+
return default if keys.empty?
|
|
35
|
+
|
|
36
|
+
keys.each do |key|
|
|
37
|
+
value = ::I18n.t(key, default: nil)
|
|
38
|
+
return value unless value.nil?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
default
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def metadata_i18n_keys(context, i18n_path, kind, fallback)
|
|
45
|
+
explicit_key = explicit_metadata_i18n_key(kind)
|
|
46
|
+
|
|
47
|
+
return [normalize_metadata_i18n_key(context, explicit_key)] if explicit_key
|
|
48
|
+
return [] if fallback.is_a?(HaveAPI::LocalizedMessage)
|
|
49
|
+
return [] unless i18n_path
|
|
50
|
+
|
|
51
|
+
scope = parameter_i18n_scope(context)
|
|
52
|
+
return [] unless scope
|
|
53
|
+
|
|
54
|
+
exact_key = [
|
|
55
|
+
scope,
|
|
56
|
+
i18n_path,
|
|
57
|
+
HaveAPI::Params.i18n_segment(@name),
|
|
58
|
+
metadata_i18n_key_suffix(kind)
|
|
59
|
+
].join('.')
|
|
60
|
+
|
|
61
|
+
[exact_key, *metadata_i18n_default_keys(context, i18n_path, kind, scope)].uniq
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def metadata_i18n_fallback(kind)
|
|
65
|
+
case kind
|
|
66
|
+
when :label
|
|
67
|
+
@label
|
|
68
|
+
when :description
|
|
69
|
+
@desc
|
|
70
|
+
else
|
|
71
|
+
raise ArgumentError, "unsupported parameter metadata kind #{kind.inspect}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def explicit_metadata_i18n_key(kind)
|
|
76
|
+
case kind
|
|
77
|
+
when :label
|
|
78
|
+
@label_key
|
|
79
|
+
when :description
|
|
80
|
+
@desc_key
|
|
81
|
+
else
|
|
82
|
+
raise ArgumentError, "unsupported parameter metadata kind #{kind.inspect}"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def normalize_metadata_i18n_key(context, key)
|
|
87
|
+
str = key.to_s
|
|
88
|
+
return str if str.include?('.')
|
|
89
|
+
|
|
90
|
+
scope = parameter_i18n_scope(context)
|
|
91
|
+
scope ? "#{scope}.#{str}" : str
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def parameter_i18n_scope(context)
|
|
95
|
+
return unless context.respond_to?(:server)
|
|
96
|
+
return unless context.server.respond_to?(:parameter_i18n_scope)
|
|
97
|
+
|
|
98
|
+
scope = context.server.parameter_i18n_scope
|
|
99
|
+
return if scope.nil? || scope == false
|
|
100
|
+
|
|
101
|
+
Array(scope).flat_map { |segment| segment.to_s.split('.') }
|
|
102
|
+
.map { |segment| HaveAPI::Params.i18n_segment(segment) }
|
|
103
|
+
.join('.')
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def metadata_i18n_key_suffix(kind)
|
|
107
|
+
kind == :description ? 'description' : kind.to_s
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def metadata_i18n_default_keys(context, i18n_path, kind, scope)
|
|
111
|
+
param = HaveAPI::Params.i18n_segment(@name)
|
|
112
|
+
suffix = metadata_i18n_key_suffix(kind)
|
|
113
|
+
resource_prefix = metadata_i18n_resource_prefix(context)
|
|
114
|
+
path = metadata_i18n_path_parts(i18n_path)
|
|
115
|
+
keys = []
|
|
116
|
+
|
|
117
|
+
if path[:meta_type]
|
|
118
|
+
if resource_prefix
|
|
119
|
+
keys << [
|
|
120
|
+
scope,
|
|
121
|
+
resource_prefix,
|
|
122
|
+
'meta',
|
|
123
|
+
path.fetch(:meta_type),
|
|
124
|
+
path.fetch(:direction),
|
|
125
|
+
param,
|
|
126
|
+
suffix
|
|
127
|
+
].join('.')
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
keys << [
|
|
131
|
+
scope,
|
|
132
|
+
'meta',
|
|
133
|
+
path.fetch(:meta_type),
|
|
134
|
+
path.fetch(:direction),
|
|
135
|
+
param,
|
|
136
|
+
suffix
|
|
137
|
+
].join('.')
|
|
138
|
+
elsif resource_prefix
|
|
139
|
+
keys << [
|
|
140
|
+
scope,
|
|
141
|
+
resource_prefix,
|
|
142
|
+
path.fetch(:direction),
|
|
143
|
+
param,
|
|
144
|
+
suffix
|
|
145
|
+
].join('.')
|
|
146
|
+
keys << [
|
|
147
|
+
scope,
|
|
148
|
+
resource_prefix,
|
|
149
|
+
'attributes',
|
|
150
|
+
param,
|
|
151
|
+
suffix
|
|
152
|
+
].join('.')
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
keys << [scope, 'attributes', param, suffix].join('.') unless path[:meta_type]
|
|
156
|
+
keys
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def metadata_i18n_resource_prefix(context)
|
|
160
|
+
return unless context.respond_to?(:resource_path) && context.resource_path
|
|
161
|
+
|
|
162
|
+
[
|
|
163
|
+
'resources',
|
|
164
|
+
*context.resource_path.map { |segment| HaveAPI::Params.i18n_segment(segment) }
|
|
165
|
+
].join('.')
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def metadata_i18n_path_parts(i18n_path)
|
|
169
|
+
parts = i18n_path.to_s.split('.')
|
|
170
|
+
|
|
171
|
+
return { direction: parts.last } unless parts[-3] == 'meta'
|
|
172
|
+
|
|
173
|
+
{
|
|
174
|
+
meta_type: parts[-2],
|
|
175
|
+
direction: parts[-1]
|
|
176
|
+
}
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
@@ -1,16 +1,23 @@
|
|
|
1
|
+
require_relative 'metadata_i18n'
|
|
2
|
+
|
|
1
3
|
module HaveAPI::Parameters
|
|
2
4
|
class Resource
|
|
5
|
+
include MetadataI18n
|
|
6
|
+
|
|
3
7
|
attr_reader :name, :resource, :label, :desc, :type, :value_id, :value_label,
|
|
4
8
|
:choices, :value_params
|
|
5
9
|
|
|
6
10
|
def initialize(resource, name: nil, label: nil, desc: nil,
|
|
7
11
|
choices: nil, value_id: :id, value_label: :label, required: nil,
|
|
8
|
-
db_name: nil, fetch: nil, nullable: nil
|
|
12
|
+
db_name: nil, fetch: nil, nullable: nil, label_key: nil,
|
|
13
|
+
desc_key: nil)
|
|
9
14
|
@resource = resource
|
|
10
15
|
@resource_path = build_resource_path(resource)
|
|
11
16
|
@name = name || resource.resource_name.underscore.to_sym
|
|
12
17
|
@label = label || (name && name.to_s.capitalize) || resource.resource_name
|
|
13
18
|
@desc = desc
|
|
19
|
+
@label_key = label_key
|
|
20
|
+
@desc_key = desc_key
|
|
14
21
|
@choices = choices || @resource::Index
|
|
15
22
|
@value_id = value_id
|
|
16
23
|
@value_label = value_label
|
|
@@ -46,7 +53,7 @@ module HaveAPI::Parameters
|
|
|
46
53
|
@resource::Index
|
|
47
54
|
end
|
|
48
55
|
|
|
49
|
-
def describe(context)
|
|
56
|
+
def describe(context, i18n_path: nil)
|
|
50
57
|
val_path = context.path_for(
|
|
51
58
|
@resource::Show,
|
|
52
59
|
context.endpoint && context.action_prepare && context.layout == :object && context.call_path_params(context.action, context.action_prepare)
|
|
@@ -62,8 +69,8 @@ module HaveAPI::Parameters
|
|
|
62
69
|
{
|
|
63
70
|
required: required?,
|
|
64
71
|
nullable: nullable?,
|
|
65
|
-
label:
|
|
66
|
-
description:
|
|
72
|
+
label: localized_label(context, i18n_path),
|
|
73
|
+
description: localized_description(context, i18n_path),
|
|
67
74
|
type: 'Resource',
|
|
68
75
|
resource: @resource_path,
|
|
69
76
|
value_id: @value_id,
|
|
@@ -101,7 +108,7 @@ module HaveAPI::Parameters
|
|
|
101
108
|
if raw.nil?
|
|
102
109
|
return nil if nullable?
|
|
103
110
|
|
|
104
|
-
raise
|
|
111
|
+
raise validation_error('haveapi.validation.cannot_be_null')
|
|
105
112
|
end
|
|
106
113
|
|
|
107
114
|
if raw.is_a?(String)
|
|
@@ -133,7 +140,7 @@ module HaveAPI::Parameters
|
|
|
133
140
|
def strip_string(value)
|
|
134
141
|
value.strip
|
|
135
142
|
rescue ArgumentError, Encoding::CompatibilityError
|
|
136
|
-
raise
|
|
143
|
+
raise validation_error('haveapi.validation.invalid_string_encoding')
|
|
137
144
|
end
|
|
138
145
|
|
|
139
146
|
def build_resource_path(r)
|
|
@@ -175,7 +182,11 @@ module HaveAPI::Parameters
|
|
|
175
182
|
action = show_action.new(context.request, context.version, path_params, {}, child_context)
|
|
176
183
|
return if action.authorized?(context.current_user)
|
|
177
184
|
|
|
178
|
-
raise
|
|
185
|
+
raise validation_error('haveapi.validation.resource_not_found')
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def validation_error(key, **values)
|
|
189
|
+
HaveAPI::ValidationError.new(HaveAPI.message(key, **values))
|
|
179
190
|
end
|
|
180
191
|
end
|
|
181
192
|
end
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
require 'date'
|
|
2
2
|
require 'time'
|
|
3
|
+
require_relative 'metadata_i18n'
|
|
3
4
|
|
|
4
5
|
module HaveAPI::Parameters
|
|
5
6
|
class Typed
|
|
7
|
+
include MetadataI18n
|
|
8
|
+
|
|
6
9
|
ATTRIBUTES = %i[
|
|
7
10
|
label desc type db_name default fill clean protected load_validators
|
|
8
|
-
nullable symbolize_keys
|
|
11
|
+
nullable symbolize_keys label_key desc_key
|
|
9
12
|
].freeze
|
|
10
13
|
|
|
11
14
|
attr_reader :name, :label, :desc, :type, :default
|
|
@@ -52,12 +55,12 @@ module HaveAPI::Parameters
|
|
|
52
55
|
@load_validators.nil? || @load_validators
|
|
53
56
|
end
|
|
54
57
|
|
|
55
|
-
def describe(context)
|
|
58
|
+
def describe(context, i18n_path: nil)
|
|
56
59
|
{
|
|
57
60
|
required: required?,
|
|
58
61
|
nullable: nullable?,
|
|
59
|
-
label:
|
|
60
|
-
description:
|
|
62
|
+
label: localized_label(context, i18n_path),
|
|
63
|
+
description: localized_description(context, i18n_path),
|
|
61
64
|
type: @type ? @type.to_s : String.to_s,
|
|
62
65
|
validators: @validators ? @validators.describe : {},
|
|
63
66
|
default: @default,
|
|
@@ -88,7 +91,7 @@ module HaveAPI::Parameters
|
|
|
88
91
|
if raw.nil?
|
|
89
92
|
return nil if nullable?
|
|
90
93
|
|
|
91
|
-
raise
|
|
94
|
+
raise validation_error('haveapi.validation.cannot_be_null')
|
|
92
95
|
end
|
|
93
96
|
|
|
94
97
|
if raw.is_a?(String)
|
|
@@ -154,7 +157,7 @@ module HaveAPI::Parameters
|
|
|
154
157
|
|
|
155
158
|
def validate_cleaned_value(value)
|
|
156
159
|
if value.nil? && !nullable?
|
|
157
|
-
raise
|
|
160
|
+
raise validation_error('haveapi.validation.cannot_be_null')
|
|
158
161
|
end
|
|
159
162
|
|
|
160
163
|
value
|
|
@@ -188,7 +191,7 @@ module HaveAPI::Parameters
|
|
|
188
191
|
def strip_string(value)
|
|
189
192
|
value.strip
|
|
190
193
|
rescue ArgumentError, Encoding::CompatibilityError
|
|
191
|
-
raise
|
|
194
|
+
raise validation_error('haveapi.validation.invalid_string_encoding')
|
|
192
195
|
end
|
|
193
196
|
|
|
194
197
|
def coerce_integer(raw)
|
|
@@ -197,7 +200,7 @@ module HaveAPI::Parameters
|
|
|
197
200
|
raw
|
|
198
201
|
when Float
|
|
199
202
|
unless raw.finite? && (raw % 1) == 0
|
|
200
|
-
raise
|
|
203
|
+
raise validation_error('haveapi.types.invalid_integer', value: raw.inspect)
|
|
201
204
|
end
|
|
202
205
|
|
|
203
206
|
raw.to_i
|
|
@@ -205,12 +208,12 @@ module HaveAPI::Parameters
|
|
|
205
208
|
s = strip_string(raw)
|
|
206
209
|
|
|
207
210
|
if s.empty? || !s.match?(/\A[+-]?\d+\z/)
|
|
208
|
-
raise
|
|
211
|
+
raise validation_error('haveapi.types.invalid_integer', value: raw.inspect)
|
|
209
212
|
end
|
|
210
213
|
|
|
211
214
|
Integer(s, 10)
|
|
212
215
|
else
|
|
213
|
-
raise
|
|
216
|
+
raise validation_error('haveapi.types.invalid_integer', value: raw.inspect)
|
|
214
217
|
end
|
|
215
218
|
end
|
|
216
219
|
|
|
@@ -220,19 +223,19 @@ module HaveAPI::Parameters
|
|
|
220
223
|
|
|
221
224
|
elsif raw.is_a?(String)
|
|
222
225
|
s = strip_string(raw)
|
|
223
|
-
raise
|
|
226
|
+
raise validation_error('haveapi.types.invalid_float', value: raw.inspect) if s.empty?
|
|
224
227
|
|
|
225
228
|
begin
|
|
226
229
|
f = Float(s)
|
|
227
230
|
rescue ArgumentError
|
|
228
|
-
raise
|
|
231
|
+
raise validation_error('haveapi.types.invalid_float', value: raw.inspect)
|
|
229
232
|
end
|
|
230
233
|
|
|
231
234
|
else
|
|
232
|
-
raise
|
|
235
|
+
raise validation_error('haveapi.types.invalid_float', value: raw.inspect)
|
|
233
236
|
end
|
|
234
237
|
|
|
235
|
-
raise
|
|
238
|
+
raise validation_error('haveapi.types.invalid_float', value: raw.inspect) unless f.finite?
|
|
236
239
|
|
|
237
240
|
f
|
|
238
241
|
end
|
|
@@ -247,35 +250,39 @@ module HaveAPI::Parameters
|
|
|
247
250
|
|
|
248
251
|
elsif raw.is_a?(String)
|
|
249
252
|
s = strip_string(raw)
|
|
250
|
-
raise
|
|
253
|
+
raise validation_error('haveapi.types.invalid_boolean', value: raw.inspect) if s.empty?
|
|
251
254
|
|
|
252
255
|
return true if %w[true t yes y 1].include?(s.downcase)
|
|
253
256
|
return false if %w[false f no n 0].include?(s.downcase)
|
|
254
257
|
end
|
|
255
258
|
|
|
256
|
-
raise
|
|
259
|
+
raise validation_error('haveapi.types.invalid_boolean', value: raw.inspect)
|
|
257
260
|
end
|
|
258
261
|
|
|
259
262
|
def coerce_datetime(raw)
|
|
260
263
|
unless raw.is_a?(String)
|
|
261
|
-
raise
|
|
264
|
+
raise validation_error('haveapi.types.invalid_datetime', value: raw)
|
|
262
265
|
end
|
|
263
266
|
|
|
264
267
|
if strip_string(raw).empty?
|
|
265
|
-
raise
|
|
268
|
+
raise validation_error('haveapi.types.invalid_datetime', value: raw)
|
|
266
269
|
end
|
|
267
270
|
|
|
268
271
|
DateTime.iso8601(raw).to_time
|
|
269
272
|
rescue ArgumentError, TypeError
|
|
270
|
-
raise
|
|
273
|
+
raise validation_error('haveapi.types.invalid_datetime', value: raw)
|
|
271
274
|
end
|
|
272
275
|
|
|
273
276
|
def coerce_string(raw)
|
|
274
277
|
if raw.is_a?(Array) || raw.is_a?(Hash)
|
|
275
|
-
raise
|
|
278
|
+
raise validation_error('haveapi.types.invalid_string', value: raw.inspect)
|
|
276
279
|
end
|
|
277
280
|
|
|
278
281
|
raw.to_s
|
|
279
282
|
end
|
|
283
|
+
|
|
284
|
+
def validation_error(key, **values)
|
|
285
|
+
HaveAPI::ValidationError.new(HaveAPI.message(key, **values))
|
|
286
|
+
end
|
|
280
287
|
end
|
|
281
288
|
end
|
data/lib/haveapi/params.rb
CHANGED
|
@@ -3,11 +3,22 @@ module HaveAPI
|
|
|
3
3
|
end
|
|
4
4
|
|
|
5
5
|
class ValidationError < StandardError
|
|
6
|
+
attr_reader :message_value
|
|
7
|
+
|
|
6
8
|
def initialize(msg, errors = {})
|
|
9
|
+
@message_value = msg
|
|
7
10
|
super(msg)
|
|
8
11
|
@errors = errors
|
|
9
12
|
end
|
|
10
13
|
|
|
14
|
+
def message
|
|
15
|
+
HaveAPI.localize(@message_value)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_s
|
|
19
|
+
message
|
|
20
|
+
end
|
|
21
|
+
|
|
11
22
|
def to_hash
|
|
12
23
|
@errors
|
|
13
24
|
end
|
|
@@ -17,6 +28,45 @@ module HaveAPI
|
|
|
17
28
|
attr_reader :params
|
|
18
29
|
attr_accessor :action
|
|
19
30
|
|
|
31
|
+
class << self
|
|
32
|
+
def action_i18n_path(context, direction)
|
|
33
|
+
prefix = action_i18n_prefix(context)
|
|
34
|
+
return unless prefix
|
|
35
|
+
|
|
36
|
+
"#{prefix}.#{i18n_segment(direction)}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def metadata_i18n_path(context, type, direction)
|
|
40
|
+
prefix = action_i18n_prefix(context)
|
|
41
|
+
return unless prefix
|
|
42
|
+
|
|
43
|
+
[
|
|
44
|
+
prefix,
|
|
45
|
+
'meta',
|
|
46
|
+
i18n_segment(type),
|
|
47
|
+
i18n_segment(direction)
|
|
48
|
+
].join('.')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def i18n_segment(value)
|
|
52
|
+
value.to_s.underscore.downcase.gsub(/[^a-z0-9_]+/, '_').gsub(/\A_+|_+\z/, '')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def action_i18n_prefix(context)
|
|
58
|
+
return unless context.respond_to?(:resource_path) && context.resource_path
|
|
59
|
+
return unless context.respond_to?(:action) && context.action
|
|
60
|
+
|
|
61
|
+
[
|
|
62
|
+
'resources',
|
|
63
|
+
*context.resource_path.map { |segment| i18n_segment(segment) },
|
|
64
|
+
'actions',
|
|
65
|
+
i18n_segment(context.action.action_name)
|
|
66
|
+
].join('.')
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
20
70
|
def initialize(direction, action)
|
|
21
71
|
@direction = direction
|
|
22
72
|
@params = []
|
|
@@ -171,7 +221,7 @@ module HaveAPI
|
|
|
171
221
|
add_param(name, apply(kwargs, type: Custom, clean: block, symbolize_keys:))
|
|
172
222
|
end
|
|
173
223
|
|
|
174
|
-
def describe(context, metadata: false)
|
|
224
|
+
def describe(context, metadata: false, i18n_path: nil)
|
|
175
225
|
context.layout = layout
|
|
176
226
|
|
|
177
227
|
ret = { parameters: {} }
|
|
@@ -179,8 +229,10 @@ module HaveAPI
|
|
|
179
229
|
ret[:namespace] = namespace
|
|
180
230
|
ret[:format] = @structure if @structure
|
|
181
231
|
|
|
232
|
+
i18n_path ||= self.class.action_i18n_path(context, @direction)
|
|
233
|
+
|
|
182
234
|
@params.each do |p|
|
|
183
|
-
ret[:parameters][p.name] = p.describe(context)
|
|
235
|
+
ret[:parameters][p.name] = p.describe(context, i18n_path:)
|
|
184
236
|
end
|
|
185
237
|
|
|
186
238
|
ret[:parameters] = filtered_description_parameters(context, ret, metadata)
|
|
@@ -188,6 +240,23 @@ module HaveAPI
|
|
|
188
240
|
ret
|
|
189
241
|
end
|
|
190
242
|
|
|
243
|
+
def parameter_metadata_i18n_items(context, i18n_path: nil, meta_type: nil)
|
|
244
|
+
i18n_path ||= self.class.action_i18n_path(context, @direction)
|
|
245
|
+
|
|
246
|
+
@params.flat_map do |param|
|
|
247
|
+
next [] unless param.respond_to?(:metadata_i18n_catalog_items)
|
|
248
|
+
|
|
249
|
+
param.metadata_i18n_catalog_items(context, i18n_path).map do |item|
|
|
250
|
+
item.merge(
|
|
251
|
+
resource_path: Array(context.resource_path).map { |v| self.class.i18n_segment(v) },
|
|
252
|
+
action: self.class.i18n_segment(context.action.action_name),
|
|
253
|
+
direction: self.class.i18n_segment(@direction),
|
|
254
|
+
meta_type: meta_type && self.class.i18n_segment(meta_type)
|
|
255
|
+
)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
191
260
|
def validate_build
|
|
192
261
|
m = :"validate_build_#{@direction}"
|
|
193
262
|
|
|
@@ -202,10 +271,10 @@ module HaveAPI
|
|
|
202
271
|
value = namespace ? params[namespace] : params
|
|
203
272
|
|
|
204
273
|
if value.nil?
|
|
205
|
-
raise ValidationError.new('
|
|
274
|
+
raise ValidationError.new(HaveAPI.message('haveapi.validation.invalid_input_layout'), {}) if any_required_params?
|
|
206
275
|
|
|
207
276
|
elsif !valid_layout?(value)
|
|
208
|
-
raise ValidationError.new('
|
|
277
|
+
raise ValidationError.new(HaveAPI.message('haveapi.validation.invalid_input_layout'), {})
|
|
209
278
|
end
|
|
210
279
|
|
|
211
280
|
return unless namespace
|
|
@@ -231,7 +300,7 @@ module HaveAPI
|
|
|
231
300
|
next if permitted && !permitted.include?(p.name)
|
|
232
301
|
|
|
233
302
|
if p.required? && input[p.name].nil?
|
|
234
|
-
errors[p.name] = ['
|
|
303
|
+
errors[p.name] = [HaveAPI.message('haveapi.validation.required_parameter_missing')]
|
|
235
304
|
next
|
|
236
305
|
end
|
|
237
306
|
|
|
@@ -248,7 +317,7 @@ module HaveAPI
|
|
|
248
317
|
end
|
|
249
318
|
rescue ValidationError => e
|
|
250
319
|
errors[p.name] ||= []
|
|
251
|
-
errors[p.name] << e.
|
|
320
|
+
errors[p.name] << e.message_value
|
|
252
321
|
next
|
|
253
322
|
end
|
|
254
323
|
|
|
@@ -271,7 +340,7 @@ module HaveAPI
|
|
|
271
340
|
end
|
|
272
341
|
|
|
273
342
|
unless errors.empty?
|
|
274
|
-
raise ValidationError.new('
|
|
343
|
+
raise ValidationError.new(HaveAPI.message('haveapi.validation.input_parameters_not_valid'), errors)
|
|
275
344
|
end
|
|
276
345
|
|
|
277
346
|
params
|
data/lib/haveapi/resource.rb
CHANGED
|
@@ -8,19 +8,25 @@ module HaveAPI::Resources
|
|
|
8
8
|
|
|
9
9
|
params(:all) do
|
|
10
10
|
id :id
|
|
11
|
-
string :label, label: '
|
|
12
|
-
bool :finished, label: '
|
|
13
|
-
bool :status,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
integer :
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
string :label, label: HaveAPI.message('haveapi.parameters.action_state.label.label')
|
|
12
|
+
bool :finished, label: HaveAPI.message('haveapi.parameters.action_state.finished.label')
|
|
13
|
+
bool :status,
|
|
14
|
+
label: HaveAPI.message('haveapi.parameters.action_state.status.label'),
|
|
15
|
+
desc: HaveAPI.message('haveapi.parameters.action_state.status.description')
|
|
16
|
+
integer :current, label: HaveAPI.message('haveapi.parameters.action_state.current.label')
|
|
17
|
+
integer :total,
|
|
18
|
+
label: HaveAPI.message('haveapi.parameters.action_state.total.label'),
|
|
19
|
+
desc: HaveAPI.message('haveapi.parameters.action_state.total.description')
|
|
20
|
+
string :unit,
|
|
21
|
+
label: HaveAPI.message('haveapi.parameters.action_state.unit.label'),
|
|
22
|
+
desc: HaveAPI.message('haveapi.parameters.action_state.unit.description')
|
|
23
|
+
bool :can_cancel,
|
|
24
|
+
label: HaveAPI.message('haveapi.parameters.action_state.can_cancel.label'),
|
|
25
|
+
desc: HaveAPI.message('haveapi.parameters.action_state.can_cancel.description')
|
|
26
|
+
datetime :created_at, label: HaveAPI.message('haveapi.parameters.action_state.created_at.label')
|
|
27
|
+
datetime :updated_at,
|
|
28
|
+
label: HaveAPI.message('haveapi.parameters.action_state.updated_at.label'),
|
|
29
|
+
desc: HaveAPI.message('haveapi.parameters.action_state.updated_at.description')
|
|
24
30
|
end
|
|
25
31
|
|
|
26
32
|
module Mixin
|
|
@@ -84,15 +90,25 @@ module HaveAPI::Resources
|
|
|
84
90
|
route '{%{resource}_id}/poll'
|
|
85
91
|
|
|
86
92
|
input(:hash) do
|
|
87
|
-
float :timeout,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
float :timeout,
|
|
94
|
+
label: HaveAPI.message('haveapi.parameters.action_state.poll.timeout.label'),
|
|
95
|
+
desc: HaveAPI.message('haveapi.parameters.action_state.poll.timeout.description'),
|
|
96
|
+
default: 15,
|
|
97
|
+
fill: true,
|
|
98
|
+
number: { min: 0, max: MAX_TIMEOUT }
|
|
99
|
+
float :update_in,
|
|
100
|
+
label: HaveAPI.message('haveapi.parameters.action_state.poll.update_in.label'),
|
|
101
|
+
desc: HaveAPI.message('haveapi.parameters.action_state.poll.update_in.description'),
|
|
102
|
+
nullable: true
|
|
103
|
+
bool :status,
|
|
104
|
+
desc: HaveAPI.message('haveapi.parameters.action_state.poll.status.description'),
|
|
105
|
+
nullable: true
|
|
106
|
+
integer :current,
|
|
107
|
+
desc: HaveAPI.message('haveapi.parameters.action_state.poll.current.description'),
|
|
108
|
+
nullable: true
|
|
109
|
+
integer :total,
|
|
110
|
+
desc: HaveAPI.message('haveapi.parameters.action_state.poll.total.description'),
|
|
111
|
+
nullable: true
|
|
96
112
|
end
|
|
97
113
|
|
|
98
114
|
output(:hash) do
|
|
@@ -103,7 +119,11 @@ module HaveAPI::Resources
|
|
|
103
119
|
|
|
104
120
|
def exec
|
|
105
121
|
if input[:timeout] > MAX_TIMEOUT
|
|
106
|
-
error!(
|
|
122
|
+
error!(
|
|
123
|
+
HaveAPI.message('haveapi.action_state.timeout_max', max: MAX_TIMEOUT),
|
|
124
|
+
{},
|
|
125
|
+
http_status: 400
|
|
126
|
+
)
|
|
107
127
|
end
|
|
108
128
|
|
|
109
129
|
t = Time.now
|
|
@@ -114,7 +134,7 @@ module HaveAPI::Resources
|
|
|
114
134
|
id: path_params['action_state_id']
|
|
115
135
|
)
|
|
116
136
|
|
|
117
|
-
error!('
|
|
137
|
+
error!(HaveAPI.message('haveapi.action_state.not_found')) unless state.valid?
|
|
118
138
|
|
|
119
139
|
if state.finished? || (Time.now - t) >= input[:timeout]
|
|
120
140
|
return state_to_hash(state)
|
|
@@ -153,7 +173,7 @@ module HaveAPI::Resources
|
|
|
153
173
|
|
|
154
174
|
return state_to_hash(state) if state.valid?
|
|
155
175
|
|
|
156
|
-
error!('
|
|
176
|
+
error!(HaveAPI.message('haveapi.action_state.not_found'))
|
|
157
177
|
end
|
|
158
178
|
end
|
|
159
179
|
|
|
@@ -172,7 +192,7 @@ module HaveAPI::Resources
|
|
|
172
192
|
id: path_params['action_state_id']
|
|
173
193
|
)
|
|
174
194
|
|
|
175
|
-
error!('
|
|
195
|
+
error!(HaveAPI.message('haveapi.action_state.not_found')) unless state.valid?
|
|
176
196
|
|
|
177
197
|
ret = state.cancel
|
|
178
198
|
|
|
@@ -183,7 +203,7 @@ module HaveAPI::Resources
|
|
|
183
203
|
ok!
|
|
184
204
|
|
|
185
205
|
else
|
|
186
|
-
error!('
|
|
206
|
+
error!(HaveAPI.message('haveapi.action_state.cancellation_failed'))
|
|
187
207
|
end
|
|
188
208
|
rescue RuntimeError, NotImplementedError => e
|
|
189
209
|
error!(e.message)
|