haveapi 0.28.3 → 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/extensions/exception_mailer.rb +136 -17
- 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 +287 -94
- 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/extensions/exception_mailer_spec.rb +195 -0
- data/spec/i18n_spec.rb +520 -0
- data/spec/params_spec.rb +183 -0
- data/spec/server/integration_spec.rb +34 -0
- metadata +30 -7
- data/doc/create-client.md +0 -107
- data/doc/json-schema.html +0 -1182
- data/doc/protocol.md +0 -535
- data/doc/protocol.png +0 -0
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)
|