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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +139 -0
  3. data/Rakefile +1 -0
  4. data/haveapi.gemspec +2 -1
  5. data/lib/haveapi/action.rb +26 -9
  6. data/lib/haveapi/actions/default.rb +5 -2
  7. data/lib/haveapi/actions/paginable.rb +8 -4
  8. data/lib/haveapi/authentication/oauth2/provider.rb +1 -1
  9. data/lib/haveapi/authentication/token/config.rb +10 -3
  10. data/lib/haveapi/authentication/token/provider.rb +22 -21
  11. data/lib/haveapi/context.rb +4 -1
  12. data/lib/haveapi/i18n.rb +125 -0
  13. data/lib/haveapi/locales/cs.yml +167 -0
  14. data/lib/haveapi/locales/en.yml +168 -0
  15. data/lib/haveapi/metadata.rb +25 -3
  16. data/lib/haveapi/model_adapters/active_record.rb +40 -26
  17. data/lib/haveapi/output_formatter.rb +2 -2
  18. data/lib/haveapi/parameters/metadata_i18n.rb +179 -0
  19. data/lib/haveapi/parameters/resource.rb +18 -7
  20. data/lib/haveapi/parameters/typed.rb +27 -20
  21. data/lib/haveapi/params.rb +76 -7
  22. data/lib/haveapi/resource.rb +1 -1
  23. data/lib/haveapi/resources/action_state.rb +47 -27
  24. data/lib/haveapi/server.rb +156 -16
  25. data/lib/haveapi/spec/api_builder.rb +25 -0
  26. data/lib/haveapi/spec/spec_methods.rb +10 -0
  27. data/lib/haveapi/tasks/i18n.rb +198 -0
  28. data/lib/haveapi/validator_chain.rb +1 -1
  29. data/lib/haveapi/validators/acceptance.rb +5 -2
  30. data/lib/haveapi/validators/confirmation.rb +5 -2
  31. data/lib/haveapi/validators/exclusion.rb +2 -2
  32. data/lib/haveapi/validators/format.rb +5 -2
  33. data/lib/haveapi/validators/inclusion.rb +2 -2
  34. data/lib/haveapi/validators/length.rb +5 -5
  35. data/lib/haveapi/validators/numericality.rb +20 -22
  36. data/lib/haveapi/validators/presence.rb +4 -2
  37. data/lib/haveapi/version.rb +1 -1
  38. data/lib/haveapi.rb +1 -0
  39. data/spec/authentication/oauth2_spec.rb +10 -0
  40. data/spec/i18n_spec.rb +520 -0
  41. data/spec/params_spec.rb +183 -0
  42. 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: @label,
66
- description: @desc,
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 HaveAPI::ValidationError, 'cannot be null'
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 HaveAPI::ValidationError, 'invalid string encoding'
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 HaveAPI::ValidationError, 'resource not found'
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: @label,
60
- description: @desc,
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 HaveAPI::ValidationError, 'cannot be null'
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 HaveAPI::ValidationError, 'cannot be null'
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 HaveAPI::ValidationError, 'invalid string encoding'
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 HaveAPI::ValidationError, "not a valid integer #{raw.inspect}"
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 HaveAPI::ValidationError, "not a valid integer #{raw.inspect}"
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 HaveAPI::ValidationError, "not a valid integer #{raw.inspect}"
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 HaveAPI::ValidationError, "not a valid float #{raw.inspect}" if s.empty?
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 HaveAPI::ValidationError, "not a valid float #{raw.inspect}"
231
+ raise validation_error('haveapi.types.invalid_float', value: raw.inspect)
229
232
  end
230
233
 
231
234
  else
232
- raise HaveAPI::ValidationError, "not a valid float #{raw.inspect}"
235
+ raise validation_error('haveapi.types.invalid_float', value: raw.inspect)
233
236
  end
234
237
 
235
- raise HaveAPI::ValidationError, "not a valid float #{raw.inspect}" unless f.finite?
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 HaveAPI::ValidationError, "not a valid boolean #{raw.inspect}" if s.empty?
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 HaveAPI::ValidationError, "not a valid boolean #{raw.inspect}"
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 HaveAPI::ValidationError, "not in ISO 8601 format '#{raw}'"
264
+ raise validation_error('haveapi.types.invalid_datetime', value: raw)
262
265
  end
263
266
 
264
267
  if strip_string(raw).empty?
265
- raise HaveAPI::ValidationError, "not in ISO 8601 format '#{raw}'"
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 HaveAPI::ValidationError, "not in ISO 8601 format '#{raw}'"
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 HaveAPI::ValidationError, "not a valid string #{raw.inspect}"
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
@@ -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('invalid input layout', {}) if any_required_params?
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('invalid input layout', {})
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] = ['required parameter missing']
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.message
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('input parameters not valid', errors)
343
+ raise ValidationError.new(HaveAPI.message('haveapi.validation.input_parameters_not_valid'), errors)
275
344
  end
276
345
 
277
346
  params
@@ -80,7 +80,7 @@ module HaveAPI
80
80
  end
81
81
 
82
82
  def self.describe(hash, context)
83
- ret = { description: desc, actions: {}, resources: {} }
83
+ ret = { description: HaveAPI.localize(desc), actions: {}, resources: {} }
84
84
 
85
85
  context.resource = self
86
86
 
@@ -8,19 +8,25 @@ module HaveAPI::Resources
8
8
 
9
9
  params(:all) do
10
10
  id :id
11
- string :label, label: 'Label'
12
- bool :finished, label: 'Finished'
13
- bool :status, label: 'Status',
14
- desc: 'Determines whether the action is proceeding or failing'
15
- integer :current, label: 'Current progress'
16
- integer :total, label: 'Total',
17
- desc: 'The action is finished when current equals to total'
18
- string :unit, label: 'Unit', desc: 'Unit of current and total'
19
- bool :can_cancel, label: 'Can cancel',
20
- desc: 'When true, execution of this action can be cancelled'
21
- datetime :created_at, label: 'Created at'
22
- datetime :updated_at, label: 'Updated at',
23
- desc: 'When was the progress last updated'
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, label: 'Timeout', desc: 'in seconds', default: 15, fill: true,
88
- number: { min: 0, max: MAX_TIMEOUT }
89
- float :update_in, label: 'Progress',
90
- desc: 'number of seconds after which the state is returned if the progress ' \
91
- 'has changed',
92
- nullable: true
93
- bool :status, desc: 'status to check with if update_in is set', nullable: true
94
- integer :current, desc: 'progress to check with if update_in is set', nullable: true
95
- integer :total, desc: 'progress to check with if update_in is set', nullable: true
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!("timeout has to be maximally #{MAX_TIMEOUT}", {}, http_status: 400)
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!('action state not found') unless state.valid?
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!('action state not found')
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!('action state not found') unless state.valid?
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!('cancellation failed')
206
+ error!(HaveAPI.message('haveapi.action_state.cancellation_failed'))
187
207
  end
188
208
  rescue RuntimeError, NotImplementedError => e
189
209
  error!(e.message)