smplkit 1.0.11 → 1.0.12
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 +8 -1
- data/lib/smplkit/config_resolution.rb +4 -1
- data/lib/smplkit/management/client.rb +85 -43
- data/lib/smplkit/ws.rb +7 -1
- 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: 33719678260c3b3cd9ae01b3a7d58226363cc292d93130600622df9ba4d259ec
|
|
4
|
+
data.tar.gz: 6c5a7f1f074f2b3f87266929598722da16cf44209057ccb147cff4bb84cb2e90
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a0f1c4f3b2e0100c229f9e64d78410289891024b97ffc3b7ff2af3daf7e94f60d58677d75f08ddc3b009d1543cc39d19612fe5d3a0cc20852498423d9f03f095
|
|
7
|
+
data.tar.gz: a6b6c420674b9f983e925134b12cea12ef97edf8d992113845bf458042af29ff44492a7391a968d23d0aa14d00131757f4a61cb99ff3005c1fa8ce790a0d3e85
|
data/README.md
CHANGED
|
@@ -77,7 +77,14 @@ Two adapters ship at launch (per ADR-046 §2.3):
|
|
|
77
77
|
| `stdlib-logger` | Ruby stdlib `Logger` (and Rails via `ActiveSupport::Logger`) |
|
|
78
78
|
| `semantic-logger` | The [`semantic_logger` gem](https://github.com/reidmorrison/semantic_logger) |
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
Both are auto-loaded by `install` when the corresponding framework is available. To support an additional framework, subclass `Smplkit::Logging::Adapters::Base` and implement the five contract methods (`name`, `discover`, `apply_level`, `install_hook`, `uninstall_hook`), then register before `install`:
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
client.logging.register_adapter(MyFrameworkAdapter.new)
|
|
84
|
+
client.logging.install
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Calling `register_adapter` disables auto-loading — only the adapters you register are used.
|
|
81
88
|
|
|
82
89
|
## Rails integration
|
|
83
90
|
|
|
@@ -81,7 +81,10 @@ module Smplkit
|
|
|
81
81
|
|
|
82
82
|
sections =
|
|
83
83
|
begin
|
|
84
|
-
|
|
84
|
+
# Force UTF-8 — the file may contain comments with non-ASCII bytes
|
|
85
|
+
# (em dashes, smart quotes) and the system's default external
|
|
86
|
+
# encoding is locale-dependent.
|
|
87
|
+
parse_ini(File.read(path, encoding: "UTF-8"))
|
|
85
88
|
rescue StandardError
|
|
86
89
|
return {}
|
|
87
90
|
end
|
|
@@ -143,25 +143,25 @@ module Smplkit
|
|
|
143
143
|
return if batch.empty?
|
|
144
144
|
|
|
145
145
|
body = { "data" => { "type" => "context_bulk_register", "attributes" => { "contexts" => batch } } }
|
|
146
|
-
http_post("/api/contexts/
|
|
146
|
+
http_post("/api/v1/contexts/bulk", body)
|
|
147
147
|
rescue StandardError => e
|
|
148
148
|
Smplkit.debug("registration", "context flush failed: #{e.class}: #{e.message}")
|
|
149
149
|
end
|
|
150
150
|
|
|
151
151
|
def list
|
|
152
|
-
list_resp = http_list("/api/contexts
|
|
152
|
+
list_resp = http_list("/api/v1/contexts")
|
|
153
153
|
list_resp.map { |r| context_from_resource(r) }
|
|
154
154
|
end
|
|
155
155
|
|
|
156
156
|
def get(id_or_type, key = nil)
|
|
157
157
|
type, ckey = split_id(id_or_type, key)
|
|
158
|
-
resource = http_get("/api/contexts
|
|
158
|
+
resource = http_get("/api/v1/contexts/#{type}:#{ckey}")
|
|
159
159
|
context_from_resource(resource["data"])
|
|
160
160
|
end
|
|
161
161
|
|
|
162
162
|
def delete(id_or_type, key = nil)
|
|
163
163
|
type, ckey = split_id(id_or_type, key)
|
|
164
|
-
http_delete("/api/contexts
|
|
164
|
+
http_delete("/api/v1/contexts/#{type}:#{ckey}")
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
def _save_context(ctx)
|
|
@@ -172,7 +172,7 @@ module Smplkit
|
|
|
172
172
|
"attributes" => { "type" => ctx.type, "key" => ctx.key, "attributes" => ctx.attributes }.compact
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
-
resp = http_put("/api/contexts
|
|
175
|
+
resp = http_put("/api/v1/contexts/#{ctx.id}", body)
|
|
176
176
|
context_from_resource(resp["data"]).tap { |c| c._bind_client(self) }
|
|
177
177
|
end
|
|
178
178
|
|
|
@@ -210,17 +210,17 @@ module Smplkit
|
|
|
210
210
|
end
|
|
211
211
|
|
|
212
212
|
def list
|
|
213
|
-
list_resp = http_list("/api/context_types
|
|
213
|
+
list_resp = http_list("/api/v1/context_types")
|
|
214
214
|
list_resp.map { |r| from_resource(r) }
|
|
215
215
|
end
|
|
216
216
|
|
|
217
217
|
def get(key)
|
|
218
|
-
resp = http_get("/api/context_types
|
|
218
|
+
resp = http_get("/api/v1/context_types/#{key}")
|
|
219
219
|
from_resource(resp["data"])
|
|
220
220
|
end
|
|
221
221
|
|
|
222
222
|
def delete(key)
|
|
223
|
-
http_delete("/api/context_types
|
|
223
|
+
http_delete("/api/v1/context_types/#{key}")
|
|
224
224
|
end
|
|
225
225
|
|
|
226
226
|
def new_context_type(key, name: nil, description: nil)
|
|
@@ -228,12 +228,12 @@ module Smplkit
|
|
|
228
228
|
end
|
|
229
229
|
|
|
230
230
|
def _create_context_type(ct)
|
|
231
|
-
resp = http_post("/api/context_types
|
|
231
|
+
resp = http_post("/api/v1/context_types", body_for(ct))
|
|
232
232
|
from_resource(resp["data"])
|
|
233
233
|
end
|
|
234
234
|
|
|
235
235
|
def _update_context_type(ct)
|
|
236
|
-
resp = http_put("/api/context_types
|
|
236
|
+
resp = http_put("/api/v1/context_types/#{ct.key}", body_for(ct))
|
|
237
237
|
from_resource(resp["data"])
|
|
238
238
|
end
|
|
239
239
|
|
|
@@ -268,17 +268,17 @@ module Smplkit
|
|
|
268
268
|
end
|
|
269
269
|
|
|
270
270
|
def list
|
|
271
|
-
list_resp = http_list("/api/environments
|
|
271
|
+
list_resp = http_list("/api/v1/environments")
|
|
272
272
|
list_resp.map { |r| from_resource(r) }
|
|
273
273
|
end
|
|
274
274
|
|
|
275
275
|
def get(key)
|
|
276
|
-
resp = http_get("/api/environments
|
|
276
|
+
resp = http_get("/api/v1/environments/#{key}")
|
|
277
277
|
from_resource(resp["data"])
|
|
278
278
|
end
|
|
279
279
|
|
|
280
280
|
def delete(key)
|
|
281
|
-
http_delete("/api/environments
|
|
281
|
+
http_delete("/api/v1/environments/#{key}")
|
|
282
282
|
end
|
|
283
283
|
|
|
284
284
|
def new(key, name: nil, color: nil,
|
|
@@ -293,12 +293,12 @@ module Smplkit
|
|
|
293
293
|
end
|
|
294
294
|
|
|
295
295
|
def _create_environment(env)
|
|
296
|
-
resp = http_post("/api/environments
|
|
296
|
+
resp = http_post("/api/v1/environments", body_for(env))
|
|
297
297
|
from_resource(resp["data"])
|
|
298
298
|
end
|
|
299
299
|
|
|
300
300
|
def _update_environment(env)
|
|
301
|
-
resp = http_put("/api/environments
|
|
301
|
+
resp = http_put("/api/v1/environments/#{env.key}", body_for(env))
|
|
302
302
|
from_resource(resp["data"])
|
|
303
303
|
end
|
|
304
304
|
|
|
@@ -342,12 +342,12 @@ module Smplkit
|
|
|
342
342
|
end
|
|
343
343
|
|
|
344
344
|
def get
|
|
345
|
-
resp = http_get("/api/
|
|
345
|
+
resp = http_get("/api/v1/accounts/current/settings")
|
|
346
346
|
from_resource(resp["data"])
|
|
347
347
|
end
|
|
348
348
|
|
|
349
349
|
def _update_account_settings(settings)
|
|
350
|
-
resp = http_put("/api/
|
|
350
|
+
resp = http_put("/api/v1/accounts/current/settings", body_for(settings))
|
|
351
351
|
from_resource(resp["data"])
|
|
352
352
|
end
|
|
353
353
|
|
|
@@ -385,17 +385,17 @@ module Smplkit
|
|
|
385
385
|
end
|
|
386
386
|
|
|
387
387
|
def list
|
|
388
|
-
list_resp = http_list("/api/configs
|
|
388
|
+
list_resp = http_list("/api/v1/configs")
|
|
389
389
|
list_resp.map { |r| Smplkit::Config::Helpers.config_from_json(self, r) }
|
|
390
390
|
end
|
|
391
391
|
|
|
392
392
|
def get(key)
|
|
393
|
-
resp = http_get("/api/configs
|
|
393
|
+
resp = http_get("/api/v1/configs/#{key}")
|
|
394
394
|
Smplkit::Config::Helpers.config_from_json(self, resp["data"])
|
|
395
395
|
end
|
|
396
396
|
|
|
397
397
|
def delete(key)
|
|
398
|
-
http_delete("/api/configs
|
|
398
|
+
http_delete("/api/v1/configs/#{key}")
|
|
399
399
|
end
|
|
400
400
|
|
|
401
401
|
def new_config(key, name: nil, description: nil, parent: nil)
|
|
@@ -407,19 +407,61 @@ module Smplkit
|
|
|
407
407
|
|
|
408
408
|
def _create_config(config)
|
|
409
409
|
body = Smplkit::Config::Helpers.build_config_request_body(config)
|
|
410
|
-
resp = http_post("/api/configs
|
|
410
|
+
resp = http_post("/api/v1/configs", body)
|
|
411
411
|
Smplkit::Config::Helpers.config_from_json(self, resp["data"])
|
|
412
412
|
end
|
|
413
413
|
|
|
414
414
|
def _update_config(config)
|
|
415
415
|
body = Smplkit::Config::Helpers.build_config_request_body(config)
|
|
416
|
-
resp = http_put("/api/configs
|
|
416
|
+
resp = http_put("/api/v1/configs/#{config.key}", body)
|
|
417
417
|
Smplkit::Config::Helpers.config_from_json(self, resp["data"])
|
|
418
418
|
end
|
|
419
419
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
420
|
+
# Build the parent-chain for a given config, walking +parent_id+
|
|
421
|
+
# pointers across the full config list. Mirrors the Python SDK's
|
|
422
|
+
# client-side resolution — there is no server +/chain+ endpoint.
|
|
423
|
+
#
|
|
424
|
+
# Each chain entry is a Hash matching the wire shape that
|
|
425
|
+
# +Smplkit::Config::Helpers.resolve_chain+ consumes.
|
|
426
|
+
def fetch_chain(target_key)
|
|
427
|
+
all_configs = list
|
|
428
|
+
by_key = all_configs.to_h { |c| [c.key, c] }
|
|
429
|
+
by_id = all_configs.to_h { |c| [c.id, c] }
|
|
430
|
+
|
|
431
|
+
current = by_key[target_key]
|
|
432
|
+
return [] unless current
|
|
433
|
+
|
|
434
|
+
chain = []
|
|
435
|
+
loop do
|
|
436
|
+
chain << config_to_chain_entry(current)
|
|
437
|
+
parent_id = current.parent_id
|
|
438
|
+
break if parent_id.nil? || parent_id == ""
|
|
439
|
+
|
|
440
|
+
parent = by_id[parent_id]
|
|
441
|
+
break unless parent
|
|
442
|
+
|
|
443
|
+
current = parent
|
|
444
|
+
end
|
|
445
|
+
chain
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
private
|
|
449
|
+
|
|
450
|
+
def config_to_chain_entry(config)
|
|
451
|
+
items_hash = {}
|
|
452
|
+
config.items.each do |item|
|
|
453
|
+
items_hash[item.name] = {
|
|
454
|
+
"value" => item.value,
|
|
455
|
+
"type" => item.type,
|
|
456
|
+
"description" => item.description
|
|
457
|
+
}.compact
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
environments = config.environments.each_with_object({}) do |(env_key, env_obj), out|
|
|
461
|
+
out[env_key] = { "values" => env_obj.values_raw }
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
{ "id" => config.id, "items" => items_hash, "environments" => environments }
|
|
423
465
|
end
|
|
424
466
|
end
|
|
425
467
|
|
|
@@ -441,23 +483,23 @@ module Smplkit
|
|
|
441
483
|
return if batch.empty?
|
|
442
484
|
|
|
443
485
|
body = { "data" => { "type" => "flag_bulk_register", "attributes" => { "flags" => batch } } }
|
|
444
|
-
http_post("/api/flags/
|
|
486
|
+
http_post("/api/v1/flags/bulk", body)
|
|
445
487
|
rescue StandardError => e
|
|
446
488
|
Smplkit.debug("registration", "flag flush failed: #{e.class}: #{e.message}")
|
|
447
489
|
end
|
|
448
490
|
|
|
449
491
|
def list
|
|
450
|
-
list_resp = http_list("/api/flags
|
|
492
|
+
list_resp = http_list("/api/v1/flags")
|
|
451
493
|
list_resp.map { |r| flag_from_resource(r) }
|
|
452
494
|
end
|
|
453
495
|
|
|
454
496
|
def get(id)
|
|
455
|
-
resp = http_get("/api/flags
|
|
497
|
+
resp = http_get("/api/v1/flags/#{id}")
|
|
456
498
|
flag_from_resource(resp["data"])
|
|
457
499
|
end
|
|
458
500
|
|
|
459
501
|
def delete(id)
|
|
460
|
-
http_delete("/api/flags
|
|
502
|
+
http_delete("/api/v1/flags/#{id}")
|
|
461
503
|
end
|
|
462
504
|
|
|
463
505
|
def new_boolean_flag(id, default:, name: nil, description: nil, values: nil)
|
|
@@ -490,23 +532,23 @@ module Smplkit
|
|
|
490
532
|
|
|
491
533
|
def _create_flag(flag)
|
|
492
534
|
body = Smplkit::Flags::Helpers.build_flag_request_body(flag)
|
|
493
|
-
resp = http_post("/api/flags
|
|
535
|
+
resp = http_post("/api/v1/flags", body)
|
|
494
536
|
flag_from_resource(resp["data"])
|
|
495
537
|
end
|
|
496
538
|
|
|
497
539
|
def _update_flag(flag)
|
|
498
540
|
body = Smplkit::Flags::Helpers.build_flag_request_body(flag)
|
|
499
|
-
resp = http_put("/api/flags
|
|
541
|
+
resp = http_put("/api/v1/flags/#{flag.id}", body)
|
|
500
542
|
flag_from_resource(resp["data"])
|
|
501
543
|
end
|
|
502
544
|
|
|
503
545
|
def fetch_flag(id)
|
|
504
|
-
resp = http_get("/api/flags
|
|
546
|
+
resp = http_get("/api/v1/flags/#{id}")
|
|
505
547
|
Smplkit::Flags::Helpers.flag_dict_from_json(resp["data"])
|
|
506
548
|
end
|
|
507
549
|
|
|
508
550
|
def list_flags
|
|
509
|
-
body = http_list("/api/flags
|
|
551
|
+
body = http_list("/api/v1/flags")
|
|
510
552
|
body.map { |r| Smplkit::Flags::Helpers.flag_dict_from_json(r) }
|
|
511
553
|
end
|
|
512
554
|
|
|
@@ -550,30 +592,30 @@ module Smplkit
|
|
|
550
592
|
return if batch.empty?
|
|
551
593
|
|
|
552
594
|
body = { "data" => { "type" => "logger_bulk_register", "attributes" => { "loggers" => batch } } }
|
|
553
|
-
http_post("/api/loggers/
|
|
595
|
+
http_post("/api/v1/loggers/bulk", body)
|
|
554
596
|
rescue StandardError => e
|
|
555
597
|
Smplkit.debug("registration", "logger flush failed: #{e.class}: #{e.message}")
|
|
556
598
|
end
|
|
557
599
|
|
|
558
600
|
def list
|
|
559
|
-
list_resp = http_list("/api/loggers
|
|
601
|
+
list_resp = http_list("/api/v1/loggers")
|
|
560
602
|
list_resp.map { |r| Smplkit::Logging::Helpers.logger_resource_to_model(self, r) }
|
|
561
603
|
end
|
|
562
604
|
|
|
563
605
|
def get(id)
|
|
564
606
|
normalized = Smplkit::Logging::Normalize.normalize_logger_name(id)
|
|
565
|
-
resp = http_get("/api/loggers
|
|
607
|
+
resp = http_get("/api/v1/loggers/#{normalized}")
|
|
566
608
|
Smplkit::Logging::Helpers.logger_resource_to_model(self, resp["data"])
|
|
567
609
|
end
|
|
568
610
|
|
|
569
611
|
def delete(id)
|
|
570
612
|
normalized = Smplkit::Logging::Normalize.normalize_logger_name(id)
|
|
571
|
-
http_delete("/api/loggers
|
|
613
|
+
http_delete("/api/v1/loggers/#{normalized}")
|
|
572
614
|
end
|
|
573
615
|
|
|
574
616
|
def _update_logger(logger)
|
|
575
617
|
body = Smplkit::Logging::Helpers.build_logger_body(logger)
|
|
576
|
-
resp = http_put("/api/loggers
|
|
618
|
+
resp = http_put("/api/v1/loggers/#{logger.id || logger.name}", body)
|
|
577
619
|
Smplkit::Logging::Helpers.logger_resource_to_model(self, resp["data"])
|
|
578
620
|
end
|
|
579
621
|
end
|
|
@@ -586,17 +628,17 @@ module Smplkit
|
|
|
586
628
|
end
|
|
587
629
|
|
|
588
630
|
def list
|
|
589
|
-
list_resp = http_list("/api/log_groups
|
|
631
|
+
list_resp = http_list("/api/v1/log_groups")
|
|
590
632
|
list_resp.map { |r| Smplkit::Logging::Helpers.log_group_resource_to_model(self, r) }
|
|
591
633
|
end
|
|
592
634
|
|
|
593
635
|
def get(key)
|
|
594
|
-
resp = http_get("/api/log_groups
|
|
636
|
+
resp = http_get("/api/v1/log_groups/#{key}")
|
|
595
637
|
Smplkit::Logging::Helpers.log_group_resource_to_model(self, resp["data"])
|
|
596
638
|
end
|
|
597
639
|
|
|
598
640
|
def delete(key)
|
|
599
|
-
http_delete("/api/log_groups
|
|
641
|
+
http_delete("/api/v1/log_groups/#{key}")
|
|
600
642
|
end
|
|
601
643
|
|
|
602
644
|
def new_log_group(key, name: nil, level: nil, description: nil, parent: nil)
|
|
@@ -609,13 +651,13 @@ module Smplkit
|
|
|
609
651
|
|
|
610
652
|
def _create_log_group(group)
|
|
611
653
|
body = Smplkit::Logging::Helpers.build_log_group_body(group)
|
|
612
|
-
resp = http_post("/api/log_groups
|
|
654
|
+
resp = http_post("/api/v1/log_groups", body)
|
|
613
655
|
Smplkit::Logging::Helpers.log_group_resource_to_model(self, resp["data"])
|
|
614
656
|
end
|
|
615
657
|
|
|
616
658
|
def _update_log_group(group)
|
|
617
659
|
body = Smplkit::Logging::Helpers.build_log_group_body(group)
|
|
618
|
-
resp = http_put("/api/log_groups
|
|
660
|
+
resp = http_put("/api/v1/log_groups/#{group.key}", body)
|
|
619
661
|
Smplkit::Logging::Helpers.log_group_resource_to_model(self, resp["data"])
|
|
620
662
|
end
|
|
621
663
|
end
|
data/lib/smplkit/ws.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
require "async"
|
|
5
5
|
require "async/http/endpoint"
|
|
6
|
+
require "async/http/protocol/http1"
|
|
6
7
|
require "async/websocket/client"
|
|
7
8
|
require "concurrent"
|
|
8
9
|
|
|
@@ -172,7 +173,12 @@ module Smplkit
|
|
|
172
173
|
@connection_status = "connecting"
|
|
173
174
|
Smplkit.debug("websocket", "connecting to #{safe_url}")
|
|
174
175
|
|
|
175
|
-
|
|
176
|
+
# Force HTTP/1.1 for the WebSocket upgrade. async-http defaults to
|
|
177
|
+
# HTTP/2 on TLS endpoints, but the smplkit event gateway speaks the
|
|
178
|
+
# classic RFC 6455 upgrade over HTTP/1.1, not the HTTP/2-tunneled
|
|
179
|
+
# variant from RFC 8441 — without this override the upgrade returns
|
|
180
|
+
# a Protocol::HTTP2::StreamError before any frame is exchanged.
|
|
181
|
+
endpoint = Async::HTTP::Endpoint.parse(url, protocol: Async::HTTP::Protocol::HTTP1)
|
|
176
182
|
headers = { "user-agent" => USER_AGENT }
|
|
177
183
|
connection = Async::WebSocket::Client.connect(endpoint, headers: headers)
|
|
178
184
|
@connection_lock.synchronize { @connection = connection }
|