smplkit 3.0.129 → 3.0.131
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/audit/client.rb +23 -10
- data/lib/smplkit/audit/events.rb +45 -17
- data/lib/smplkit/config/client.rb +56 -18
- data/lib/smplkit/config_resolution.rb +17 -5
- data/lib/smplkit/flags/client.rb +69 -13
- data/lib/smplkit/jobs/client.rb +25 -12
- data/lib/smplkit/logging/client.rb +82 -20
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 80d71501763192031a0fb659bbe16d4aa0b021c1995dbcef803cb3cc11298355
|
|
4
|
+
data.tar.gz: e8f81502fe54983ca18c2d83bda4089e36f21b6bd5e6b58aea74ebd4b6a5edec
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cd8267d396fcb466ff2f4b2ac8addfbbd667b33bc122a599628d0a49134be4c93810fae6b779ae12f633d52062fc032ad3da0948d36c5fd472857ac0d604dfae
|
|
7
|
+
data.tar.gz: 36368e8ea040abb9905ff4d84650cd147cadf3bcef6038165970c1804df311d42e3e84fdd51e522042ee7c5a98324961a3733efc86f207243079db4a0872997d
|
data/lib/smplkit/audit/client.rb
CHANGED
|
@@ -23,7 +23,8 @@ module Smplkit
|
|
|
23
23
|
# @param environment [String, nil] Deployment environment to scope recording
|
|
24
24
|
# and reads to. Sent on the event request body when recording and as the
|
|
25
25
|
# default +filter[environment]+ on the read surfaces (events list and the
|
|
26
|
-
# resource_type / event_type / category discovery lists).
|
|
26
|
+
# resource_type / event_type / category discovery lists). When omitted,
|
|
27
|
+
# resolved from +SMPLKIT_ENVIRONMENT+ or +~/.smplkit+. Optional —
|
|
27
28
|
# forwarder CRUD is environment-agnostic, and reads accept an explicit
|
|
28
29
|
# +environments: [...]+ filter that overrides this default. With no
|
|
29
30
|
# configured environment, recording falls back to the server-side default
|
|
@@ -37,6 +38,13 @@ module Smplkit
|
|
|
37
38
|
# @param extra_headers [Hash{String => String}, nil] Extra headers attached
|
|
38
39
|
# to every request. SDK-owned headers (authorization, content-type,
|
|
39
40
|
# user-agent) cannot be overridden.
|
|
41
|
+
# @param buffered [Boolean] Buffer event recording (default +true+):
|
|
42
|
+
# +events.record+ enqueues onto an in-memory buffer that a background
|
|
43
|
+
# worker drains with retry, and returns immediately. Set +false+ for the
|
|
44
|
+
# stateless write path: +events.record+ performs one blocking POST per
|
|
45
|
+
# call and raises on failure, +events.flush+ is a no-op, and no worker
|
|
46
|
+
# thread or background state is created — the right shape for serverless
|
|
47
|
+
# and edge runtimes, where an instance may be constructed per request.
|
|
40
48
|
class AuditClient
|
|
41
49
|
attr_reader :events, :resource_types, :event_types, :categories, :forwarders
|
|
42
50
|
|
|
@@ -44,19 +52,21 @@ module Smplkit
|
|
|
44
52
|
|
|
45
53
|
def initialize(api_key: nil, base_url: nil, environment: nil, profile: nil,
|
|
46
54
|
base_domain: nil, scheme: nil, debug: nil, timeout: 10.0,
|
|
47
|
-
extra_headers: nil)
|
|
48
|
-
# +base_url+/+
|
|
49
|
-
# path the top-level client takes after it has
|
|
50
|
-
# otherwise the config resolver fills in
|
|
51
|
-
# (+~/.smplkit+ / env vars / defaults) and the
|
|
52
|
-
# from +base_domain+/+scheme+ via
|
|
53
|
-
|
|
55
|
+
extra_headers: nil, buffered: true)
|
|
56
|
+
# +api_key+/+base_url+/+environment+ are used directly when all three
|
|
57
|
+
# are supplied (the path the top-level client takes after it has
|
|
58
|
+
# already resolved them); otherwise the config resolver fills in
|
|
59
|
+
# whatever is missing (+~/.smplkit+ / env vars / defaults) and the
|
|
60
|
+
# audit base URL is derived from +base_domain+/+scheme+ via
|
|
61
|
+
# +service_url+.
|
|
62
|
+
if api_key.nil? || base_url.nil? || environment.nil?
|
|
54
63
|
resolved = ConfigResolution.resolve_client_config(
|
|
55
64
|
profile: profile, api_key: api_key, base_domain: base_domain,
|
|
56
|
-
scheme: scheme, debug: debug
|
|
65
|
+
scheme: scheme, environment: environment, debug: debug
|
|
57
66
|
)
|
|
58
67
|
api_key = resolved.api_key if api_key.nil?
|
|
59
68
|
base_url = ConfigResolution.service_url(resolved.scheme, "audit", resolved.base_domain) if base_url.nil?
|
|
69
|
+
environment = resolved.environment if environment.nil?
|
|
60
70
|
debug = resolved.debug if debug.nil?
|
|
61
71
|
end
|
|
62
72
|
cfg = SmplkitGeneratedClient::Audit::Configuration.new
|
|
@@ -78,7 +88,10 @@ module Smplkit
|
|
|
78
88
|
extra_headers&.each do |k, v|
|
|
79
89
|
api_client.default_headers[k] = v unless SDK_OWNED_HEADERS.include?(k.downcase)
|
|
80
90
|
end
|
|
81
|
-
@events = Events.new(
|
|
91
|
+
@events = Events.new(
|
|
92
|
+
SmplkitGeneratedClient::Audit::EventsApi.new(api_client),
|
|
93
|
+
environment: environment, buffered: buffered
|
|
94
|
+
)
|
|
82
95
|
@resource_types = ResourceTypes.new(
|
|
83
96
|
SmplkitGeneratedClient::Audit::ResourceTypesApi.new(api_client), environment: environment
|
|
84
97
|
)
|
data/lib/smplkit/audit/events.rb
CHANGED
|
@@ -7,22 +7,33 @@ module Smplkit
|
|
|
7
7
|
# +#record+ is fire-and-forget by default — the call enqueues the event
|
|
8
8
|
# onto an in-memory bounded buffer and returns immediately. Pass
|
|
9
9
|
# +flush: true+ to block until the event is durable before continuing.
|
|
10
|
+
# In stateless mode (+buffered: false+ on the client) there is no buffer:
|
|
11
|
+
# every +#record+ performs one blocking POST and raises on failure.
|
|
10
12
|
# +#list+ and +#get+ are synchronous reads.
|
|
11
13
|
class Events
|
|
12
|
-
def initialize(api, environment: nil)
|
|
14
|
+
def initialize(api, environment: nil, buffered: true)
|
|
13
15
|
@api = api
|
|
14
16
|
@environment = environment
|
|
15
|
-
|
|
17
|
+
# Stateless mode installs no buffer at all — no worker thread, no
|
|
18
|
+
# background state — so a client may be constructed per request in
|
|
19
|
+
# serverless or edge runtimes.
|
|
20
|
+
@buffer = buffered ? EventBuffer.new(api) : nil
|
|
16
21
|
end
|
|
17
22
|
|
|
18
|
-
#
|
|
23
|
+
# Record an audit event.
|
|
19
24
|
#
|
|
20
|
-
#
|
|
21
|
-
# buffer's worker thread performs the
|
|
22
|
-
# transient failures. When +flush: true+,
|
|
23
|
-
# buffer has drained or +flush_timeout+
|
|
24
|
-
# needs the event durable before
|
|
25
|
-
# assertions, or any flow about to
|
|
25
|
+
# In the default buffered mode this enqueues for asynchronous delivery
|
|
26
|
+
# and returns immediately — the buffer's worker thread performs the
|
|
27
|
+
# actual POST with retry on transient failures. When +flush: true+,
|
|
28
|
+
# the call blocks until the buffer has drained or +flush_timeout+
|
|
29
|
+
# elapses; use it when the caller needs the event durable before
|
|
30
|
+
# continuing (CLI tools, in-test assertions, or any flow about to
|
|
31
|
+
# terminate the process).
|
|
32
|
+
#
|
|
33
|
+
# In stateless mode (+buffered: false+ on the client) every call
|
|
34
|
+
# performs one blocking POST and raises one of the SDK's typed errors
|
|
35
|
+
# on failure; +flush+/+flush_timeout+ are meaningless there and
|
|
36
|
+
# ignored.
|
|
26
37
|
#
|
|
27
38
|
# Actor attribution (+actor_type+, +actor_id+, +actor_label+) is
|
|
28
39
|
# customer-supplied and free-form. The audit service stores
|
|
@@ -69,10 +80,11 @@ module Smplkit
|
|
|
69
80
|
# enabled forwarder so the skip is visible in the forwarder delivery log.
|
|
70
81
|
# @param flush [Boolean] When +true+, block until the buffer has drained
|
|
71
82
|
# (or +flush_timeout+ elapses) before returning. Defaults to +false+
|
|
72
|
-
# (fire-and-forget).
|
|
83
|
+
# (fire-and-forget). Ignored in stateless mode (+buffered: false+),
|
|
84
|
+
# where every record is already durable on return.
|
|
73
85
|
# @param flush_timeout [Float, nil] Upper bound on the blocking flush, in
|
|
74
|
-
# seconds. Ignored when +flush+ is +false
|
|
75
|
-
# Defaults to +5.0+.
|
|
86
|
+
# seconds. Ignored when +flush+ is +false+ and in stateless mode.
|
|
87
|
+
# +nil+ blocks indefinitely. Defaults to +5.0+.
|
|
76
88
|
# @return [void]
|
|
77
89
|
def record(event_type:, resource_type:, resource_id:,
|
|
78
90
|
occurred_at: nil, actor_type: nil, actor_id: nil,
|
|
@@ -124,6 +136,14 @@ module Smplkit
|
|
|
124
136
|
attributes: attrs
|
|
125
137
|
)
|
|
126
138
|
body = SmplkitGeneratedClient::Audit::EventRequest.new(data: resource)
|
|
139
|
+
if @buffer.nil?
|
|
140
|
+
# Stateless path: one blocking POST per call — the same wire call
|
|
141
|
+
# the buffer's worker performs, but failures surface to the caller
|
|
142
|
+
# as the SDK's typed errors instead of being retried in background.
|
|
143
|
+
opts = idempotency_key ? { idempotency_key: idempotency_key } : {}
|
|
144
|
+
Smplkit::Audit.call_api { @api.record_event(body, opts) }
|
|
145
|
+
return nil
|
|
146
|
+
end
|
|
127
147
|
@buffer.enqueue(body, idempotency_key)
|
|
128
148
|
@buffer.flush(timeout: flush_timeout) if flush
|
|
129
149
|
nil
|
|
@@ -171,6 +191,9 @@ module Smplkit
|
|
|
171
191
|
# equals this value. Omit to match any.
|
|
172
192
|
# @param actor_id [String, nil] Return only events whose +actor_id+
|
|
173
193
|
# matches this value as a literal string. Omit to match any.
|
|
194
|
+
# @param category [String, nil] Filter to this exact category — the
|
|
195
|
+
# indexed correlation label callers stamp on related events. Omit to
|
|
196
|
+
# match any.
|
|
174
197
|
# @param occurred_at_range [String, nil] Restrict to events whose
|
|
175
198
|
# +occurred_at+ falls in this range. Omit to leave the time window open.
|
|
176
199
|
# @param search [String, nil] Optional free-text filter — returns only
|
|
@@ -189,7 +212,7 @@ module Smplkit
|
|
|
189
212
|
# @return [Smplkit::Audit::ListEventsPage] A page of the matching events;
|
|
190
213
|
# its +#next_cursor+ is set when more pages are available.
|
|
191
214
|
def list(event_type: nil, resource_type: nil, resource_id: nil,
|
|
192
|
-
actor_type: nil, actor_id: nil, occurred_at_range: nil,
|
|
215
|
+
actor_type: nil, actor_id: nil, category: nil, occurred_at_range: nil,
|
|
193
216
|
search: nil, environments: nil, page_size: nil, page_after: nil)
|
|
194
217
|
# Generated client opts use snake_case keys that internally map
|
|
195
218
|
# to the JSON:API ``filter[*]`` / ``page[*]`` query-string format
|
|
@@ -202,6 +225,7 @@ module Smplkit
|
|
|
202
225
|
opts[:filter_resource_id] = resource_id if resource_id
|
|
203
226
|
opts[:filter_actor_type] = actor_type if actor_type
|
|
204
227
|
opts[:filter_actor_id] = actor_id if actor_id
|
|
228
|
+
opts[:filter_category] = category if category
|
|
205
229
|
opts[:filter_occurred_at] = occurred_at_range if occurred_at_range
|
|
206
230
|
opts[:filter_search] = search if search
|
|
207
231
|
resolved_environment = Smplkit::Audit.resolve_environment_filter(environments, @environment)
|
|
@@ -217,18 +241,22 @@ module Smplkit
|
|
|
217
241
|
# Block until the in-memory buffer is drained or the timeout elapses.
|
|
218
242
|
#
|
|
219
243
|
# Useful for draining buffered events at process shutdown or after a batch
|
|
220
|
-
# of fire-and-forget records.
|
|
244
|
+
# of fire-and-forget records. A no-op in stateless mode
|
|
245
|
+
# (+buffered: false+), where every record is already durable on return.
|
|
221
246
|
#
|
|
222
247
|
# @param timeout [Float, nil] Upper bound on the blocking flush, in
|
|
223
248
|
# seconds. +nil+ blocks indefinitely. Defaults to +5.0+.
|
|
224
249
|
# @return [void]
|
|
225
250
|
def flush(timeout: 5.0)
|
|
226
|
-
@buffer
|
|
251
|
+
@buffer&.flush(timeout: timeout)
|
|
252
|
+
nil
|
|
227
253
|
end
|
|
228
254
|
|
|
229
|
-
# @api private — drains best-effort and stops the worker thread.
|
|
255
|
+
# @api private — drains best-effort and stops the worker thread. A no-op
|
|
256
|
+
# in stateless mode (+buffered: false+), which has no worker to stop.
|
|
230
257
|
def _close
|
|
231
|
-
@buffer
|
|
258
|
+
@buffer&.close
|
|
259
|
+
nil
|
|
232
260
|
end
|
|
233
261
|
end
|
|
234
262
|
|
|
@@ -337,8 +337,9 @@ module Smplkit
|
|
|
337
337
|
# +base_url+/+api_key+ are used directly when supplied (the path a top-level
|
|
338
338
|
# client takes after it has already resolved them); otherwise the config
|
|
339
339
|
# resolver fills in whatever is missing (+~/.smplkit+ / env vars /
|
|
340
|
-
# defaults).
|
|
341
|
-
#
|
|
340
|
+
# defaults). +environment+/+service+ resolve the same way (constructor
|
|
341
|
+
# argument wins). The app base URL is returned alongside so a standalone
|
|
342
|
+
# client can open its own WebSocket against the event gateway.
|
|
342
343
|
#
|
|
343
344
|
# @api private
|
|
344
345
|
# @param api_key [String, nil] API key, or +nil+ to resolve it.
|
|
@@ -347,14 +348,20 @@ module Smplkit
|
|
|
347
348
|
# @param profile [String, nil] Named +~/.smplkit+ profile section.
|
|
348
349
|
# @param base_domain [String, nil] Base domain for API requests.
|
|
349
350
|
# @param scheme [String, nil] URL scheme.
|
|
351
|
+
# @param environment [String, nil] Deployment environment, or +nil+ to
|
|
352
|
+
# resolve it.
|
|
353
|
+
# @param service [String, nil] Service name, or +nil+ to resolve it.
|
|
350
354
|
# @param debug [Boolean, nil] Enable SDK debug logging.
|
|
351
355
|
# @param extra_headers [Hash{String => String}, nil] Headers attached to
|
|
352
356
|
# every request.
|
|
353
|
-
# @return [Array(Object, String, String)] The transport,
|
|
354
|
-
#
|
|
355
|
-
|
|
357
|
+
# @return [Array(Object, String, String, String, String)] The transport,
|
|
358
|
+
# the app base URL, the resolved API key, the resolved environment, and
|
|
359
|
+
# the resolved service (the last two +nil+ when configured nowhere).
|
|
360
|
+
def self.config_transport(api_key:, base_url:, profile:, base_domain:, scheme:,
|
|
361
|
+
environment:, service:, debug:, extra_headers:)
|
|
356
362
|
cfg = ConfigResolution.resolve_client_config(
|
|
357
|
-
profile: profile, api_key: api_key, base_domain: base_domain, scheme: scheme,
|
|
363
|
+
profile: profile, api_key: api_key, base_domain: base_domain, scheme: scheme,
|
|
364
|
+
environment: environment, service: service, debug: debug
|
|
358
365
|
)
|
|
359
366
|
resolved_key = api_key.nil? ? cfg.api_key : api_key
|
|
360
367
|
merged = {}
|
|
@@ -366,7 +373,7 @@ module Smplkit
|
|
|
366
373
|
)
|
|
367
374
|
app_url = ConfigResolution.service_url(cfg.scheme, "app", cfg.base_domain)
|
|
368
375
|
transport = Transport.build_api_client(SmplkitGeneratedClient::Config, "config", tcfg, base_url: base_url)
|
|
369
|
-
[transport, app_url, resolved_key]
|
|
376
|
+
[transport, app_url, resolved_key, cfg.environment, cfg.service]
|
|
370
377
|
end
|
|
371
378
|
|
|
372
379
|
# The Smpl Config client (sync).
|
|
@@ -396,7 +403,11 @@ module Smplkit
|
|
|
396
403
|
# @param api_key [String, nil] API key. When omitted, resolved from
|
|
397
404
|
# +SMPLKIT_API_KEY+ or +~/.smplkit+.
|
|
398
405
|
# @param environment [String, nil] Deployment environment used to resolve
|
|
399
|
-
# runtime config values and to scope discovery declarations.
|
|
406
|
+
# runtime config values and to scope discovery declarations. When
|
|
407
|
+
# omitted, resolved from +SMPLKIT_ENVIRONMENT+ or +~/.smplkit+.
|
|
408
|
+
# @param service [String, nil] Service name attached to discovery
|
|
409
|
+
# declarations. When omitted, resolved from +SMPLKIT_SERVICE+ or
|
|
410
|
+
# +~/.smplkit+. Optional.
|
|
400
411
|
# @param base_url [String, nil] Full config-service base URL. Usually
|
|
401
412
|
# resolved from +base_domain+/+scheme+; supplied directly by the
|
|
402
413
|
# top-level clients which have already computed it.
|
|
@@ -407,30 +418,49 @@ module Smplkit
|
|
|
407
418
|
# @param debug [Boolean, nil] Enable SDK debug logging.
|
|
408
419
|
# @param extra_headers [Hash{String => String}, nil] Extra headers
|
|
409
420
|
# attached to every request.
|
|
421
|
+
# @param streaming [Boolean] Live updates over WebSocket (default
|
|
422
|
+
# +true+): the first live call opens a shared socket and config changes
|
|
423
|
+
# stream in. Set +false+ for the stateless read-through surface: the
|
|
424
|
+
# first live call still fetches and resolves every config once
|
|
425
|
+
# (blocking), reads stay local, +refresh+ re-fetches on demand, and NO
|
|
426
|
+
# socket or background thread is ever created — the right shape for
|
|
427
|
+
# serverless and edge runtimes. +on_change+ listeners fire only from
|
|
428
|
+
# explicit +refresh+ calls in this mode (there is no stream to drive
|
|
429
|
+
# them).
|
|
410
430
|
# @param parent [Smplkit::Client, nil] Internal — the owning client. Not
|
|
411
431
|
# for direct use.
|
|
412
432
|
# @param transport [Object, nil] Internal — a pre-built config transport
|
|
413
433
|
# supplied by a top-level client so the config surface shares one
|
|
414
434
|
# connection pool. Not for direct use.
|
|
415
435
|
# @param metrics [Object, nil] Internal — the parent's metrics reporter.
|
|
416
|
-
def initialize(api_key = nil, environment: nil, base_url: nil, profile: nil,
|
|
436
|
+
def initialize(api_key = nil, environment: nil, service: nil, base_url: nil, profile: nil,
|
|
417
437
|
base_domain: nil, scheme: nil, debug: nil, extra_headers: nil,
|
|
418
|
-
parent: nil, transport: nil, metrics: nil)
|
|
438
|
+
streaming: true, parent: nil, transport: nil, metrics: nil)
|
|
419
439
|
@parent = parent
|
|
420
440
|
@metrics = metrics
|
|
421
|
-
@
|
|
422
|
-
@service = parent&._service
|
|
441
|
+
@streaming = streaming ? true : false
|
|
423
442
|
@standalone_api_key = nil
|
|
424
443
|
if transport.nil?
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
444
|
+
# Standalone: resolve like Smplkit::Client — defaults → ~/.smplkit →
|
|
445
|
+
# SMPLKIT_* env vars → constructor args — environment and service
|
|
446
|
+
# included.
|
|
447
|
+
@http, @app_base_url, @standalone_api_key, resolved_env, resolved_service =
|
|
448
|
+
Smplkit::Config.config_transport(
|
|
449
|
+
api_key: api_key, base_url: base_url, profile: profile,
|
|
450
|
+
base_domain: base_domain, scheme: scheme, environment: environment,
|
|
451
|
+
service: service, debug: debug, extra_headers: extra_headers
|
|
452
|
+
)
|
|
453
|
+
@environment = parent.nil? ? resolved_env : parent._environment
|
|
454
|
+
@service = parent.nil? ? resolved_service : parent._service
|
|
429
455
|
@owns_transport = true
|
|
430
456
|
else
|
|
431
457
|
@http = transport
|
|
432
458
|
@app_base_url = nil
|
|
433
459
|
@owns_transport = false
|
|
460
|
+
# Wired: the parent has already resolved environment/service once —
|
|
461
|
+
# its values win over both the raw kwargs and re-resolution.
|
|
462
|
+
@environment = parent.nil? ? environment : parent._environment
|
|
463
|
+
@service = parent.nil? ? service : parent._service
|
|
434
464
|
end
|
|
435
465
|
@api = SmplkitGeneratedClient::Config::ConfigsApi.new(@http)
|
|
436
466
|
|
|
@@ -839,7 +869,8 @@ module Smplkit
|
|
|
839
869
|
# Flushes any buffered discovery declarations, fetches and resolves every
|
|
840
870
|
# config for the configured environment into the local cache, opens the
|
|
841
871
|
# shared WebSocket, and subscribes to +config_changed+ / +config_deleted+
|
|
842
|
-
# / +configs_changed+ events.
|
|
872
|
+
# / +configs_changed+ events. In stateless mode (+streaming: false+) no
|
|
873
|
+
# socket is ever created; +refresh+ re-fetches on demand.
|
|
843
874
|
#
|
|
844
875
|
# Idempotent and internal — every live method calls it on first use, so
|
|
845
876
|
# the live surface auto-connects with no explicit step.
|
|
@@ -859,6 +890,7 @@ module Smplkit
|
|
|
859
890
|
# old_cache, so any registered listeners see "initial" events).
|
|
860
891
|
do_refresh("initial")
|
|
861
892
|
@connected = true
|
|
893
|
+
return unless @streaming
|
|
862
894
|
|
|
863
895
|
@ws_manager = ensure_ws
|
|
864
896
|
@ws_manager.on("config_changed") { |data| handle_config_changed(data) }
|
|
@@ -992,7 +1024,13 @@ module Smplkit
|
|
|
992
1024
|
def trigger_background_flush_if_needed
|
|
993
1025
|
return unless @buffer.pending_count >= CONFIG_BATCH_FLUSH_SIZE
|
|
994
1026
|
|
|
995
|
-
|
|
1027
|
+
# Stateless mode (+streaming: false+) never spawns background threads —
|
|
1028
|
+
# the threshold flush runs inline (blocking) instead.
|
|
1029
|
+
if @streaming
|
|
1030
|
+
Thread.new { threshold_flush }
|
|
1031
|
+
else
|
|
1032
|
+
threshold_flush
|
|
1033
|
+
end
|
|
996
1034
|
end
|
|
997
1035
|
|
|
998
1036
|
def threshold_flush
|
|
@@ -36,7 +36,7 @@ module Smplkit
|
|
|
36
36
|
)
|
|
37
37
|
|
|
38
38
|
ResolvedClientConfig = Struct.new(
|
|
39
|
-
:api_key, :base_domain, :scheme, :debug, :extra_headers,
|
|
39
|
+
:api_key, :base_domain, :scheme, :environment, :service, :debug, :extra_headers,
|
|
40
40
|
keyword_init: true
|
|
41
41
|
)
|
|
42
42
|
|
|
@@ -160,18 +160,21 @@ module Smplkit
|
|
|
160
160
|
end
|
|
161
161
|
|
|
162
162
|
def resolve_client_config(profile: nil, api_key: nil, base_domain: nil,
|
|
163
|
-
scheme: nil,
|
|
163
|
+
scheme: nil, environment: nil, service: nil,
|
|
164
|
+
debug: nil, home_dir: nil)
|
|
164
165
|
resolved = {
|
|
165
166
|
"api_key" => nil,
|
|
166
167
|
"base_domain" => "smplkit.com",
|
|
167
168
|
"scheme" => "https",
|
|
169
|
+
"environment" => nil,
|
|
170
|
+
"service" => nil,
|
|
168
171
|
"debug" => false
|
|
169
172
|
}
|
|
170
173
|
|
|
171
174
|
active_profile = profile || ENV["SMPLKIT_PROFILE"] || "default"
|
|
172
175
|
|
|
173
176
|
file_values = read_config_file(active_profile, home_dir: home_dir)
|
|
174
|
-
%w[api_key base_domain scheme debug].each do |key|
|
|
177
|
+
%w[api_key base_domain scheme environment service debug].each do |key|
|
|
175
178
|
next unless file_values.key?(key)
|
|
176
179
|
|
|
177
180
|
val = file_values[key]
|
|
@@ -180,7 +183,8 @@ module Smplkit
|
|
|
180
183
|
|
|
181
184
|
[
|
|
182
185
|
%w[api_key SMPLKIT_API_KEY], %w[base_domain SMPLKIT_BASE_DOMAIN],
|
|
183
|
-
%w[scheme SMPLKIT_SCHEME], %w[
|
|
186
|
+
%w[scheme SMPLKIT_SCHEME], %w[environment SMPLKIT_ENVIRONMENT],
|
|
187
|
+
%w[service SMPLKIT_SERVICE], %w[debug SMPLKIT_DEBUG]
|
|
184
188
|
].each do |key, env_var|
|
|
185
189
|
env_val = ENV.fetch(env_var, "")
|
|
186
190
|
next if env_val.empty?
|
|
@@ -188,7 +192,10 @@ module Smplkit
|
|
|
188
192
|
resolved[key] = key == "debug" ? parse_bool(env_val, env_var) : env_val
|
|
189
193
|
end
|
|
190
194
|
|
|
191
|
-
ctor = {
|
|
195
|
+
ctor = {
|
|
196
|
+
"api_key" => api_key, "base_domain" => base_domain, "scheme" => scheme,
|
|
197
|
+
"environment" => environment, "service" => service, "debug" => debug
|
|
198
|
+
}
|
|
192
199
|
ctor.each { |k, v| resolved[k] = v unless v.nil? }
|
|
193
200
|
|
|
194
201
|
missing_required(resolved, "api_key", active_profile)
|
|
@@ -197,6 +204,11 @@ module Smplkit
|
|
|
197
204
|
api_key: resolved["api_key"].to_s,
|
|
198
205
|
base_domain: resolved["base_domain"].to_s,
|
|
199
206
|
scheme: resolved["scheme"].to_s,
|
|
207
|
+
# Preserve nil rather than coercing to the literal string "" —
|
|
208
|
+
# +environment+ and +service+ are optional and their absence is
|
|
209
|
+
# meaningful (no environment signal is sent on the wire).
|
|
210
|
+
environment: resolved["environment"]&.to_s,
|
|
211
|
+
service: resolved["service"]&.to_s,
|
|
200
212
|
debug: resolved["debug"] ? true : false
|
|
201
213
|
)
|
|
202
214
|
end
|
data/lib/smplkit/flags/client.rb
CHANGED
|
@@ -145,14 +145,17 @@ module Smplkit
|
|
|
145
145
|
# +base_url+/+api_key+ are used directly when supplied (the path a top-level
|
|
146
146
|
# client takes after it has already resolved them); otherwise the management
|
|
147
147
|
# config resolver fills in whatever is missing (+~/.smplkit+ / env vars /
|
|
148
|
-
# defaults).
|
|
148
|
+
# defaults). +environment+/+service+ resolve the same way (constructor
|
|
149
|
+
# argument wins). The app transport backs the standalone contexts client
|
|
149
150
|
# (evaluation-context registration); the app base URL is returned so a
|
|
150
151
|
# standalone client can open its own WebSocket against the event gateway.
|
|
151
152
|
#
|
|
152
153
|
# @api private
|
|
153
|
-
def self.flags_transport(api_key:, base_url:, profile:, base_domain:, scheme:,
|
|
154
|
+
def self.flags_transport(api_key:, base_url:, profile:, base_domain:, scheme:,
|
|
155
|
+
environment:, service:, debug:, extra_headers:)
|
|
154
156
|
cfg = ConfigResolution.resolve_client_config(
|
|
155
|
-
profile: profile, api_key: api_key, base_domain: base_domain, scheme: scheme,
|
|
157
|
+
profile: profile, api_key: api_key, base_domain: base_domain, scheme: scheme,
|
|
158
|
+
environment: environment, service: service, debug: debug
|
|
156
159
|
)
|
|
157
160
|
resolved_key = api_key.nil? ? cfg.api_key : api_key
|
|
158
161
|
merged = {}
|
|
@@ -165,7 +168,7 @@ module Smplkit
|
|
|
165
168
|
app_url = ConfigResolution.service_url(cfg.scheme, "app", cfg.base_domain)
|
|
166
169
|
flags_http = Transport.build_api_client(SmplkitGeneratedClient::Flags, "flags", tcfg, base_url: base_url)
|
|
167
170
|
app_http = Transport.build_api_client(SmplkitGeneratedClient::App, "app", tcfg)
|
|
168
|
-
[flags_http, app_http, app_url, resolved_key]
|
|
171
|
+
[flags_http, app_http, app_url, resolved_key, cfg.environment, cfg.service]
|
|
169
172
|
end
|
|
170
173
|
|
|
171
174
|
# The Smpl Flags client (sync).
|
|
@@ -186,24 +189,68 @@ module Smplkit
|
|
|
186
189
|
# definitions into the local cache, and opens the live-updates WebSocket. No
|
|
187
190
|
# explicit install step is required.
|
|
188
191
|
class FlagsClient
|
|
189
|
-
|
|
192
|
+
# @param api_key [String, nil] API key. When omitted, resolved from
|
|
193
|
+
# +SMPLKIT_API_KEY+ or +~/.smplkit+.
|
|
194
|
+
# @param environment [String, nil] Deployment environment used to resolve
|
|
195
|
+
# runtime flag values and to scope discovery declarations. When
|
|
196
|
+
# omitted, resolved from +SMPLKIT_ENVIRONMENT+ or +~/.smplkit+.
|
|
197
|
+
# @param service [String, nil] Service name attached to discovery
|
|
198
|
+
# declarations and auto-injected into the evaluation context. When
|
|
199
|
+
# omitted, resolved from +SMPLKIT_SERVICE+ or +~/.smplkit+. Optional.
|
|
200
|
+
# @param base_url [String, nil] Full flags-service base URL. Usually
|
|
201
|
+
# resolved from +base_domain+/+scheme+; supplied directly by the
|
|
202
|
+
# top-level clients which have already computed it.
|
|
203
|
+
# @param profile [String, nil] Named +~/.smplkit+ profile section.
|
|
204
|
+
# @param base_domain [String, nil] Base domain for API requests (default
|
|
205
|
+
# +"smplkit.com"+).
|
|
206
|
+
# @param scheme [String, nil] URL scheme (default +"https"+).
|
|
207
|
+
# @param debug [Boolean, nil] Enable SDK debug logging.
|
|
208
|
+
# @param extra_headers [Hash{String => String}, nil] Extra headers
|
|
209
|
+
# attached to every request.
|
|
210
|
+
# @param streaming [Boolean] Live updates over WebSocket (default
|
|
211
|
+
# +true+): the first live call opens a shared socket and flag changes
|
|
212
|
+
# stream in. Set +false+ for the stateless read-through surface: the
|
|
213
|
+
# first live call still fetches all flag definitions once (blocking),
|
|
214
|
+
# evaluation stays local, +refresh+ re-fetches on demand, and NO socket
|
|
215
|
+
# or background thread is ever created — the right shape for serverless
|
|
216
|
+
# and edge runtimes. +on_change+ listeners fire only from explicit
|
|
217
|
+
# +refresh+ calls in this mode (there is no stream to drive them).
|
|
218
|
+
# @param parent [Smplkit::Client, nil] Internal — the owning client. Not
|
|
219
|
+
# for direct use.
|
|
220
|
+
# @param transport [Object, nil] Internal — a pre-built flags transport
|
|
221
|
+
# supplied by a top-level client so the flags surface shares one
|
|
222
|
+
# connection pool. Not for direct use.
|
|
223
|
+
# @param contexts [Object, nil] Internal — +client.platform.contexts+
|
|
224
|
+
# used for evaluation-context registration. Not for direct use.
|
|
225
|
+
# @param metrics [Object, nil] Internal — the parent's metrics reporter.
|
|
226
|
+
def initialize(api_key = nil, environment: nil, service: nil, base_url: nil, profile: nil,
|
|
190
227
|
base_domain: nil, scheme: nil, debug: nil, extra_headers: nil,
|
|
191
|
-
parent: nil, transport: nil, contexts: nil, metrics: nil)
|
|
228
|
+
streaming: true, parent: nil, transport: nil, contexts: nil, metrics: nil)
|
|
192
229
|
@parent = parent
|
|
193
230
|
@metrics = metrics
|
|
194
|
-
@
|
|
195
|
-
@service = parent&._service
|
|
231
|
+
@streaming = streaming ? true : false
|
|
196
232
|
@standalone_api_key = nil
|
|
197
233
|
if transport.nil?
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
234
|
+
# Standalone: resolve like Smplkit::Client — defaults → ~/.smplkit →
|
|
235
|
+
# SMPLKIT_* env vars → constructor args — environment and service
|
|
236
|
+
# included.
|
|
237
|
+
@flags_http, app_http, @app_base_url, @standalone_api_key, resolved_env, resolved_service =
|
|
238
|
+
Flags.flags_transport(
|
|
239
|
+
api_key: api_key, base_url: base_url, profile: profile,
|
|
240
|
+
base_domain: base_domain, scheme: scheme, environment: environment,
|
|
241
|
+
service: service, debug: debug, extra_headers: extra_headers
|
|
242
|
+
)
|
|
243
|
+
@environment = parent.nil? ? resolved_env : parent._environment
|
|
244
|
+
@service = parent.nil? ? resolved_service : parent._service
|
|
202
245
|
# Standalone: build our own contexts client (and own its app transport).
|
|
203
246
|
@contexts = Platform::ContextsClient.new(app_http, ContextRegistrationBuffer.new)
|
|
204
247
|
else
|
|
205
248
|
@flags_http = transport
|
|
206
249
|
@app_base_url = nil
|
|
250
|
+
# Wired: the parent has already resolved environment/service once —
|
|
251
|
+
# its values win over both the raw kwargs and re-resolution.
|
|
252
|
+
@environment = parent.nil? ? environment : parent._environment
|
|
253
|
+
@service = parent.nil? ? service : parent._service
|
|
207
254
|
# Wired: borrow client.platform.contexts as the evaluation-context
|
|
208
255
|
# registration seam.
|
|
209
256
|
@contexts = contexts
|
|
@@ -372,7 +419,13 @@ module Smplkit
|
|
|
372
419
|
end
|
|
373
420
|
return unless @buffer.pending_count >= FLAG_BATCH_FLUSH_SIZE
|
|
374
421
|
|
|
375
|
-
|
|
422
|
+
# Stateless mode (+streaming: false+) never spawns background threads —
|
|
423
|
+
# the threshold flush runs inline (blocking) instead.
|
|
424
|
+
if @streaming
|
|
425
|
+
Thread.new { threshold_flush }
|
|
426
|
+
else
|
|
427
|
+
threshold_flush
|
|
428
|
+
end
|
|
376
429
|
end
|
|
377
430
|
|
|
378
431
|
# POST pending declarations to the flags bulk endpoint.
|
|
@@ -648,6 +701,8 @@ module Smplkit
|
|
|
648
701
|
# Flushes any buffered discovery declarations, fetches all flag
|
|
649
702
|
# definitions into the local cache, opens the shared WebSocket, and
|
|
650
703
|
# subscribes to +flag_changed+ / +flag_deleted+ / +flags_changed+ events.
|
|
704
|
+
# In stateless mode (+streaming: false+) no socket is ever created;
|
|
705
|
+
# +refresh+ re-fetches on demand.
|
|
651
706
|
#
|
|
652
707
|
# Idempotent and internal — every live method calls it on first use, so
|
|
653
708
|
# the live surface auto-connects with no explicit step.
|
|
@@ -666,6 +721,7 @@ module Smplkit
|
|
|
666
721
|
fetch_all_flags
|
|
667
722
|
@cache.clear
|
|
668
723
|
@connected = true
|
|
724
|
+
return unless @streaming
|
|
669
725
|
|
|
670
726
|
@ws_manager = ensure_ws
|
|
671
727
|
return if @ws_subscribed
|
data/lib/smplkit/jobs/client.rb
CHANGED
|
@@ -284,12 +284,14 @@ module Smplkit
|
|
|
284
284
|
# Reuses the config resolver (jobs is account-global and never
|
|
285
285
|
# environment-scoped at the transport layer) so a standalone jobs client
|
|
286
286
|
# resolves credentials/base-domain from +~/.smplkit+ / env vars /
|
|
287
|
-
# constructor args exactly like the top-level clients do.
|
|
288
|
-
#
|
|
289
|
-
#
|
|
290
|
-
|
|
287
|
+
# constructor args exactly like the top-level clients do. The client-level
|
|
288
|
+
# default +environment+ resolves through the same chain (constructor
|
|
289
|
+
# argument wins) and is returned alongside. Smpl Jobs is JSON:API, so the
|
|
290
|
+
# transport carries the +application/vnd.api+json+ Accept header.
|
|
291
|
+
def self.jobs_transport(api_key:, profile:, base_domain:, scheme:, environment:, debug:, extra_headers:)
|
|
291
292
|
cfg = ConfigResolution.resolve_client_config(
|
|
292
|
-
profile: profile, api_key: api_key, base_domain: base_domain, scheme: scheme,
|
|
293
|
+
profile: profile, api_key: api_key, base_domain: base_domain, scheme: scheme,
|
|
294
|
+
environment: environment, debug: debug
|
|
293
295
|
)
|
|
294
296
|
merged = {}
|
|
295
297
|
merged.merge!(cfg.extra_headers || {})
|
|
@@ -298,7 +300,10 @@ module Smplkit
|
|
|
298
300
|
api_key: cfg.api_key, base_domain: cfg.base_domain, scheme: cfg.scheme,
|
|
299
301
|
debug: cfg.debug, extra_headers: merged
|
|
300
302
|
)
|
|
301
|
-
Transport.build_api_client(
|
|
303
|
+
transport = Transport.build_api_client(
|
|
304
|
+
SmplkitGeneratedClient::Jobs, "jobs", tcfg, accept: "application/vnd.api+json"
|
|
305
|
+
)
|
|
306
|
+
[transport, cfg.environment]
|
|
302
307
|
end
|
|
303
308
|
|
|
304
309
|
# The active-record entry points are {#new_recurring_job}, {#new_manual_job},
|
|
@@ -327,18 +332,26 @@ module Smplkit
|
|
|
327
332
|
# @param environment [String, nil] Default environment for
|
|
328
333
|
# environment-scoped operations — the environment a one-off job created
|
|
329
334
|
# through this client is born in, the default a manual run executes in,
|
|
330
|
-
# and the default scope for +runs.list+.
|
|
335
|
+
# and the default scope for +runs.list+. When omitted, resolved from
|
|
336
|
+
# +SMPLKIT_ENVIRONMENT+ or +~/.smplkit+. +nil+ leaves these unset (the
|
|
331
337
|
# credential's permitted environment is implied where unambiguous).
|
|
332
338
|
# @param auth_client [Object, nil] Internal — a pre-built transport
|
|
333
339
|
# supplied by a top-level client so the jobs surface shares one
|
|
334
340
|
# connection pool. Not for direct use.
|
|
335
341
|
def initialize(api_key = nil, profile: nil, base_domain: nil, scheme: nil,
|
|
336
342
|
debug: nil, extra_headers: nil, environment: nil, auth_client: nil)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
343
|
+
if auth_client.nil?
|
|
344
|
+
# Standalone: resolve like Smplkit::Client — defaults → ~/.smplkit →
|
|
345
|
+
# SMPLKIT_* env vars → constructor args — environment included.
|
|
346
|
+
auth, resolved_env = Jobs.jobs_transport(
|
|
347
|
+
api_key: api_key, profile: profile, base_domain: base_domain,
|
|
348
|
+
scheme: scheme, environment: environment, debug: debug, extra_headers: extra_headers
|
|
349
|
+
)
|
|
350
|
+
@environment = resolved_env
|
|
351
|
+
else
|
|
352
|
+
auth = auth_client
|
|
353
|
+
@environment = environment
|
|
354
|
+
end
|
|
342
355
|
@api = SmplkitGeneratedClient::Jobs::JobsApi.new(auth)
|
|
343
356
|
@runs = RunsClient.new(SmplkitGeneratedClient::Jobs::RunsApi.new(auth), environment: environment)
|
|
344
357
|
@retry_policies = RetryPoliciesClient.new(SmplkitGeneratedClient::Jobs::RetryPoliciesApi.new(auth))
|
|
@@ -44,14 +44,18 @@ module Smplkit
|
|
|
44
44
|
# +base_url+/+api_key+ are used directly when supplied (the path a top-level
|
|
45
45
|
# client takes after it has already resolved them); otherwise the management
|
|
46
46
|
# config resolver fills in whatever is missing (+~/.smplkit+ / env vars /
|
|
47
|
-
# defaults).
|
|
48
|
-
#
|
|
49
|
-
#
|
|
47
|
+
# defaults). +environment+/+service+ resolve the same way (constructor
|
|
48
|
+
# argument wins). The app transport is needed for the WebSocket gateway,
|
|
49
|
+
# which lives on the app service (like flags); the app base URL is returned
|
|
50
|
+
# so a standalone client can open its own WebSocket against the event
|
|
51
|
+
# gateway.
|
|
50
52
|
#
|
|
51
53
|
# @api private
|
|
52
|
-
def self.logging_transport(api_key:, base_url:, profile:, base_domain:, scheme:,
|
|
54
|
+
def self.logging_transport(api_key:, base_url:, profile:, base_domain:, scheme:,
|
|
55
|
+
environment:, service:, debug:, extra_headers:)
|
|
53
56
|
cfg = ConfigResolution.resolve_client_config(
|
|
54
|
-
profile: profile, api_key: api_key, base_domain: base_domain, scheme: scheme,
|
|
57
|
+
profile: profile, api_key: api_key, base_domain: base_domain, scheme: scheme,
|
|
58
|
+
environment: environment, service: service, debug: debug
|
|
55
59
|
)
|
|
56
60
|
resolved_key = api_key.nil? ? cfg.api_key : api_key
|
|
57
61
|
merged = {}
|
|
@@ -64,7 +68,7 @@ module Smplkit
|
|
|
64
68
|
app_url = ConfigResolution.service_url(cfg.scheme, "app", cfg.base_domain)
|
|
65
69
|
logging_http = Transport.build_api_client(SmplkitGeneratedClient::Logging, "logging", tcfg, base_url: base_url)
|
|
66
70
|
app_http = Transport.build_api_client(SmplkitGeneratedClient::App, "app", tcfg)
|
|
67
|
-
[logging_http, app_http, app_url, resolved_key]
|
|
71
|
+
[logging_http, app_http, app_url, resolved_key, cfg.environment, cfg.service]
|
|
68
72
|
end
|
|
69
73
|
|
|
70
74
|
# Discover and load the SDK's built-in logging adapters.
|
|
@@ -107,9 +111,15 @@ module Smplkit
|
|
|
107
111
|
# +LoggingClient+ and shared here so discovery (driven by
|
|
108
112
|
# +LoggingClient#install+) and explicit +register+ drain through one queue.
|
|
109
113
|
class LoggersClient
|
|
110
|
-
|
|
114
|
+
# @param http_client [Object] The logging-service transport.
|
|
115
|
+
# @param buffer [LoggerRegistrationBuffer] The shared discovery buffer,
|
|
116
|
+
# owned by the fused +LoggingClient+.
|
|
117
|
+
# @param streaming [Boolean] Internal — +false+ runs threshold flushes
|
|
118
|
+
# inline instead of on a background thread (stateless mode).
|
|
119
|
+
def initialize(http_client, buffer:, streaming: true)
|
|
111
120
|
@api = SmplkitGeneratedClient::Logging::LoggersApi.new(http_client)
|
|
112
121
|
@buffer = buffer
|
|
122
|
+
@streaming = streaming ? true : false
|
|
113
123
|
end
|
|
114
124
|
|
|
115
125
|
# Queue one or more logger sources for registration with the server.
|
|
@@ -138,7 +148,13 @@ module Smplkit
|
|
|
138
148
|
end
|
|
139
149
|
return unless @buffer.pending_count >= LOGGER_BATCH_FLUSH_SIZE
|
|
140
150
|
|
|
141
|
-
|
|
151
|
+
# Stateless mode (+streaming: false+) never spawns background threads —
|
|
152
|
+
# the threshold flush runs inline (blocking) instead.
|
|
153
|
+
if @streaming
|
|
154
|
+
Thread.new { threshold_flush }
|
|
155
|
+
else
|
|
156
|
+
threshold_flush
|
|
157
|
+
end
|
|
142
158
|
end
|
|
143
159
|
|
|
144
160
|
# Drain the buffer and POST pending logger sources to the bulk endpoint.
|
|
@@ -423,28 +439,70 @@ module Smplkit
|
|
|
423
439
|
class LoggingClient
|
|
424
440
|
attr_reader :loggers, :log_groups
|
|
425
441
|
|
|
426
|
-
|
|
442
|
+
# @param api_key [String, nil] API key. When omitted, resolved from
|
|
443
|
+
# +SMPLKIT_API_KEY+ or +~/.smplkit+.
|
|
444
|
+
# @param environment [String, nil] Deployment environment used to resolve
|
|
445
|
+
# logger levels and to scope discovery declarations. When omitted,
|
|
446
|
+
# resolved from +SMPLKIT_ENVIRONMENT+ or +~/.smplkit+.
|
|
447
|
+
# @param service [String, nil] Service name attached to discovery
|
|
448
|
+
# declarations. When omitted, resolved from +SMPLKIT_SERVICE+ or
|
|
449
|
+
# +~/.smplkit+. Optional.
|
|
450
|
+
# @param base_url [String, nil] Full logging-service base URL. Usually
|
|
451
|
+
# resolved from +base_domain+/+scheme+; supplied directly by the
|
|
452
|
+
# top-level clients which have already computed it.
|
|
453
|
+
# @param profile [String, nil] Named +~/.smplkit+ profile section.
|
|
454
|
+
# @param base_domain [String, nil] Base domain for API requests (default
|
|
455
|
+
# +"smplkit.com"+).
|
|
456
|
+
# @param scheme [String, nil] URL scheme (default +"https"+).
|
|
457
|
+
# @param debug [Boolean, nil] Enable SDK debug logging.
|
|
458
|
+
# @param extra_headers [Hash{String => String}, nil] Extra headers
|
|
459
|
+
# attached to every request.
|
|
460
|
+
# @param streaming [Boolean] Live updates over WebSocket (default
|
|
461
|
+
# +true+): +install+ opens a shared socket and server-side level
|
|
462
|
+
# changes stream in. Set +false+ for the stateless apply-once surface:
|
|
463
|
+
# +install+ still loads adapters, flushes discovery, and applies the
|
|
464
|
+
# server's levels — all blocking — but NO socket or background thread
|
|
465
|
+
# is ever created; +refresh+ re-fetches and re-applies on demand. The
|
|
466
|
+
# right shape for serverless and edge runtimes; note that live level
|
|
467
|
+
# changes then arrive only via +refresh+.
|
|
468
|
+
# @param parent [Smplkit::Client, nil] Internal — the owning client. Not
|
|
469
|
+
# for direct use.
|
|
470
|
+
# @param transport [Object, nil] Internal — a pre-built logging transport
|
|
471
|
+
# supplied by a top-level client so the logging surface shares one
|
|
472
|
+
# connection pool. Not for direct use.
|
|
473
|
+
# @param metrics [Object, nil] Internal — the parent's metrics reporter.
|
|
474
|
+
def initialize(api_key = nil, environment: nil, service: nil, base_url: nil, profile: nil,
|
|
427
475
|
base_domain: nil, scheme: nil, debug: nil, extra_headers: nil,
|
|
428
|
-
parent: nil, transport: nil, metrics: nil)
|
|
476
|
+
streaming: true, parent: nil, transport: nil, metrics: nil)
|
|
429
477
|
@parent = parent
|
|
430
478
|
@metrics = metrics
|
|
431
|
-
@
|
|
432
|
-
@service = parent&._service
|
|
479
|
+
@streaming = streaming ? true : false
|
|
433
480
|
@standalone_api_key = nil
|
|
434
481
|
if transport.nil?
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
482
|
+
# Standalone: resolve like Smplkit::Client — defaults → ~/.smplkit →
|
|
483
|
+
# SMPLKIT_* env vars → constructor args — environment and service
|
|
484
|
+
# included.
|
|
485
|
+
@logging_http, _app_http, @app_base_url, @standalone_api_key, resolved_env, resolved_service =
|
|
486
|
+
Logging.logging_transport(
|
|
487
|
+
api_key: api_key, base_url: base_url, profile: profile,
|
|
488
|
+
base_domain: base_domain, scheme: scheme, environment: environment,
|
|
489
|
+
service: service, debug: debug, extra_headers: extra_headers
|
|
490
|
+
)
|
|
491
|
+
@environment = parent.nil? ? resolved_env : parent._environment
|
|
492
|
+
@service = parent.nil? ? resolved_service : parent._service
|
|
439
493
|
else
|
|
440
494
|
@logging_http = transport
|
|
441
495
|
@app_base_url = nil
|
|
496
|
+
# Wired: the parent has already resolved environment/service once —
|
|
497
|
+
# its values win over both the raw kwargs and re-resolution.
|
|
498
|
+
@environment = parent.nil? ? environment : parent._environment
|
|
499
|
+
@service = parent.nil? ? service : parent._service
|
|
442
500
|
end
|
|
443
501
|
|
|
444
502
|
# Discovery buffer is owned by this client; the loggers sub-client shares
|
|
445
503
|
# it so discovery and explicit registration drain together.
|
|
446
504
|
@buffer = LoggerRegistrationBuffer.new
|
|
447
|
-
@loggers = LoggersClient.new(@logging_http, buffer: @buffer)
|
|
505
|
+
@loggers = LoggersClient.new(@logging_http, buffer: @buffer, streaming: @streaming)
|
|
448
506
|
@log_groups = LogGroupsClient.new(@logging_http)
|
|
449
507
|
|
|
450
508
|
# Live-surface state.
|
|
@@ -542,9 +600,13 @@ module Smplkit
|
|
|
542
600
|
"(logging: #{@logging_http&.config&.host}): #{e.class}: #{e.message}")
|
|
543
601
|
end
|
|
544
602
|
|
|
545
|
-
# 7. Register WebSocket event handlers for real-time level updates
|
|
546
|
-
|
|
547
|
-
|
|
603
|
+
# 7. Register WebSocket event handlers for real-time level updates.
|
|
604
|
+
# In stateless mode (+streaming: false+) no socket is ever created —
|
|
605
|
+
# level changes then arrive only via +refresh+.
|
|
606
|
+
if @streaming
|
|
607
|
+
@ws_manager = ensure_ws
|
|
608
|
+
ws_handlers.each { |event, handler| @ws_manager.on(event, &handler) }
|
|
609
|
+
end
|
|
548
610
|
|
|
549
611
|
@connected = true
|
|
550
612
|
self
|