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
|
@@ -11,6 +11,21 @@ module HaveAPI::Extensions
|
|
|
11
11
|
# is added. Some helper methods are taken either from Sinatra or Rack.
|
|
12
12
|
class ExceptionMailer < Base
|
|
13
13
|
Frame = Struct.new(:filename, :lineno, :function, :context_line)
|
|
14
|
+
FILTERED_VALUE = '[FILTERED]'.freeze
|
|
15
|
+
SENSITIVE_KEY_PATTERN = /
|
|
16
|
+
authorization|cookie|password|passwd|passphrase|secret|token|
|
|
17
|
+
api[_-]?key|credential|jwt|session|csrf|query_string|form_vars|
|
|
18
|
+
request_uri|original_fullpath|fullpath
|
|
19
|
+
/ix
|
|
20
|
+
SENSITIVE_STRING_PATTERN = /
|
|
21
|
+
(
|
|
22
|
+
(?:authorization|cookie|password|passwd|passphrase|secret|token|
|
|
23
|
+
api[_-]?key|credential|jwt|session|csrf)
|
|
24
|
+
[^=:\s&;<>]{0,64}
|
|
25
|
+
\s*(?:=|:|=>)\s*["']?
|
|
26
|
+
)
|
|
27
|
+
[^&;\s<"'}]+
|
|
28
|
+
/ix
|
|
14
29
|
|
|
15
30
|
# @param opts [Hash] options
|
|
16
31
|
# @option opts to [String] recipient address
|
|
@@ -24,21 +39,33 @@ module HaveAPI::Extensions
|
|
|
24
39
|
|
|
25
40
|
def enabled(server)
|
|
26
41
|
HaveAPI::Action.connect_hook(:exec_exception) do |ret, context, e|
|
|
27
|
-
|
|
42
|
+
safe_log(context, e)
|
|
28
43
|
ret
|
|
29
44
|
end
|
|
30
45
|
|
|
31
46
|
server.connect_hook(:description_exception) do |ret, context, e|
|
|
32
|
-
|
|
47
|
+
safe_log(context, e)
|
|
33
48
|
ret
|
|
34
49
|
end
|
|
50
|
+
|
|
51
|
+
server.connect_hook(:request_exception) do |ret, context, e|
|
|
52
|
+
safe_log(context, e)
|
|
53
|
+
ret
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def safe_log(context, exception)
|
|
58
|
+
log(context, exception)
|
|
59
|
+
rescue StandardError => e
|
|
60
|
+
warn "HaveAPI::Extensions::ExceptionMailer failed: #{e.class}: #{e.message}"
|
|
35
61
|
end
|
|
36
62
|
|
|
37
63
|
def log(context, exception)
|
|
38
|
-
|
|
39
|
-
|
|
64
|
+
request_context = context&.request
|
|
65
|
+
req = request_context.respond_to?(:request) ? request_context.request : request_context
|
|
66
|
+
path = request_path(context, req)
|
|
40
67
|
|
|
41
|
-
frames = exception.backtrace.map do |line|
|
|
68
|
+
frames = Array(exception.backtrace).map do |line|
|
|
42
69
|
frame = Frame.new
|
|
43
70
|
|
|
44
71
|
next unless line =~ /(.*?):(\d+)(:in `(.*)')?/
|
|
@@ -57,14 +84,21 @@ module HaveAPI::Extensions
|
|
|
57
84
|
|
|
58
85
|
frame
|
|
59
86
|
end.compact
|
|
87
|
+
frames = [Frame.new('(unknown)', 0, nil, nil)] if frames.empty?
|
|
60
88
|
|
|
61
|
-
|
|
89
|
+
args = redact(context&.args)
|
|
90
|
+
path_params = redact(context&.path_params)
|
|
91
|
+
input = redact(context&.input)
|
|
92
|
+
get = request_params(req, :GET)
|
|
93
|
+
post = request_params(req, :POST)
|
|
94
|
+
cookies = request_cookies(req)
|
|
95
|
+
env = redact(request_env(request_context, req))
|
|
62
96
|
|
|
63
97
|
user =
|
|
64
|
-
if context
|
|
98
|
+
if context&.current_user.respond_to?(:id)
|
|
65
99
|
context.current_user.id
|
|
66
100
|
else
|
|
67
|
-
context
|
|
101
|
+
context&.current_user
|
|
68
102
|
end
|
|
69
103
|
|
|
70
104
|
mail(context, exception, TEMPLATE.result(binding))
|
|
@@ -114,6 +148,91 @@ module HaveAPI::Extensions
|
|
|
114
148
|
end
|
|
115
149
|
end
|
|
116
150
|
|
|
151
|
+
def request_path(context, req)
|
|
152
|
+
if req.respond_to?(:script_name) && req.respond_to?(:path_info)
|
|
153
|
+
return filter_query_string((req.script_name + req.path_info).squeeze('/'))
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
filter_query_string(
|
|
157
|
+
if context.respond_to?(:resolved_path)
|
|
158
|
+
context.resolved_path
|
|
159
|
+
elsif context.respond_to?(:path)
|
|
160
|
+
context.path
|
|
161
|
+
else
|
|
162
|
+
'(unknown)'
|
|
163
|
+
end
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def request_env(request_context, req)
|
|
168
|
+
if request_context.respond_to?(:env)
|
|
169
|
+
request_context.env
|
|
170
|
+
elsif req.respond_to?(:env)
|
|
171
|
+
req.env
|
|
172
|
+
else
|
|
173
|
+
{}
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def request_params(req, method)
|
|
178
|
+
return {} unless req.respond_to?(method)
|
|
179
|
+
|
|
180
|
+
redact(req.public_send(method) || {})
|
|
181
|
+
rescue StandardError
|
|
182
|
+
{}
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def request_cookies(req)
|
|
186
|
+
return {} unless req.respond_to?(:cookies)
|
|
187
|
+
|
|
188
|
+
cookies = req.cookies || {}
|
|
189
|
+
|
|
190
|
+
cookies.each_with_object({}) do |(key, _value), ret|
|
|
191
|
+
ret[key] = FILTERED_VALUE
|
|
192
|
+
end
|
|
193
|
+
rescue StandardError
|
|
194
|
+
{}
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def filter_query_string(path)
|
|
198
|
+
return path unless path.respond_to?(:sub)
|
|
199
|
+
|
|
200
|
+
path.sub(/\?.*/, "?#{FILTERED_VALUE}")
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def redact(value, key = nil, seen = nil)
|
|
204
|
+
return FILTERED_VALUE if sensitive_key?(key)
|
|
205
|
+
|
|
206
|
+
seen ||= {}.compare_by_identity
|
|
207
|
+
|
|
208
|
+
case value
|
|
209
|
+
when Hash
|
|
210
|
+
return value if seen.has_key?(value)
|
|
211
|
+
|
|
212
|
+
seen[value] = true
|
|
213
|
+
value.each_with_object({}) do |(inner_key, inner_value), ret|
|
|
214
|
+
ret[inner_key] = redact(inner_value, inner_key, seen)
|
|
215
|
+
end
|
|
216
|
+
when Array
|
|
217
|
+
return value if seen.has_key?(value)
|
|
218
|
+
|
|
219
|
+
seen[value] = true
|
|
220
|
+
value.map { |inner_value| redact(inner_value, nil, seen) }
|
|
221
|
+
when String
|
|
222
|
+
redact_string(value)
|
|
223
|
+
else
|
|
224
|
+
value
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def sensitive_key?(key)
|
|
229
|
+
key && key.to_s.match?(SENSITIVE_KEY_PATTERN)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def redact_string(value)
|
|
233
|
+
value.gsub(SENSITIVE_STRING_PATTERN, "\\1#{FILTERED_VALUE}")
|
|
234
|
+
end
|
|
235
|
+
|
|
117
236
|
TEMPLATE = ERB.new(<<~END
|
|
118
237
|
<!DOCTYPE html>
|
|
119
238
|
<html>
|
|
@@ -224,15 +343,15 @@ module HaveAPI::Extensions
|
|
|
224
343
|
</tr>
|
|
225
344
|
<tr>
|
|
226
345
|
<th>Arguments</th>
|
|
227
|
-
<td><%=h
|
|
346
|
+
<td><%=h args %></td>
|
|
228
347
|
</tr>
|
|
229
348
|
<tr>
|
|
230
349
|
<th>Path parameters</th>
|
|
231
|
-
<td><%=h
|
|
350
|
+
<td><%=h path_params %></td>
|
|
232
351
|
</tr>
|
|
233
352
|
<tr>
|
|
234
353
|
<th>Input</th>
|
|
235
|
-
<td><%=h
|
|
354
|
+
<td><%=h input %></td>
|
|
236
355
|
</tr>
|
|
237
356
|
<tr>
|
|
238
357
|
<th>User</th>
|
|
@@ -269,13 +388,13 @@ module HaveAPI::Extensions
|
|
|
269
388
|
</div> <!-- /BACKTRACE -->
|
|
270
389
|
<div id="get">
|
|
271
390
|
<h3 id="get-info">GET</h3>
|
|
272
|
-
<% if
|
|
391
|
+
<% if !get.empty? %>
|
|
273
392
|
<table class="req">
|
|
274
393
|
<tr>
|
|
275
394
|
<th>Variable</th>
|
|
276
395
|
<th>Value</th>
|
|
277
396
|
</tr>
|
|
278
|
-
<%
|
|
397
|
+
<% get.sort_by { |k, v| k.to_s }.each { |key, val| %>
|
|
279
398
|
<tr>
|
|
280
399
|
<td><%=h key %></td>
|
|
281
400
|
<td class="code"><div><%=h val.inspect %></div></td>
|
|
@@ -289,13 +408,13 @@ module HaveAPI::Extensions
|
|
|
289
408
|
</div> <!-- /GET -->
|
|
290
409
|
<div id="post">
|
|
291
410
|
<h3 id="post-info">POST</h3>
|
|
292
|
-
<% if
|
|
411
|
+
<% if !post.empty? %>
|
|
293
412
|
<table class="req">
|
|
294
413
|
<tr>
|
|
295
414
|
<th>Variable</th>
|
|
296
415
|
<th>Value</th>
|
|
297
416
|
</tr>
|
|
298
|
-
<%
|
|
417
|
+
<% post.sort_by { |k, v| k.to_s }.each { |key, val| %>
|
|
299
418
|
<tr>
|
|
300
419
|
<td><%=h key %></td>
|
|
301
420
|
<td class="code"><div><%=h val.inspect %></div></td>
|
|
@@ -309,13 +428,13 @@ module HaveAPI::Extensions
|
|
|
309
428
|
</div> <!-- /POST -->
|
|
310
429
|
<div id="cookies">
|
|
311
430
|
<h3 id="cookie-info">COOKIES</h3>
|
|
312
|
-
<% unless
|
|
431
|
+
<% unless cookies.empty? %>
|
|
313
432
|
<table class="req">
|
|
314
433
|
<tr>
|
|
315
434
|
<th>Variable</th>
|
|
316
435
|
<th>Value</th>
|
|
317
436
|
</tr>
|
|
318
|
-
<%
|
|
437
|
+
<% cookies.each { |key, val| %>
|
|
319
438
|
<tr>
|
|
320
439
|
<td><%=h key %></td>
|
|
321
440
|
<td class="code"><div><%=h val.inspect %></div></td>
|
data/lib/haveapi/i18n.rb
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require 'i18n'
|
|
2
|
+
|
|
3
|
+
module HaveAPI
|
|
4
|
+
class LocalizedMessage
|
|
5
|
+
attr_reader :key, :values, :default
|
|
6
|
+
|
|
7
|
+
def initialize(key, default: nil, scope: nil, **values)
|
|
8
|
+
@key = normalize_key(key, scope)
|
|
9
|
+
@default = default
|
|
10
|
+
@values = values
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def translate(extra_values = {})
|
|
14
|
+
values = localized_values(@values.merge(extra_values))
|
|
15
|
+
opts = values
|
|
16
|
+
opts[:default] = @default if @default
|
|
17
|
+
|
|
18
|
+
::I18n.t(@key, **opts)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_s
|
|
22
|
+
translate
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def normalize_key(key, scope)
|
|
28
|
+
return key.to_s unless scope
|
|
29
|
+
|
|
30
|
+
"#{scope}.#{key}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def localized_values(values)
|
|
34
|
+
values.transform_values do |value|
|
|
35
|
+
case value
|
|
36
|
+
when Array
|
|
37
|
+
value.map { |v| HaveAPI.localize(v) }.join('; ')
|
|
38
|
+
else
|
|
39
|
+
HaveAPI.localize(value)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module I18n
|
|
46
|
+
class << self
|
|
47
|
+
def setup
|
|
48
|
+
locale_dir = File.expand_path('locales', __dir__)
|
|
49
|
+
::I18n.load_path |= Dir[File.join(locale_dir, '*.yml')]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def available_locales
|
|
53
|
+
locale_dir = File.expand_path('locales', __dir__)
|
|
54
|
+
|
|
55
|
+
Dir[File.join(locale_dir, '*.yml')].map do |path|
|
|
56
|
+
File.basename(path, '.yml').to_sym
|
|
57
|
+
end.sort
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def accept_language(header, available_locales)
|
|
61
|
+
header.to_s.split(',').each_with_index.filter_map do |raw, index|
|
|
62
|
+
tag, *params = raw.strip.split(';')
|
|
63
|
+
next if tag.nil? || tag.empty?
|
|
64
|
+
|
|
65
|
+
q = params.map(&:strip).grep(/\Aq=/).first
|
|
66
|
+
weight = q ? q.split('=', 2).last.to_f : 1.0
|
|
67
|
+
next if weight <= 0.0
|
|
68
|
+
|
|
69
|
+
[tag, weight, index]
|
|
70
|
+
end.sort_by { |(_, weight, index)| [-weight, index] }.each do |tag, _, _|
|
|
71
|
+
locale = normalize_locale(tag, available_locales)
|
|
72
|
+
return locale if locale
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
nil
|
|
76
|
+
rescue StandardError
|
|
77
|
+
nil
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def normalize_locale(locale, available_locales)
|
|
81
|
+
return if locale.nil?
|
|
82
|
+
|
|
83
|
+
tag = locale.to_s.strip.tr('_', '-')
|
|
84
|
+
return if tag.empty? || tag == '*'
|
|
85
|
+
|
|
86
|
+
available = Array(available_locales).map(&:to_s)
|
|
87
|
+
candidates = [tag, tag.split('-').first].compact.map(&:downcase).uniq
|
|
88
|
+
|
|
89
|
+
candidates.each do |candidate|
|
|
90
|
+
match = available.detect { |v| v.downcase == candidate }
|
|
91
|
+
return match.to_sym if match
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
nil
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class << self
|
|
100
|
+
def message(key, default: nil, scope: nil, **values)
|
|
101
|
+
LocalizedMessage.new(key, default:, scope:, **values)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def t(key, default: nil, scope: nil, **values)
|
|
105
|
+
message(key, default:, scope:, **values).translate
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def localize(value, **extra_values)
|
|
109
|
+
case value
|
|
110
|
+
when LocalizedMessage
|
|
111
|
+
value.translate(extra_values)
|
|
112
|
+
when Array
|
|
113
|
+
value.map { |v| localize(v, **extra_values) }
|
|
114
|
+
when Hash
|
|
115
|
+
value.transform_values { |v| localize(v, **extra_values) }
|
|
116
|
+
when String
|
|
117
|
+
extra_values.empty? ? value : format(value, extra_values)
|
|
118
|
+
else
|
|
119
|
+
value
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
HaveAPI::I18n.setup
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# This file is generated from i18n/haveapi.yml.
|
|
2
|
+
# Do not edit it manually; manual changes will be overwritten.
|
|
3
|
+
# Update i18n/haveapi.yml and run bundle exec rake i18n:update.
|
|
4
|
+
---
|
|
5
|
+
cs:
|
|
6
|
+
haveapi:
|
|
7
|
+
action_state:
|
|
8
|
+
cancellation_failed: zrušení selhalo
|
|
9
|
+
not_found: stav akce nebyl nalezen
|
|
10
|
+
timeout_max: timeout může být nejvýše %{max}
|
|
11
|
+
authentication:
|
|
12
|
+
failed: přihlášení selhalo
|
|
13
|
+
invalid_credentials: neplatné přihlašovací údaje
|
|
14
|
+
multiple_oauth2_tokens: Bylo poskytnuto více OAuth2 tokenů
|
|
15
|
+
multiple_tokens: Bylo poskytnuto více ověřovacích tokenů
|
|
16
|
+
renew_failed: obnovení selhalo
|
|
17
|
+
required: Akce vyžaduje přihlášení uživatele
|
|
18
|
+
revoke_failed: zrušení selhalo
|
|
19
|
+
token_required: Akce vyžaduje přihlášení uživatele tokenem
|
|
20
|
+
authorization:
|
|
21
|
+
insufficient_permissions: Přístup odepřen. Nedostatečná oprávnění.
|
|
22
|
+
errors:
|
|
23
|
+
action_not_found: Akce nebyla nalezena
|
|
24
|
+
bad_accept_header: Chybná hlavička Accept
|
|
25
|
+
bad_json_syntax: Chybná syntaxe JSON
|
|
26
|
+
json_body_object: Tělo JSON musí být objekt
|
|
27
|
+
server_error: Došlo k chybě serveru
|
|
28
|
+
unsupported_content_type: Nepodporovaný Content-Type
|
|
29
|
+
pagination:
|
|
30
|
+
limit_max: limit může být nejvýše %{max}
|
|
31
|
+
parameters:
|
|
32
|
+
action_state:
|
|
33
|
+
can_cancel:
|
|
34
|
+
description: Je-li true, spuštění této akce lze zrušit
|
|
35
|
+
label: Lze zrušit
|
|
36
|
+
created_at:
|
|
37
|
+
label: Vytvořeno
|
|
38
|
+
current:
|
|
39
|
+
label: Aktuální průběh
|
|
40
|
+
finished:
|
|
41
|
+
label: Dokončeno
|
|
42
|
+
label:
|
|
43
|
+
label: Popisek
|
|
44
|
+
poll:
|
|
45
|
+
current:
|
|
46
|
+
description: průběh, se kterým se porovnává při nastaveném update_in
|
|
47
|
+
status:
|
|
48
|
+
description: stav, se kterým se porovnává při nastaveném update_in
|
|
49
|
+
timeout:
|
|
50
|
+
description: v sekundách
|
|
51
|
+
label: Časový limit
|
|
52
|
+
total:
|
|
53
|
+
description: průběh, se kterým se porovnává při nastaveném update_in
|
|
54
|
+
update_in:
|
|
55
|
+
description: počet sekund, po kterém se stav vrátí, pokud se průběh změnil
|
|
56
|
+
label: Průběh
|
|
57
|
+
status:
|
|
58
|
+
description: Určuje, zda akce probíhá nebo selhává
|
|
59
|
+
label: Stav
|
|
60
|
+
total:
|
|
61
|
+
description: Akce je dokončena, když se aktuální průběh rovná celkovému
|
|
62
|
+
label: Celkem
|
|
63
|
+
unit:
|
|
64
|
+
description: Jednotka aktuálního a celkového průběhu
|
|
65
|
+
label: Jednotka
|
|
66
|
+
updated_at:
|
|
67
|
+
description: Kdy byl průběh naposledy aktualizován
|
|
68
|
+
label: Aktualizováno
|
|
69
|
+
action_state_id:
|
|
70
|
+
description: ID objektu ActionState pro dotazování stavu. Pokud je null, akce
|
|
71
|
+
není pro aktuální volání blokující.
|
|
72
|
+
label: ID stavu akce
|
|
73
|
+
active_record:
|
|
74
|
+
includes:
|
|
75
|
+
description: |-
|
|
76
|
+
Seznam názvů asociovaných prostředků oddělených čárkou.
|
|
77
|
+
Vnořené asociace se zapisují pomocí '__' mezi názvy prostředků.
|
|
78
|
+
Například 'user,node' přeloží tyto dvě asociace.
|
|
79
|
+
Pro přeložení dalších asociací uzlu použijte např. 'user,node__location',
|
|
80
|
+
pro ještě hlubší úroveň např. 'user,node__location__environment'.
|
|
81
|
+
label: Zahrnuté asociace
|
|
82
|
+
path_params:
|
|
83
|
+
description: Pole parametrů potřebných k přeložení URL na tento objekt
|
|
84
|
+
label: Parametry URL
|
|
85
|
+
resolved:
|
|
86
|
+
description: True, pokud je asociace přeložena
|
|
87
|
+
label: Přeloženo
|
|
88
|
+
authentication:
|
|
89
|
+
token:
|
|
90
|
+
interval:
|
|
91
|
+
description: Jak dlouho bude požadovaný token platný, v sekundách.
|
|
92
|
+
label: Interval
|
|
93
|
+
lifetime:
|
|
94
|
+
description: |-
|
|
95
|
+
fixed - token má pevnou dobu platnosti a nelze ho obnovit
|
|
96
|
+
renewable_manual - token lze obnovit, ale musí se obnovit ručně akcí renew
|
|
97
|
+
renewable_auto - token se při každém použití automaticky obnoví na now+interval
|
|
98
|
+
permanent - token bude platný navždy, dokud nebude smazán
|
|
99
|
+
label: Životnost
|
|
100
|
+
password:
|
|
101
|
+
label: Heslo
|
|
102
|
+
scope:
|
|
103
|
+
label: Rozsah
|
|
104
|
+
user:
|
|
105
|
+
label: Uživatel
|
|
106
|
+
default:
|
|
107
|
+
count:
|
|
108
|
+
label: Vrátit počet všech položek
|
|
109
|
+
total_count:
|
|
110
|
+
label: Celkový počet všech položek
|
|
111
|
+
metadata:
|
|
112
|
+
'no':
|
|
113
|
+
label: Zakázat metadata
|
|
114
|
+
paginable:
|
|
115
|
+
from_id:
|
|
116
|
+
description: Vypsat objekty s větším/menším ID
|
|
117
|
+
label: Od ID
|
|
118
|
+
limit:
|
|
119
|
+
description: Počet objektů k načtení
|
|
120
|
+
label: Limit
|
|
121
|
+
types:
|
|
122
|
+
invalid_boolean: neplatná pravdivostní hodnota %{value}
|
|
123
|
+
invalid_datetime: není ve formátu ISO 8601 '%{value}'
|
|
124
|
+
invalid_float: neplatné desetinné číslo %{value}
|
|
125
|
+
invalid_integer: neplatné celé číslo %{value}
|
|
126
|
+
invalid_string: neplatný řetězec %{value}
|
|
127
|
+
validation:
|
|
128
|
+
cannot_be_null: nesmí být null
|
|
129
|
+
includes_only_strings: includes musí obsahovat jen řetězce
|
|
130
|
+
includes_string_or_array: includes musí být řetězec nebo pole
|
|
131
|
+
input_parameters_not_valid: vstupní parametry nejsou platné
|
|
132
|
+
invalid_id: neplatné ID %{value}
|
|
133
|
+
invalid_input_layout: neplatná struktura vstupu
|
|
134
|
+
invalid_path_parameter_encoding: neplatné kódování parametru cesty
|
|
135
|
+
invalid_string_encoding: neplatné kódování řetězce
|
|
136
|
+
required_parameter_missing: povinný parametr chybí
|
|
137
|
+
resource_not_found: prostředek nebyl nalezen
|
|
138
|
+
validators:
|
|
139
|
+
acceptance:
|
|
140
|
+
accepted: musí být %{value}
|
|
141
|
+
confirmation:
|
|
142
|
+
different: musí být odlišné od %{parameter}
|
|
143
|
+
same: musí být stejné jako %{parameter}
|
|
144
|
+
exclusion:
|
|
145
|
+
excluded: "%{value} nelze použít"
|
|
146
|
+
format:
|
|
147
|
+
invalid: "%{value} nemá platný formát"
|
|
148
|
+
inclusion:
|
|
149
|
+
included: "%{value} nelze použít"
|
|
150
|
+
length:
|
|
151
|
+
equals: délka musí být %{equals}
|
|
152
|
+
max: délka může být nejvýše %{max}
|
|
153
|
+
min: délka musí být alespoň %{min}
|
|
154
|
+
range: délka musí být v rozsahu <%{min}, %{max}>
|
|
155
|
+
numericality:
|
|
156
|
+
composite: "%{requirements}"
|
|
157
|
+
even: sudé číslo
|
|
158
|
+
max: musí být nejvýše %{max}
|
|
159
|
+
min: musí být alespoň %{min}
|
|
160
|
+
mod: zbytek po dělení %{mod} musí být nula
|
|
161
|
+
number: musí být číslo
|
|
162
|
+
odd: liché číslo
|
|
163
|
+
range: musí být v rozsahu <%{min}, %{max}>
|
|
164
|
+
step: v krocích po %{step}
|
|
165
|
+
presence:
|
|
166
|
+
non_empty: musí být vyplněno a neprázdné
|
|
167
|
+
present: musí být vyplněno
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# This file is generated from i18n/haveapi.yml.
|
|
2
|
+
# Do not edit it manually; manual changes will be overwritten.
|
|
3
|
+
# Update i18n/haveapi.yml and run bundle exec rake i18n:update.
|
|
4
|
+
---
|
|
5
|
+
en:
|
|
6
|
+
haveapi:
|
|
7
|
+
action_state:
|
|
8
|
+
cancellation_failed: cancellation failed
|
|
9
|
+
not_found: action state not found
|
|
10
|
+
timeout_max: timeout has to be maximally %{max}
|
|
11
|
+
authentication:
|
|
12
|
+
failed: authentication failed
|
|
13
|
+
invalid_credentials: invalid authentication credentials
|
|
14
|
+
multiple_oauth2_tokens: Multiple OAuth2 tokens provided
|
|
15
|
+
multiple_tokens: Multiple authentication tokens provided
|
|
16
|
+
renew_failed: renew failed
|
|
17
|
+
required: Action requires user to authenticate
|
|
18
|
+
revoke_failed: revoke failed
|
|
19
|
+
token_required: Action requires user to authenticate with a token
|
|
20
|
+
authorization:
|
|
21
|
+
insufficient_permissions: Access denied. Insufficient permissions.
|
|
22
|
+
errors:
|
|
23
|
+
action_not_found: Action not found
|
|
24
|
+
bad_accept_header: Bad Accept header
|
|
25
|
+
bad_json_syntax: Bad JSON syntax
|
|
26
|
+
json_body_object: JSON body must be an object
|
|
27
|
+
server_error: Server error occurred
|
|
28
|
+
unsupported_content_type: Unsupported Content-Type
|
|
29
|
+
pagination:
|
|
30
|
+
limit_max: limit has to be maximally %{max}
|
|
31
|
+
parameters:
|
|
32
|
+
action_state:
|
|
33
|
+
can_cancel:
|
|
34
|
+
description: When true, execution of this action can be cancelled
|
|
35
|
+
label: Can cancel
|
|
36
|
+
created_at:
|
|
37
|
+
label: Created at
|
|
38
|
+
current:
|
|
39
|
+
label: Current progress
|
|
40
|
+
finished:
|
|
41
|
+
label: Finished
|
|
42
|
+
label:
|
|
43
|
+
label: Label
|
|
44
|
+
poll:
|
|
45
|
+
current:
|
|
46
|
+
description: progress to check with if update_in is set
|
|
47
|
+
status:
|
|
48
|
+
description: status to check with if update_in is set
|
|
49
|
+
timeout:
|
|
50
|
+
description: in seconds
|
|
51
|
+
label: Timeout
|
|
52
|
+
total:
|
|
53
|
+
description: progress to check with if update_in is set
|
|
54
|
+
update_in:
|
|
55
|
+
description: number of seconds after which the state is returned if the
|
|
56
|
+
progress has changed
|
|
57
|
+
label: Progress
|
|
58
|
+
status:
|
|
59
|
+
description: Determines whether the action is proceeding or failing
|
|
60
|
+
label: Status
|
|
61
|
+
total:
|
|
62
|
+
description: The action is finished when current equals to total
|
|
63
|
+
label: Total
|
|
64
|
+
unit:
|
|
65
|
+
description: Unit of current and total
|
|
66
|
+
label: Unit
|
|
67
|
+
updated_at:
|
|
68
|
+
description: When was the progress last updated
|
|
69
|
+
label: Updated at
|
|
70
|
+
action_state_id:
|
|
71
|
+
description: ID of ActionState object for state querying. When null, the action
|
|
72
|
+
is not blocking for the current invocation.
|
|
73
|
+
label: Action state ID
|
|
74
|
+
active_record:
|
|
75
|
+
includes:
|
|
76
|
+
description: |-
|
|
77
|
+
A list of names of associated resources separated by a comma.
|
|
78
|
+
Nested associations are declared with '__' between resource names.
|
|
79
|
+
For example, 'user,node' will resolve the two associations.
|
|
80
|
+
To resolve further associations of node, use e.g. 'user,node__location',
|
|
81
|
+
to go even deeper, use e.g. 'user,node__location__environment'.
|
|
82
|
+
label: Included associations
|
|
83
|
+
path_params:
|
|
84
|
+
description: An array of parameters needed to resolve URL to this object
|
|
85
|
+
label: URL parameters
|
|
86
|
+
resolved:
|
|
87
|
+
description: True if the association is resolved
|
|
88
|
+
label: Resolved
|
|
89
|
+
authentication:
|
|
90
|
+
token:
|
|
91
|
+
interval:
|
|
92
|
+
description: How long will requested token be valid, in seconds.
|
|
93
|
+
label: Interval
|
|
94
|
+
lifetime:
|
|
95
|
+
description: |-
|
|
96
|
+
fixed - the token has a fixed validity period, it cannot be renewed
|
|
97
|
+
renewable_manual - the token can be renewed, but it must be done manually via renew action
|
|
98
|
+
renewable_auto - the token is renewed automatically to now+interval every time it is used
|
|
99
|
+
permanent - the token will be valid forever, unless deleted
|
|
100
|
+
label: Lifetime
|
|
101
|
+
password:
|
|
102
|
+
label: Password
|
|
103
|
+
scope:
|
|
104
|
+
label: Scope
|
|
105
|
+
user:
|
|
106
|
+
label: User
|
|
107
|
+
default:
|
|
108
|
+
count:
|
|
109
|
+
label: Return the count of all items
|
|
110
|
+
total_count:
|
|
111
|
+
label: Total count of all items
|
|
112
|
+
metadata:
|
|
113
|
+
'no':
|
|
114
|
+
label: Disable metadata
|
|
115
|
+
paginable:
|
|
116
|
+
from_id:
|
|
117
|
+
description: List objects with greater/lesser ID
|
|
118
|
+
label: From ID
|
|
119
|
+
limit:
|
|
120
|
+
description: Number of objects to retrieve
|
|
121
|
+
label: Limit
|
|
122
|
+
types:
|
|
123
|
+
invalid_boolean: not a valid boolean %{value}
|
|
124
|
+
invalid_datetime: not in ISO 8601 format '%{value}'
|
|
125
|
+
invalid_float: not a valid float %{value}
|
|
126
|
+
invalid_integer: not a valid integer %{value}
|
|
127
|
+
invalid_string: not a valid string %{value}
|
|
128
|
+
validation:
|
|
129
|
+
cannot_be_null: cannot be null
|
|
130
|
+
includes_only_strings: includes must contain only strings
|
|
131
|
+
includes_string_or_array: includes must be a string or array
|
|
132
|
+
input_parameters_not_valid: input parameters not valid
|
|
133
|
+
invalid_id: not a valid id %{value}
|
|
134
|
+
invalid_input_layout: invalid input layout
|
|
135
|
+
invalid_path_parameter_encoding: invalid path parameter encoding
|
|
136
|
+
invalid_string_encoding: invalid string encoding
|
|
137
|
+
required_parameter_missing: required parameter missing
|
|
138
|
+
resource_not_found: resource not found
|
|
139
|
+
validators:
|
|
140
|
+
acceptance:
|
|
141
|
+
accepted: has to be %{value}
|
|
142
|
+
confirmation:
|
|
143
|
+
different: must be different from %{parameter}
|
|
144
|
+
same: must be the same as %{parameter}
|
|
145
|
+
exclusion:
|
|
146
|
+
excluded: "%{value} cannot be used"
|
|
147
|
+
format:
|
|
148
|
+
invalid: "%{value} is not in a valid format"
|
|
149
|
+
inclusion:
|
|
150
|
+
included: "%{value} cannot be used"
|
|
151
|
+
length:
|
|
152
|
+
equals: length has to be %{equals}
|
|
153
|
+
max: length has to be maximally %{max}
|
|
154
|
+
min: length has to be minimally %{min}
|
|
155
|
+
range: length has to be in range <%{min}, %{max}>
|
|
156
|
+
numericality:
|
|
157
|
+
composite: "%{requirements}"
|
|
158
|
+
even: even
|
|
159
|
+
max: has to be maximally %{max}
|
|
160
|
+
min: has to be minimally %{min}
|
|
161
|
+
mod: mod %{mod} must equal zero
|
|
162
|
+
number: has to be a number
|
|
163
|
+
odd: odd
|
|
164
|
+
range: has to be in range <%{min}, %{max}>
|
|
165
|
+
step: in steps of %{step}
|
|
166
|
+
presence:
|
|
167
|
+
non_empty: must be present and non-empty
|
|
168
|
+
present: must be present
|