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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45b89bb8091aeb0e42f1e6ccbf0b94cde98f827bae408770317658dd24c4de3c
4
- data.tar.gz: 06eff16b91b28ef98f9fc2b7a64253d099154a6730a7ecbf1f2e99df9d8629c3
3
+ metadata.gz: 33719678260c3b3cd9ae01b3a7d58226363cc292d93130600622df9ba4d259ec
4
+ data.tar.gz: 6c5a7f1f074f2b3f87266929598722da16cf44209057ccb147cff4bb84cb2e90
5
5
  SHA512:
6
- metadata.gz: 8f06ccbadf581ad0f7253ebb80ca35ecc6f1dcd94c63ad476bbfd742068702774f9b2dc416da6d6f035fd50502cebea17bfae6d8a203f4d0f4c39dec495c30ef
7
- data.tar.gz: 6ac31365d9a694b9b11e0a8b00e7ca37c3a8fa5354fe678ef91fdc7bcd7a3ad257220b5521a4704ebdb210838c3e4ad120afe5dc4af01686f7339538510b0024
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
- Custom adapters subclass `Smplkit::Logging::Adapters::Base`.
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
- parse_ini(File.read(path))
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/v1/bulk", body)
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/v1")
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/v1/#{type}:#{ckey}")
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/v1/#{type}:#{ckey}")
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/v1/#{ctx.id}", body)
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/v1")
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/v1/#{key}")
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/v1/#{key}")
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/v1", body_for(ct))
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/v1/#{ct.key}", body_for(ct))
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/v1")
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/v1/#{key}")
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/v1/#{key}")
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/v1", body_for(env))
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/v1/#{env.key}", body_for(env))
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/account_settings/v1")
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/account_settings/v1", body_for(settings))
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/v1")
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/v1/#{key}")
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/v1/#{key}")
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/v1", body)
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/v1/#{config.key}", body)
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
- def fetch_chain(key)
421
- resp = http_get("/api/configs/v1/#{key}/chain")
422
- (resp["data"] || []).map { |r| r["attributes"] || {} }
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/v1/bulk", body)
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/v1")
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/v1/#{id}")
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/v1/#{id}")
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/v1", body)
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/v1/#{flag.id}", body)
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/v1/#{id}")
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/v1")
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/v1/bulk", body)
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/v1")
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/v1/#{normalized}")
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/v1/#{normalized}")
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/v1/#{logger.id || logger.name}", body)
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/v1")
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/v1/#{key}")
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/v1/#{key}")
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/v1", body)
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/v1/#{group.key}", body)
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
- endpoint = Async::HTTP::Endpoint.parse(url)
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 }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smplkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.11
4
+ version: 1.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Smpl Solutions LLC