smplkit 3.0.122 → 3.0.123
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/lib/smplkit/_generated/audit/lib/smplkit_audit_client/models/forwarder.rb +5 -17
- data/lib/smplkit/_generated/audit/lib/smplkit_audit_client/models/{http_configuration.rb → forwarder_http_configuration.rb} +7 -7
- data/lib/smplkit/_generated/audit/lib/smplkit_audit_client/models/test_forwarder_request.rb +3 -3
- data/lib/smplkit/_generated/audit/lib/smplkit_audit_client.rb +1 -3
- data/lib/smplkit/_generated/audit/spec/models/{http_configuration_spec.rb → forwarder_http_configuration_spec.rb} +6 -6
- data/lib/smplkit/_generated/audit/spec/models/forwarder_spec.rb +0 -6
- data/lib/smplkit/_generated/jobs/lib/smplkit_jobs_client/models/job.rb +2 -2
- data/lib/smplkit/_generated/jobs/lib/smplkit_jobs_client/models/job_http_configuration.rb +4 -4
- data/lib/smplkit/_generated/jobs/lib/smplkit_jobs_client.rb +0 -2
- data/lib/smplkit/audit/forwarders.rb +18 -24
- data/lib/smplkit/audit/models.rb +175 -103
- data/lib/smplkit/jobs/client.rb +21 -26
- data/lib/smplkit/jobs/models.rb +233 -273
- metadata +3 -11
- data/lib/smplkit/_generated/audit/lib/smplkit_audit_client/models/forwarder_environment.rb +0 -162
- data/lib/smplkit/_generated/audit/lib/smplkit_audit_client/models/http_header.rb +0 -220
- data/lib/smplkit/_generated/audit/spec/models/forwarder_environment_spec.rb +0 -42
- data/lib/smplkit/_generated/audit/spec/models/http_header_spec.rb +0 -42
- data/lib/smplkit/_generated/jobs/lib/smplkit_jobs_client/models/http_header.rb +0 -220
- data/lib/smplkit/_generated/jobs/lib/smplkit_jobs_client/models/job_environment.rb +0 -206
- data/lib/smplkit/_generated/jobs/spec/models/http_header_spec.rb +0 -42
- data/lib/smplkit/_generated/jobs/spec/models/job_environment_spec.rb +0 -66
data/lib/smplkit/jobs/models.rb
CHANGED
|
@@ -75,31 +75,19 @@ module Smplkit
|
|
|
75
75
|
|
|
76
76
|
# Coerce a caller's +environments+ map to {JobEnvironment} instances.
|
|
77
77
|
#
|
|
78
|
-
# Accepts either {JobEnvironment} values or plain hashes
|
|
79
|
-
# (+{ enabled: true, schedule: "0 3 * * *",
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
# +retry_policy+ overrides pass through.
|
|
78
|
+
# Accepts either {JobEnvironment} values or plain hashes of its constructor
|
|
79
|
+
# kwargs (+{ enabled: true, schedule: "0 3 * * *", url: "https://prod/warm",
|
|
80
|
+
# headers: { "Authorization" => "Bearer prod" } }+) so callers can use the
|
|
81
|
+
# lightweight hash form without importing the model. Each hash is splatted
|
|
82
|
+
# into {JobEnvironment.new}; both symbol- and string-keyed hashes work.
|
|
84
83
|
#
|
|
85
84
|
# @api private
|
|
86
85
|
def self.normalize_environments(environments)
|
|
87
86
|
return {} if environments.nil? || environments.empty?
|
|
88
87
|
|
|
89
88
|
environments.each_with_object({}) do |(env_key, value), out|
|
|
90
|
-
out[env_key.to_s] =
|
|
91
|
-
|
|
92
|
-
else
|
|
93
|
-
cfg = value[:configuration] || value["configuration"]
|
|
94
|
-
cfg = HttpConfig.new(**cfg) if cfg.is_a?(Hash)
|
|
95
|
-
JobEnvironment.new(
|
|
96
|
-
enabled: value[:enabled] || value["enabled"] || false,
|
|
97
|
-
schedule: value[:schedule] || value["schedule"],
|
|
98
|
-
timezone: value[:timezone] || value["timezone"],
|
|
99
|
-
retry_policy: value[:retry_policy] || value["retry_policy"],
|
|
100
|
-
configuration: cfg
|
|
101
|
-
)
|
|
102
|
-
end
|
|
89
|
+
out[env_key.to_s] =
|
|
90
|
+
value.is_a?(JobEnvironment) ? value : JobEnvironment.new(**value.transform_keys(&:to_sym))
|
|
103
91
|
end
|
|
104
92
|
end
|
|
105
93
|
|
|
@@ -174,15 +162,6 @@ module Smplkit
|
|
|
174
162
|
end
|
|
175
163
|
end
|
|
176
164
|
|
|
177
|
-
# A single name/value HTTP header on the request a job performs.
|
|
178
|
-
#
|
|
179
|
-
# @!attribute [rw] name
|
|
180
|
-
# @return [String] Header name (e.g. +"Authorization"+, +"Content-Type"+).
|
|
181
|
-
# @!attribute [rw] value
|
|
182
|
-
# @return [String] Header value. Returned in plaintext on reads, so a
|
|
183
|
-
# get-mutate-put round-trip preserves it without re-entering secrets.
|
|
184
|
-
HttpHeader = Struct.new(:name, :value, keyword_init: true)
|
|
185
|
-
|
|
186
165
|
# The HTTP request a job performs when it fires (the +http+ configuration).
|
|
187
166
|
#
|
|
188
167
|
# Extends the shared forwarder shape with the two fields a scheduled job
|
|
@@ -193,9 +172,12 @@ module Smplkit
|
|
|
193
172
|
# @!attribute [rw] url
|
|
194
173
|
# @return [String] Destination URL the job requests on each run.
|
|
195
174
|
# @!attribute [rw] headers
|
|
196
|
-
# @return [
|
|
197
|
-
#
|
|
198
|
-
#
|
|
175
|
+
# @return [Hash{String => String}] Headers attached to every request, as a
|
|
176
|
+
# name→value object (e.g. +{ "Authorization" => "Bearer s3cr3t" }+). Use
|
|
177
|
+
# {#set_header} / {#get_header} to read and write individual headers.
|
|
178
|
+
# Values often carry credentials and are returned in plaintext on reads,
|
|
179
|
+
# so a get-mutate-put round-trip preserves them without re-entering
|
|
180
|
+
# secrets.
|
|
199
181
|
# @!attribute [rw] body
|
|
200
182
|
# @return [String, nil] Request body sent on each run. +nil+ (the default)
|
|
201
183
|
# sends an empty body, suitable for a connectivity ping. Sent verbatim —
|
|
@@ -229,13 +211,31 @@ module Smplkit
|
|
|
229
211
|
success_status: "2xx", timeout: 30, tls_verify: true, ca_cert: nil
|
|
230
212
|
)
|
|
231
213
|
super(
|
|
232
|
-
method: HttpMethod.coerce(method), url: url, headers: headers ||
|
|
233
|
-
success_status: success_status, timeout: timeout, tls_verify: tls_verify, ca_cert: ca_cert
|
|
214
|
+
method: HttpMethod.coerce(method), url: url, headers: (headers || {}).transform_keys(&:to_s),
|
|
215
|
+
body: body, success_status: success_status, timeout: timeout, tls_verify: tls_verify, ca_cert: ca_cert
|
|
234
216
|
)
|
|
235
217
|
end
|
|
236
218
|
|
|
219
|
+
# Set (or replace) a single request header by name.
|
|
220
|
+
#
|
|
221
|
+
# @param name [String] Header name.
|
|
222
|
+
# @param value [String] Header value.
|
|
223
|
+
def set_header(name, value)
|
|
224
|
+
self.headers ||= {}
|
|
225
|
+
headers[name.to_s] = value
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# The value of header +name+, or +nil+ when it is not set.
|
|
229
|
+
#
|
|
230
|
+
# @param name [String] Header name.
|
|
231
|
+
# @return [String, nil]
|
|
232
|
+
def get_header(name)
|
|
233
|
+
(headers || {})[name.to_s]
|
|
234
|
+
end
|
|
235
|
+
|
|
237
236
|
# @api private — Convert an {HttpConfig} (or a Hash with the same keys)
|
|
238
|
-
# into the generated wire model the jobs service expects.
|
|
237
|
+
# into the generated wire model the jobs service expects. Headers travel
|
|
238
|
+
# as the generated name→value headers object.
|
|
239
239
|
#
|
|
240
240
|
# @param src [HttpConfig, Hash] The HTTP configuration to serialize.
|
|
241
241
|
# @return [SmplkitGeneratedClient::Jobs::JobHttpConfiguration] The wire model.
|
|
@@ -244,14 +244,7 @@ module Smplkit
|
|
|
244
244
|
SmplkitGeneratedClient::Jobs::JobHttpConfiguration.new(
|
|
245
245
|
method: HttpMethod.coerce(h.method),
|
|
246
246
|
url: h.url,
|
|
247
|
-
headers: (h.headers ||
|
|
248
|
-
name, value = if hdr.is_a?(Hash)
|
|
249
|
-
[hdr[:name] || hdr["name"], hdr[:value] || hdr["value"]]
|
|
250
|
-
else
|
|
251
|
-
[hdr.name, hdr.value]
|
|
252
|
-
end
|
|
253
|
-
SmplkitGeneratedClient::Jobs::HttpHeader.new(name: name, value: value)
|
|
254
|
-
end,
|
|
247
|
+
headers: (h.headers || {}).transform_keys(&:to_s),
|
|
255
248
|
body: h.body,
|
|
256
249
|
success_status: h.success_status,
|
|
257
250
|
timeout: h.timeout,
|
|
@@ -273,11 +266,13 @@ module Smplkit
|
|
|
273
266
|
# +url+, +success_status+, and +timeout+ are required non-nil on the
|
|
274
267
|
# generated jobs config, so they pass straight through. +method+,
|
|
275
268
|
# +tls_verify+, +headers+, +body+, and +ca_cert+ are nullable and get
|
|
276
|
-
# wrapper-side defaults when the wire omits them.
|
|
269
|
+
# wrapper-side defaults when the wire omits them. Header keys arrive as
|
|
270
|
+
# symbols (the generated client symbolizes JSON) — stringify them so
|
|
271
|
+
# {#get_header} and round-trips behave by name.
|
|
277
272
|
new(
|
|
278
273
|
method: src.method || HttpMethod::POST,
|
|
279
274
|
url: src.url,
|
|
280
|
-
headers: (src.headers ||
|
|
275
|
+
headers: (src.headers || {}).transform_keys(&:to_s),
|
|
281
276
|
body: src.body,
|
|
282
277
|
success_status: src.success_status,
|
|
283
278
|
timeout: src.timeout,
|
|
@@ -292,70 +287,169 @@ module Smplkit
|
|
|
292
287
|
end
|
|
293
288
|
# rubocop:enable Lint/StructNewOverride
|
|
294
289
|
|
|
295
|
-
#
|
|
290
|
+
# The per-environment scalar override leaves (everything except +enabled+ and
|
|
291
|
+
# +headers+, which are addressed individually as +headers.<name>+). These map
|
|
292
|
+
# 1:1 onto {JobEnvironment} fields and onto the flat overlay's leaf paths, in
|
|
293
|
+
# payload order.
|
|
294
|
+
#
|
|
295
|
+
# @api private
|
|
296
|
+
JOB_ENV_SCALAR_LEAVES =
|
|
297
|
+
%i[schedule timezone retry_policy url method timeout body success_status tls_verify ca_cert].freeze
|
|
298
|
+
# The same leaves as wire-key strings, for parsing the flat overlay.
|
|
299
|
+
#
|
|
300
|
+
# @api private
|
|
301
|
+
JOB_ENV_SCALAR_LEAF_NAMES = JOB_ENV_SCALAR_LEAVES.map(&:to_s).freeze
|
|
302
|
+
|
|
303
|
+
# One environment's *sparse override* for a job (ADR-056).
|
|
304
|
+
#
|
|
305
|
+
# A job's {Job#environments} map holds one of these per environment. Only the
|
|
306
|
+
# leaves you set are sent on save; everything you leave unset is inherited
|
|
307
|
+
# from the job's base definition, and the server resolves base ⊕ overrides
|
|
308
|
+
# when the job fires. The base definition is disabled everywhere, so a job
|
|
309
|
+
# runs in an environment only when that environment's override sets
|
|
310
|
+
# +enabled: true+.
|
|
296
311
|
#
|
|
297
|
-
#
|
|
298
|
-
#
|
|
299
|
-
#
|
|
300
|
-
#
|
|
312
|
+
# Reach one through {Job#environment}, e.g.
|
|
313
|
+
# +job.environment("production").url = "https://prod.example.com/warm"+.
|
|
314
|
+
#
|
|
315
|
+
# *Reading a leaf returns this environment's override, or +nil+ when it does
|
|
316
|
+
# not override that leaf* — the SDK does not merge in the base value (jobs
|
|
317
|
+
# resolve server-side). To see a base value, read the job's base definition
|
|
318
|
+
# ({Job#configuration}, {Job#schedule}, …).
|
|
301
319
|
#
|
|
302
320
|
# @!attribute [rw] enabled
|
|
303
|
-
# @return [Boolean] Whether the job
|
|
304
|
-
# Defaults to +false+.
|
|
321
|
+
# @return [Boolean] Whether the job runs in this environment. Defaults to +false+.
|
|
305
322
|
# @!attribute [rw] schedule
|
|
306
|
-
# @return [String, nil]
|
|
307
|
-
#
|
|
308
|
-
# inherits the job's base {Job#schedule}. When present, it must be a
|
|
309
|
-
# 5-field UTC cron expression and is only meaningful on a recurring job —
|
|
310
|
-
# it cannot turn a one-off job recurring or vice-versa.
|
|
323
|
+
# @return [String, nil] Per-environment cron override (recurring jobs only).
|
|
324
|
+
# +nil+ inherits the base {Job#schedule}.
|
|
311
325
|
# @!attribute [rw] timezone
|
|
312
|
-
# @return [String, nil]
|
|
313
|
-
#
|
|
314
|
-
# +nil+ (the default) inherits the base {Job#timezone}, else UTC. When
|
|
315
|
-
# present, it must be a valid IANA zone key (e.g. +"America/New_York"+);
|
|
316
|
-
# it may be set on an environment that inherits the base schedule (it
|
|
317
|
-
# need not also override {#schedule}). Sent on writes only when present.
|
|
326
|
+
# @return [String, nil] Per-environment IANA timezone override (recurring
|
|
327
|
+
# jobs only). +nil+ inherits the base {Job#timezone}, else UTC.
|
|
318
328
|
# @!attribute [rw] retry_policy
|
|
319
|
-
# @return [String, nil]
|
|
320
|
-
#
|
|
321
|
-
#
|
|
322
|
-
# @!attribute [rw]
|
|
323
|
-
# @return [
|
|
324
|
-
#
|
|
325
|
-
#
|
|
326
|
-
#
|
|
327
|
-
#
|
|
329
|
+
# @return [String, nil] Per-environment retry-policy override — a policy id,
|
|
330
|
+
# a {RetryPolicy} (coerced to its id), or +"Default"+. +nil+ inherits the
|
|
331
|
+
# base {Job#retry_policy}.
|
|
332
|
+
# @!attribute [rw] url
|
|
333
|
+
# @return [String, nil] Per-environment URL override. +nil+ inherits the base.
|
|
334
|
+
# @!attribute [rw] method
|
|
335
|
+
# @return [String, nil] Per-environment HTTP-method override. +nil+ inherits the base.
|
|
336
|
+
# @!attribute [rw] timeout
|
|
337
|
+
# @return [Integer, nil] Per-environment timeout override. +nil+ inherits the base.
|
|
338
|
+
# @!attribute [rw] body
|
|
339
|
+
# @return [String, nil] Per-environment body override. +nil+ inherits the base.
|
|
340
|
+
# @!attribute [rw] success_status
|
|
341
|
+
# @return [String, nil] Per-environment success-status override. +nil+ inherits the base.
|
|
342
|
+
# @!attribute [rw] tls_verify
|
|
343
|
+
# @return [Boolean, nil] Per-environment TLS-verify override. +nil+ inherits the base.
|
|
344
|
+
# @!attribute [rw] ca_cert
|
|
345
|
+
# @return [String, nil] Per-environment CA-cert override. +nil+ inherits the base.
|
|
346
|
+
# @!attribute [rw] headers
|
|
347
|
+
# @return [Hash{String => String}] Per-environment header overrides, as a
|
|
348
|
+
# name→value object. Each entry overrides (or adds) that one header by name
|
|
349
|
+
# on top of the base headers, leaving the rest inherited. Use {#set_header}
|
|
350
|
+
# / {#get_header}.
|
|
328
351
|
# @!attribute [rw] next_run_at
|
|
329
|
-
# @return [String, nil] Read-only
|
|
330
|
-
# environment
|
|
331
|
-
#
|
|
352
|
+
# @return [String, nil] Read-only next scheduled fire time in this
|
|
353
|
+
# environment, or +nil+ when not enabled / once a one-off has fired. Never
|
|
354
|
+
# sent on save.
|
|
355
|
+
#
|
|
356
|
+
# rubocop:disable Lint/StructNewOverride -- ``:method`` matches the
|
|
357
|
+
# API attribute and shadowing Struct#method is the expected ergonomics.
|
|
332
358
|
JobEnvironment = Struct.new(
|
|
333
|
-
:enabled, :schedule, :timezone, :retry_policy, :
|
|
359
|
+
:enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
|
|
360
|
+
:body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
|
|
361
|
+
keyword_init: true
|
|
334
362
|
) do
|
|
335
363
|
def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
|
|
336
|
-
|
|
337
|
-
|
|
364
|
+
url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
|
|
365
|
+
tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
|
|
366
|
+
super(
|
|
367
|
+
enabled: enabled, schedule: schedule, timezone: timezone,
|
|
368
|
+
retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
|
|
369
|
+
url: url, method: method, timeout: timeout, body: body,
|
|
370
|
+
success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
|
|
371
|
+
headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
|
|
372
|
+
)
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# Coerce a retry-policy reference to its id on assignment, so both
|
|
376
|
+
# +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
|
|
377
|
+
def retry_policy=(value)
|
|
378
|
+
self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
|
|
338
379
|
end
|
|
339
380
|
|
|
340
|
-
#
|
|
381
|
+
# Override (or add) a single header by name in this environment.
|
|
341
382
|
#
|
|
342
|
-
# @param
|
|
343
|
-
#
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
383
|
+
# @param name [String] Header name.
|
|
384
|
+
# @param value [String] Header value.
|
|
385
|
+
def set_header(name, value)
|
|
386
|
+
self.headers ||= {}
|
|
387
|
+
headers[name.to_s] = value
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# This environment's override for header +name+, or +nil+ when it does not
|
|
391
|
+
# override that header.
|
|
392
|
+
#
|
|
393
|
+
# @param name [String] Header name.
|
|
394
|
+
# @return [String, nil]
|
|
395
|
+
def get_header(name)
|
|
396
|
+
(headers || {})[name.to_s]
|
|
397
|
+
end
|
|
347
398
|
|
|
348
|
-
|
|
399
|
+
# @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
|
|
400
|
+
# plus only the leaves this environment overrides, with each header as a
|
|
401
|
+
# +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
|
|
402
|
+
#
|
|
403
|
+
# @return [Hash{String => Object}]
|
|
404
|
+
def to_payload
|
|
405
|
+
payload = { "enabled" => enabled }
|
|
406
|
+
JOB_ENV_SCALAR_LEAVES.each do |leaf|
|
|
407
|
+
value = self[leaf]
|
|
408
|
+
payload[leaf.to_s] = value unless value.nil?
|
|
409
|
+
end
|
|
410
|
+
(headers || {}).each { |name, value| payload["headers.#{name}"] = value }
|
|
411
|
+
payload
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# @api private — Parse the flat leaf-path overlay the server returns
|
|
415
|
+
# (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
|
|
416
|
+
# dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
|
|
417
|
+
# leaf is a single top-level key. Unknown leaves are ignored for forward
|
|
418
|
+
# compatibility. Keys may be symbols or strings.
|
|
419
|
+
#
|
|
420
|
+
# @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
|
|
421
|
+
# override.
|
|
422
|
+
# @return [JobEnvironment]
|
|
423
|
+
def self.from_flat(raw)
|
|
424
|
+
return new if raw.nil?
|
|
425
|
+
|
|
426
|
+
headers = {}
|
|
427
|
+
scalars = {}
|
|
428
|
+
next_run_at = nil
|
|
429
|
+
raw.each do |key, value|
|
|
430
|
+
key = key.to_s
|
|
431
|
+
if key == "next_run_at"
|
|
432
|
+
next_run_at = value
|
|
433
|
+
next
|
|
434
|
+
end
|
|
435
|
+
group, _dot, name = key.partition(".")
|
|
436
|
+
if group == "headers" && !name.empty?
|
|
437
|
+
headers[name] = value
|
|
438
|
+
elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
|
|
439
|
+
scalars[key] = value
|
|
440
|
+
end
|
|
441
|
+
end
|
|
349
442
|
new(
|
|
350
|
-
enabled:
|
|
351
|
-
schedule:
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
next_run_at:
|
|
443
|
+
enabled: scalars["enabled"] ? true : false,
|
|
444
|
+
schedule: scalars["schedule"], timezone: scalars["timezone"],
|
|
445
|
+
retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
|
|
446
|
+
timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
|
|
447
|
+
tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
|
|
448
|
+
headers: headers, next_run_at: next_run_at
|
|
356
449
|
)
|
|
357
450
|
end
|
|
358
451
|
end
|
|
452
|
+
# rubocop:enable Lint/StructNewOverride
|
|
359
453
|
|
|
360
454
|
# A unit of work: an HTTP request, run on a schedule or triggered on demand.
|
|
361
455
|
#
|
|
@@ -368,11 +462,13 @@ module Smplkit
|
|
|
368
462
|
#
|
|
369
463
|
# A job's {#kind} follows from its {#schedule}: a recurring (cron) job, a
|
|
370
464
|
# manual job (no schedule, runs only when triggered), or a one-off (+now+ /
|
|
371
|
-
# datetime) job that runs a single time. Enablement
|
|
372
|
-
#
|
|
373
|
-
#
|
|
374
|
-
# environment
|
|
375
|
-
# {#
|
|
465
|
+
# datetime) job that runs a single time. Enablement and every other override
|
|
466
|
+
# is per environment: reach an environment's sparse override via
|
|
467
|
+
# {#environment} and set its +enabled+ / leaf fields
|
|
468
|
+
# (+job.environment("production").enabled = true+). Base {#enabled} is a
|
|
469
|
+
# read-only roll-up over {#environments} (+true+ when enabled in at least one
|
|
470
|
+
# environment). Base fields ({#schedule}, {#timezone}, {#retry_policy},
|
|
471
|
+
# {#configuration}) are set by direct assignment.
|
|
376
472
|
class Job
|
|
377
473
|
# @return [String] Caller-supplied unique identifier for the job (the
|
|
378
474
|
# resource +id+). Unique within the account and immutable; the service
|
|
@@ -385,20 +481,20 @@ module Smplkit
|
|
|
385
481
|
# @return [String, nil] Free-text description. +nil+ when unset.
|
|
386
482
|
attr_accessor :description
|
|
387
483
|
|
|
388
|
-
# @return [Boolean]
|
|
484
|
+
# @return [Boolean] Read-only roll-up: +true+ when the job is enabled in at
|
|
389
485
|
# least one environment. Computed from {#environments} rather than read
|
|
390
|
-
# from the wire — the API no
|
|
391
|
-
#
|
|
486
|
+
# from the wire — the API has no top-level +enabled+. Enable per
|
|
487
|
+
# environment via +job.environment(env).enabled = true+.
|
|
392
488
|
def enabled
|
|
393
489
|
(@environments || {}).each_value.any?(&:enabled)
|
|
394
490
|
end
|
|
395
491
|
|
|
396
|
-
# @return [Hash{String => JobEnvironment}] Per-environment overrides
|
|
397
|
-
# by environment key (e.g. +"production"+).
|
|
398
|
-
#
|
|
399
|
-
#
|
|
400
|
-
#
|
|
401
|
-
#
|
|
492
|
+
# @return [Hash{String => JobEnvironment}] Per-environment sparse overrides
|
|
493
|
+
# keyed by environment key (e.g. +"production"+). A job runs in an
|
|
494
|
+
# environment only when +environments[env].enabled+ is +true+. Each entry
|
|
495
|
+
# overrides only the leaves it sets; omitted leaves inherit the base
|
|
496
|
+
# definition. Reach one via {#environment}. Every referenced environment
|
|
497
|
+
# must exist and be managed for the account.
|
|
402
498
|
attr_accessor :environments
|
|
403
499
|
|
|
404
500
|
# @return [String, nil] Read-only. How the job runs, derived from its
|
|
@@ -415,8 +511,9 @@ module Smplkit
|
|
|
415
511
|
# evaluated in UTC for a recurring job; an ISO-8601 datetime for a
|
|
416
512
|
# one-off run at that instant; or the literal +"now"+ for a one-off run
|
|
417
513
|
# as soon as possible. A datetime or +"now"+ job disables itself after it
|
|
418
|
-
# fires. The schedule is environment-agnostic — set it
|
|
419
|
-
#
|
|
514
|
+
# fires. The schedule is environment-agnostic — set it by direct
|
|
515
|
+
# assignment; per-environment cron overrides live on
|
|
516
|
+
# +job.environment(env).schedule+.
|
|
420
517
|
attr_accessor :schedule
|
|
421
518
|
|
|
422
519
|
# @return [String, nil] The base IANA timezone the cron {#schedule} is
|
|
@@ -425,16 +522,22 @@ module Smplkit
|
|
|
425
522
|
# {JobEnvironment#timezone}. The cron fires on this zone's wall clock
|
|
426
523
|
# (DST-aware) while +next_run_at+ is still reported as a UTC instant.
|
|
427
524
|
# Only valid on a recurring (cron) job — leave +nil+ for a manual or
|
|
428
|
-
# one-off job.
|
|
429
|
-
# present.
|
|
525
|
+
# one-off job. Sent on writes only when present.
|
|
430
526
|
attr_accessor :timezone
|
|
431
527
|
|
|
432
528
|
# @return [String, nil] The base retry policy for failed runs — the id of a
|
|
433
529
|
# {RetryPolicy}, overridable per environment via
|
|
434
530
|
# {JobEnvironment#retry_policy}. +nil+ (the default, omitted on the wire)
|
|
435
|
-
# uses the built-in +"Default"+ policy, which never retries.
|
|
436
|
-
# {
|
|
437
|
-
|
|
531
|
+
# uses the built-in +"Default"+ policy, which never retries. Assigning
|
|
532
|
+
# accepts a {RetryPolicy} (its id is used) or a policy id string; sent on
|
|
533
|
+
# writes only when present.
|
|
534
|
+
attr_reader :retry_policy
|
|
535
|
+
|
|
536
|
+
# Set the base retry policy, coercing a {RetryPolicy} to its id so both
|
|
537
|
+
# +job.retry_policy = policy+ and +job.retry_policy = "retry-on-5xx"+ work.
|
|
538
|
+
def retry_policy=(value)
|
|
539
|
+
@retry_policy = value.is_a?(RetryPolicy) ? value.id : value
|
|
540
|
+
end
|
|
438
541
|
|
|
439
542
|
# @return [HttpConfig] The base HTTP request to perform when the job fires.
|
|
440
543
|
# Per-environment overrides live in {#environments}.
|
|
@@ -479,7 +582,7 @@ module Smplkit
|
|
|
479
582
|
@type = type
|
|
480
583
|
@schedule = schedule
|
|
481
584
|
@timezone = timezone
|
|
482
|
-
|
|
585
|
+
self.retry_policy = retry_policy
|
|
483
586
|
@configuration = configuration
|
|
484
587
|
@concurrency_policy = concurrency_policy
|
|
485
588
|
@birth_environment = birth_environment
|
|
@@ -517,35 +620,24 @@ module Smplkit
|
|
|
517
620
|
end
|
|
518
621
|
alias delete! delete
|
|
519
622
|
|
|
520
|
-
#
|
|
623
|
+
# The per-environment override for +environment+ — the single place to read
|
|
624
|
+
# or set what this job overrides there (ADR-056).
|
|
521
625
|
#
|
|
522
|
-
#
|
|
523
|
-
#
|
|
524
|
-
#
|
|
626
|
+
# Returns the {JobEnvironment} for +environment+, creating an empty one (and
|
|
627
|
+
# inserting it into {#environments}) on first access, so you can set
|
|
628
|
+
# overrides directly:
|
|
525
629
|
#
|
|
526
|
-
#
|
|
527
|
-
#
|
|
528
|
-
|
|
529
|
-
_environment_override(environment).enabled = enabled
|
|
530
|
-
end
|
|
531
|
-
|
|
532
|
-
# Whether the job is enabled.
|
|
630
|
+
# job.environment("production").enabled = true
|
|
631
|
+
# job.environment("production").url = "https://prod.example.com/warm"
|
|
632
|
+
# job.environment("production").set_header("Authorization", "Bearer prod")
|
|
533
633
|
#
|
|
534
|
-
#
|
|
535
|
-
#
|
|
536
|
-
# +environment+, returns whether the job is enabled in that specific
|
|
537
|
-
# environment.
|
|
634
|
+
# Only the leaves you set are sent on save; everything else inherits the
|
|
635
|
+
# base definition (the server resolves base ⊕ overrides when the job fires).
|
|
538
636
|
#
|
|
539
|
-
# @param environment [String
|
|
540
|
-
#
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
return enabled if environment.nil?
|
|
544
|
-
|
|
545
|
-
override = @environments[environment]
|
|
546
|
-
return false if override.nil?
|
|
547
|
-
|
|
548
|
-
override.enabled
|
|
637
|
+
# @param environment [String] The environment key.
|
|
638
|
+
# @return [JobEnvironment]
|
|
639
|
+
def environment(environment)
|
|
640
|
+
@environments[environment] ||= JobEnvironment.new
|
|
549
641
|
end
|
|
550
642
|
|
|
551
643
|
# Whether this is a recurring (cron-scheduled) job.
|
|
@@ -569,127 +661,6 @@ module Smplkit
|
|
|
569
661
|
@kind == JobKind::ONE_OFF
|
|
570
662
|
end
|
|
571
663
|
|
|
572
|
-
# Set the job's configuration — base (+environment+ omitted) or
|
|
573
|
-
# per-environment.
|
|
574
|
-
#
|
|
575
|
-
# With +environment+ given, sets the per-environment override's
|
|
576
|
-
# configuration on {#environments}, creating the override entry if it
|
|
577
|
-
# doesn't exist yet (preserving any already-set +enabled+ on it). Call
|
|
578
|
-
# {#save} to persist.
|
|
579
|
-
#
|
|
580
|
-
# @param configuration [HttpConfig] The HTTP request configuration.
|
|
581
|
-
# @param environment [String, nil] An environment key for a per-environment
|
|
582
|
-
# override, or +nil+ to set the base configuration.
|
|
583
|
-
def set_configuration(configuration, environment: nil)
|
|
584
|
-
if environment.nil?
|
|
585
|
-
@configuration = configuration
|
|
586
|
-
else
|
|
587
|
-
_environment_override(environment).configuration = configuration
|
|
588
|
-
end
|
|
589
|
-
end
|
|
590
|
-
|
|
591
|
-
# The job's effective configuration.
|
|
592
|
-
#
|
|
593
|
-
# With +environment+ omitted (the default), returns the base
|
|
594
|
-
# configuration. With an +environment+, returns that environment's
|
|
595
|
-
# configuration override when it has one, else the base configuration —
|
|
596
|
-
# the request the job actually sends when it fires in that environment.
|
|
597
|
-
#
|
|
598
|
-
# @param environment [String, nil] An environment key, or +nil+ for the
|
|
599
|
-
# base configuration.
|
|
600
|
-
# @return [HttpConfig]
|
|
601
|
-
def get_configuration(environment: nil)
|
|
602
|
-
unless environment.nil?
|
|
603
|
-
override = @environments[environment]
|
|
604
|
-
return override.configuration if override && !override.configuration.nil?
|
|
605
|
-
end
|
|
606
|
-
@configuration
|
|
607
|
-
end
|
|
608
|
-
|
|
609
|
-
# Set the job's schedule — base (+environment+ omitted) or per-environment.
|
|
610
|
-
#
|
|
611
|
-
# With +environment+ omitted (the default), sets the base {#schedule} —
|
|
612
|
-
# an ISO-8601 datetime, a 5-field UTC cron expression, or the literal
|
|
613
|
-
# +"now"+ — which every environment inherits unless it overrides it.
|
|
614
|
-
#
|
|
615
|
-
# With +environment+ given, sets that environment's per-environment cron
|
|
616
|
-
# +schedule+ override on {#environments}, creating the override entry if it
|
|
617
|
-
# doesn't exist yet (preserving any already-set +enabled+ / +configuration+
|
|
618
|
-
# on it). A per-environment override is a cron expression only and varies
|
|
619
|
-
# the cadence within that environment; it cannot turn a one-off job
|
|
620
|
-
# recurring or vice-versa. Call {#save} to persist.
|
|
621
|
-
#
|
|
622
|
-
# Because the timezone is an integral part of a cron cadence, a +timezone+
|
|
623
|
-
# may be supplied alongside the schedule; when given it sets the same
|
|
624
|
-
# scope's timezone too (equivalent to a follow-up {#set_timezone}). Omit it
|
|
625
|
-
# to leave the timezone untouched. For a timezone-only change, use
|
|
626
|
-
# {#set_timezone}.
|
|
627
|
-
#
|
|
628
|
-
# @param schedule [String] An ISO-8601 datetime, a 5-field UTC cron
|
|
629
|
-
# expression, or the literal +"now"+ (base); a 5-field UTC cron
|
|
630
|
-
# expression (per-environment).
|
|
631
|
-
# @param timezone [String, nil] An optional IANA timezone to set on the
|
|
632
|
-
# same scope (recurring jobs only). +nil+ (the default) leaves the
|
|
633
|
-
# timezone untouched.
|
|
634
|
-
# @param environment [String, nil] An environment key for a per-environment
|
|
635
|
-
# override, or +nil+ to set the base schedule.
|
|
636
|
-
def set_schedule(schedule, timezone: nil, environment: nil)
|
|
637
|
-
if environment.nil?
|
|
638
|
-
@schedule = schedule
|
|
639
|
-
else
|
|
640
|
-
_environment_override(environment).schedule = schedule
|
|
641
|
-
end
|
|
642
|
-
set_timezone(timezone, environment: environment) unless timezone.nil?
|
|
643
|
-
end
|
|
644
|
-
|
|
645
|
-
# Set the IANA timezone the cron schedule is evaluated in — base
|
|
646
|
-
# (+environment+ omitted) or per-environment.
|
|
647
|
-
#
|
|
648
|
-
# With +environment+ omitted (the default), sets the base {#timezone}
|
|
649
|
-
# every environment inherits unless it overrides it. With an +environment+
|
|
650
|
-
# given, sets that environment's per-environment +timezone+ override on
|
|
651
|
-
# {#environments}, creating the override entry if it doesn't exist yet
|
|
652
|
-
# (preserving any already-set +enabled+ / +schedule+ / +configuration+ on
|
|
653
|
-
# it). A timezone is only valid on a recurring (cron) job; +nil+ means UTC
|
|
654
|
-
# (base) or "inherit the base" (per-environment). Call {#save} to persist.
|
|
655
|
-
#
|
|
656
|
-
# @param timezone [String, nil] A valid IANA timezone key (e.g.
|
|
657
|
-
# +"America/New_York"+), or +nil+ for UTC / inherit.
|
|
658
|
-
# @param environment [String, nil] An environment key for a per-environment
|
|
659
|
-
# override, or +nil+ to set the base timezone.
|
|
660
|
-
def set_timezone(timezone, environment: nil)
|
|
661
|
-
if environment.nil?
|
|
662
|
-
@timezone = timezone
|
|
663
|
-
else
|
|
664
|
-
_environment_override(environment).timezone = timezone
|
|
665
|
-
end
|
|
666
|
-
end
|
|
667
|
-
|
|
668
|
-
# Set the retry policy for failed runs — base (+environment+ omitted) or
|
|
669
|
-
# per-environment.
|
|
670
|
-
#
|
|
671
|
-
# With +environment+ omitted (the default), sets the base {#retry_policy}
|
|
672
|
-
# every environment inherits unless it overrides it. With an +environment+
|
|
673
|
-
# given, sets that environment's per-environment override on
|
|
674
|
-
# {#environments}, creating the override entry if it doesn't exist yet
|
|
675
|
-
# (preserving any already-set +enabled+ / +schedule+ / +timezone+ /
|
|
676
|
-
# +configuration+ on it). Call {#save} to persist.
|
|
677
|
-
#
|
|
678
|
-
# Accepts either a {RetryPolicy} instance (its id is used) or a policy id
|
|
679
|
-
# string — pass +"Default"+ for the built-in never-retry policy.
|
|
680
|
-
#
|
|
681
|
-
# @param retry_policy [RetryPolicy, String] A {RetryPolicy} or a policy id.
|
|
682
|
-
# @param environment [String, nil] An environment key for a per-environment
|
|
683
|
-
# override, or +nil+ to set the base retry policy.
|
|
684
|
-
def set_retry_policy(retry_policy, environment: nil)
|
|
685
|
-
policy_id = retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy
|
|
686
|
-
if environment.nil?
|
|
687
|
-
@retry_policy = policy_id
|
|
688
|
-
else
|
|
689
|
-
_environment_override(environment).retry_policy = policy_id
|
|
690
|
-
end
|
|
691
|
-
end
|
|
692
|
-
|
|
693
664
|
# Trigger one immediate, manual run of this job (a +MANUAL+ run).
|
|
694
665
|
#
|
|
695
666
|
# @param environment [String, nil] Environment the run executes in.
|
|
@@ -731,17 +702,6 @@ module Smplkit
|
|
|
731
702
|
)
|
|
732
703
|
end
|
|
733
704
|
|
|
734
|
-
# Return the override for +environment+, creating an empty one if absent.
|
|
735
|
-
#
|
|
736
|
-
# The per-environment mutators reach through here so an existing override's
|
|
737
|
-
# other field is preserved when only one of +enabled+ / +configuration+ is
|
|
738
|
-
# being set.
|
|
739
|
-
#
|
|
740
|
-
# @api private
|
|
741
|
-
def _environment_override(environment)
|
|
742
|
-
@environments[environment] ||= JobEnvironment.new
|
|
743
|
-
end
|
|
744
|
-
|
|
745
705
|
# @api private
|
|
746
706
|
def _apply(other)
|
|
747
707
|
@id = other.id
|
|
@@ -770,7 +730,7 @@ module Smplkit
|
|
|
770
730
|
def self.from_resource(resource, client: nil)
|
|
771
731
|
a = resource.attributes
|
|
772
732
|
environments = (a.environments || {}).each_with_object({}) do |(env_key, env_raw), out|
|
|
773
|
-
out[env_key.to_s] = JobEnvironment.
|
|
733
|
+
out[env_key.to_s] = JobEnvironment.from_flat(env_raw)
|
|
774
734
|
end
|
|
775
735
|
new(
|
|
776
736
|
client,
|
|
@@ -958,9 +918,9 @@ module Smplkit
|
|
|
958
918
|
#
|
|
959
919
|
# Active-record style: instantiate via +client.jobs.retry_policies.new(...)+,
|
|
960
920
|
# mutate fields directly, and call {#save} to persist or {#delete} to remove.
|
|
961
|
-
# Reference a saved policy from a job's {Job#retry_policy} (
|
|
962
|
-
# {
|
|
963
|
-
# are account-global — never environment-scoped.
|
|
921
|
+
# Reference a saved policy from a job's {Job#retry_policy} (base) or
|
|
922
|
+
# {JobEnvironment#retry_policy} (per environment, via {Job#environment}).
|
|
923
|
+
# Retry policies are account-global — never environment-scoped.
|
|
964
924
|
#
|
|
965
925
|
# A policy decides whether and how a failed run is retried. A job that
|
|
966
926
|
# references nothing uses the built-in +"Default"+ policy, which never
|