smplkit 1.0.5

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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE +21 -0
  4. data/README.md +105 -0
  5. data/lib/smplkit/client.rb +218 -0
  6. data/lib/smplkit/config/client.rb +238 -0
  7. data/lib/smplkit/config/helpers.rb +108 -0
  8. data/lib/smplkit/config/models.rb +192 -0
  9. data/lib/smplkit/config_resolution.rb +202 -0
  10. data/lib/smplkit/context.rb +68 -0
  11. data/lib/smplkit/debug.rb +50 -0
  12. data/lib/smplkit/errors.rb +114 -0
  13. data/lib/smplkit/flags/client.rb +480 -0
  14. data/lib/smplkit/flags/helpers.rb +76 -0
  15. data/lib/smplkit/flags/models.rb +258 -0
  16. data/lib/smplkit/flags/types.rb +233 -0
  17. data/lib/smplkit/generators/install_generator.rb +42 -0
  18. data/lib/smplkit/helpers.rb +15 -0
  19. data/lib/smplkit/log_level.rb +57 -0
  20. data/lib/smplkit/logging/adapters/base.rb +63 -0
  21. data/lib/smplkit/logging/adapters/semantic_logger_adapter.rb +88 -0
  22. data/lib/smplkit/logging/adapters/stdlib_logger_adapter.rb +143 -0
  23. data/lib/smplkit/logging/client.rb +142 -0
  24. data/lib/smplkit/logging/helpers.rb +69 -0
  25. data/lib/smplkit/logging/levels.rb +86 -0
  26. data/lib/smplkit/logging/models.rb +124 -0
  27. data/lib/smplkit/logging/normalize.rb +16 -0
  28. data/lib/smplkit/logging/sources.rb +44 -0
  29. data/lib/smplkit/management/buffer.rb +111 -0
  30. data/lib/smplkit/management/client.rb +623 -0
  31. data/lib/smplkit/management/models.rb +133 -0
  32. data/lib/smplkit/management/types.rb +65 -0
  33. data/lib/smplkit/metrics.rb +78 -0
  34. data/lib/smplkit/railtie.rb +48 -0
  35. data/lib/smplkit/version.rb +5 -0
  36. data/lib/smplkit/ws.rb +92 -0
  37. data/lib/smplkit.rb +43 -0
  38. data/sig/smplkit.rbs +141 -0
  39. metadata +139 -0
@@ -0,0 +1,623 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "json"
5
+ require "concurrent"
6
+
7
+ module Smplkit
8
+ # Top-level management client. Owns the HTTP transports + CRUD APIs for
9
+ # every resource on the smplkit platform.
10
+ #
11
+ # Sub-namespaces (mirroring the Python SDK):
12
+ #
13
+ # - +mgmt.contexts.*+
14
+ # - +mgmt.context_types.*+
15
+ # - +mgmt.environments.*+
16
+ # - +mgmt.account_settings.*+
17
+ # - +mgmt.config.*+
18
+ # - +mgmt.flags.*+
19
+ # - +mgmt.loggers.*+
20
+ # - +mgmt.log_groups.*+
21
+ #
22
+ # Constructable both as +Smplkit::ManagementClient.new+ (standalone) and as
23
+ # +Smplkit::Client#manage+ (shared transports).
24
+ class ManagementClient
25
+ attr_reader :contexts, :context_types, :environments, :account_settings,
26
+ :config, :flags, :loggers, :log_groups
27
+
28
+ def self.from_resolved(resolved)
29
+ new(_resolved: resolved)
30
+ end
31
+
32
+ def initialize(api_key: nil, base_domain: nil, scheme: nil, profile: nil,
33
+ debug: nil, _resolved: nil)
34
+ cfg = _resolved ||
35
+ ConfigResolution.resolve_management_config(
36
+ api_key: api_key, base_domain: base_domain, scheme: scheme,
37
+ profile: profile, debug: debug
38
+ )
39
+ Smplkit.enable_debug if cfg.debug
40
+
41
+ @resolved = cfg
42
+ @app_http = build_http(ConfigResolution.service_url(cfg.scheme, "app", cfg.base_domain), cfg.api_key)
43
+ @config_http = build_http(ConfigResolution.service_url(cfg.scheme, "config", cfg.base_domain), cfg.api_key)
44
+ @flags_http = build_http(ConfigResolution.service_url(cfg.scheme, "flags", cfg.base_domain), cfg.api_key)
45
+ @logging_http = build_http(ConfigResolution.service_url(cfg.scheme, "logging", cfg.base_domain), cfg.api_key)
46
+
47
+ @contexts = ContextsNamespace.new(@app_http)
48
+ @context_types = ContextTypesNamespace.new(@app_http)
49
+ @environments = EnvironmentsNamespace.new(@app_http)
50
+ @account_settings = AccountSettingsNamespace.new(@app_http)
51
+ @config = ConfigNamespace.new(@config_http)
52
+ @flags = FlagsNamespace.new(@flags_http)
53
+ @loggers = LoggersNamespace.new(@logging_http)
54
+ @log_groups = LogGroupsNamespace.new(@logging_http)
55
+ end
56
+
57
+ def close
58
+ [@app_http, @config_http, @flags_http, @logging_http].each do |conn|
59
+ conn.close if conn.respond_to?(:close)
60
+ end
61
+ end
62
+
63
+ def _resolved = @resolved
64
+ def _app_http = @app_http
65
+ def _config_http = @config_http
66
+ def _flags_http = @flags_http
67
+ def _logging_http = @logging_http
68
+
69
+ private
70
+
71
+ def build_http(base_url, api_key)
72
+ Faraday.new(url: base_url) do |f|
73
+ f.request :authorization, "Bearer", api_key
74
+ f.headers["Content-Type"] = "application/vnd.api+json"
75
+ f.headers["Accept"] = "application/vnd.api+json"
76
+ f.headers["User-Agent"] = "smplkit-ruby-sdk/#{Smplkit::VERSION}"
77
+ f.adapter Faraday.default_adapter
78
+ end
79
+ end
80
+
81
+ # ------------------------------------------------------------------
82
+ # Sub-namespaces
83
+ # ------------------------------------------------------------------
84
+
85
+ # Shared HTTP helpers used by every namespace below.
86
+ #
87
+ # All methods are prefixed +http_+ to avoid colliding with the public
88
+ # +get+ / +list+ accessors on each namespace.
89
+ module HttpHelpers
90
+ private
91
+
92
+ def http_get(path)
93
+ response = @http.get(path)
94
+ Errors.raise_for_status(response.status, response.body)
95
+ response.body.to_s.empty? ? {} : JSON.parse(response.body)
96
+ end
97
+
98
+ def http_list(path)
99
+ body = http_get(path)
100
+ body["data"] || []
101
+ end
102
+
103
+ def http_post(path, body)
104
+ response = @http.post(path) do |req|
105
+ req.body = body.is_a?(String) ? body : JSON.generate(body)
106
+ end
107
+ Errors.raise_for_status(response.status, response.body)
108
+ response.body.to_s.empty? ? {} : JSON.parse(response.body)
109
+ end
110
+
111
+ def http_put(path, body)
112
+ response = @http.put(path) do |req|
113
+ req.body = body.is_a?(String) ? body : JSON.generate(body)
114
+ end
115
+ Errors.raise_for_status(response.status, response.body)
116
+ response.body.to_s.empty? ? {} : JSON.parse(response.body)
117
+ end
118
+
119
+ def http_delete(path)
120
+ response = @http.delete(path)
121
+ Errors.raise_for_status(response.status, response.body)
122
+ true
123
+ end
124
+ end
125
+
126
+ class ContextsNamespace
127
+ include HttpHelpers
128
+
129
+ def initialize(http)
130
+ @http = http
131
+ @buffer = Management::ContextRegistrationBuffer.new
132
+ end
133
+
134
+ def register(contexts)
135
+ return if contexts.nil? || contexts.empty?
136
+
137
+ @buffer.observe(contexts)
138
+ flush if @buffer.pending_count >= Management::CONTEXT_BATCH_FLUSH_SIZE
139
+ end
140
+
141
+ def flush
142
+ batch = @buffer.drain
143
+ return if batch.empty?
144
+
145
+ body = { "data" => { "type" => "context_bulk_register", "attributes" => { "contexts" => batch } } }
146
+ http_post("/api/contexts/v1/bulk", body)
147
+ rescue StandardError => e
148
+ Smplkit.debug("registration", "context flush failed: #{e.class}: #{e.message}")
149
+ end
150
+
151
+ def list
152
+ list_resp = http_list("/api/contexts/v1")
153
+ list_resp.map { |r| context_from_resource(r) }
154
+ end
155
+
156
+ def get(id_or_type, key = nil)
157
+ type, ckey = split_id(id_or_type, key)
158
+ resource = http_get("/api/contexts/v1/#{type}:#{ckey}")
159
+ context_from_resource(resource["data"])
160
+ end
161
+
162
+ def delete(id_or_type, key = nil)
163
+ type, ckey = split_id(id_or_type, key)
164
+ http_delete("/api/contexts/v1/#{type}:#{ckey}")
165
+ end
166
+
167
+ def _save_context(ctx)
168
+ body = {
169
+ "data" => {
170
+ "type" => "context",
171
+ "id" => ctx.id,
172
+ "attributes" => { "type" => ctx.type, "key" => ctx.key, "attributes" => ctx.attributes }.compact
173
+ }
174
+ }
175
+ resp = http_put("/api/contexts/v1/#{ctx.id}", body)
176
+ context_from_resource(resp["data"]).tap { |c| c._bind_client(self) }
177
+ end
178
+
179
+ private
180
+
181
+ def split_id(id_or_type, key)
182
+ return [id_or_type, key] if key
183
+
184
+ unless id_or_type.include?(":")
185
+ raise ArgumentError, "context id must be 'type:key' (got #{id_or_type.inspect}); " \
186
+ "alternatively pass type and key as separate args"
187
+ end
188
+
189
+ id_or_type.split(":", 2)
190
+ end
191
+
192
+ def context_from_resource(resource)
193
+ attrs = resource["attributes"] || {}
194
+ Smplkit::Context.new(
195
+ attrs["type"] || resource["id"].to_s.split(":").first,
196
+ attrs["key"] || resource["id"].to_s.split(":", 2).last,
197
+ attrs["attributes"] || {},
198
+ name: attrs["name"],
199
+ created_at: attrs["created_at"],
200
+ updated_at: attrs["updated_at"]
201
+ )._bind_client(self)
202
+ end
203
+ end
204
+
205
+ class ContextTypesNamespace
206
+ include HttpHelpers
207
+
208
+ def initialize(http)
209
+ @http = http
210
+ end
211
+
212
+ def list
213
+ list_resp = http_list("/api/context_types/v1")
214
+ list_resp.map { |r| from_resource(r) }
215
+ end
216
+
217
+ def get(key)
218
+ resp = http_get("/api/context_types/v1/#{key}")
219
+ from_resource(resp["data"])
220
+ end
221
+
222
+ def delete(key)
223
+ http_delete("/api/context_types/v1/#{key}")
224
+ end
225
+
226
+ def new_context_type(key, name: nil, description: nil)
227
+ Management::ContextType.new(self, key: key, name: name, description: description)
228
+ end
229
+
230
+ def _create_context_type(ct)
231
+ resp = http_post("/api/context_types/v1", body_for(ct))
232
+ from_resource(resp["data"])
233
+ end
234
+
235
+ def _update_context_type(ct)
236
+ resp = http_put("/api/context_types/v1/#{ct.key}", body_for(ct))
237
+ from_resource(resp["data"])
238
+ end
239
+
240
+ private
241
+
242
+ def body_for(ct)
243
+ {
244
+ "data" => {
245
+ "type" => "context_type",
246
+ "id" => ct.key,
247
+ "attributes" => { "key" => ct.key, "name" => ct.name, "description" => ct.description }.compact
248
+ }
249
+ }
250
+ end
251
+
252
+ def from_resource(resource)
253
+ attrs = resource["attributes"] || {}
254
+ Management::ContextType.new(
255
+ self,
256
+ id: resource["id"], key: attrs["key"] || resource["id"],
257
+ name: attrs["name"], description: attrs["description"],
258
+ created_at: attrs["created_at"], updated_at: attrs["updated_at"]
259
+ )
260
+ end
261
+ end
262
+
263
+ class EnvironmentsNamespace
264
+ include HttpHelpers
265
+
266
+ def initialize(http)
267
+ @http = http
268
+ end
269
+
270
+ def list
271
+ list_resp = http_list("/api/environments/v1")
272
+ list_resp.map { |r| from_resource(r) }
273
+ end
274
+
275
+ def get(key)
276
+ resp = http_get("/api/environments/v1/#{key}")
277
+ from_resource(resp["data"])
278
+ end
279
+
280
+ def delete(key)
281
+ http_delete("/api/environments/v1/#{key}")
282
+ end
283
+
284
+ def new(key, name: nil, color: nil,
285
+ classification: Management::EnvironmentClassification::STANDARD,
286
+ description: nil)
287
+ color = Management::Color.new(color) if color.is_a?(String)
288
+ Management::Environment.new(
289
+ self,
290
+ key: key, name: name || Smplkit::Helpers.key_to_display_name(key),
291
+ color: color, classification: classification, description: description
292
+ )
293
+ end
294
+
295
+ def _create_environment(env)
296
+ resp = http_post("/api/environments/v1", body_for(env))
297
+ from_resource(resp["data"])
298
+ end
299
+
300
+ def _update_environment(env)
301
+ resp = http_put("/api/environments/v1/#{env.key}", body_for(env))
302
+ from_resource(resp["data"])
303
+ end
304
+
305
+ private
306
+
307
+ def body_for(env)
308
+ {
309
+ "data" => {
310
+ "type" => "environment",
311
+ "id" => env.key,
312
+ "attributes" => {
313
+ "key" => env.key,
314
+ "name" => env.name,
315
+ "color" => env.color&.hex,
316
+ "classification" => env.classification,
317
+ "description" => env.description
318
+ }.compact
319
+ }
320
+ }
321
+ end
322
+
323
+ def from_resource(resource)
324
+ attrs = resource["attributes"] || {}
325
+ color = attrs["color"] && Management::Color.new(attrs["color"])
326
+ Management::Environment.new(
327
+ self,
328
+ id: resource["id"], key: attrs["key"] || resource["id"],
329
+ name: attrs["name"], color: color,
330
+ classification: attrs["classification"] || Management::EnvironmentClassification::STANDARD,
331
+ description: attrs["description"],
332
+ created_at: attrs["created_at"], updated_at: attrs["updated_at"]
333
+ )
334
+ end
335
+ end
336
+
337
+ class AccountSettingsNamespace
338
+ include HttpHelpers
339
+
340
+ def initialize(http)
341
+ @http = http
342
+ end
343
+
344
+ def get
345
+ resp = http_get("/api/account_settings/v1")
346
+ from_resource(resp["data"])
347
+ end
348
+
349
+ def _update_account_settings(settings)
350
+ resp = http_put("/api/account_settings/v1", body_for(settings))
351
+ from_resource(resp["data"])
352
+ end
353
+
354
+ private
355
+
356
+ def body_for(settings)
357
+ {
358
+ "data" => {
359
+ "type" => "account_settings",
360
+ "attributes" => {
361
+ "environment_order" => settings.environment_order,
362
+ "default_environment" => settings.default_environment
363
+ }.compact
364
+ }
365
+ }
366
+ end
367
+
368
+ def from_resource(resource)
369
+ attrs = resource["attributes"] || {}
370
+ Management::AccountSettings.new(
371
+ self,
372
+ id: resource["id"],
373
+ environment_order: attrs["environment_order"] || [],
374
+ default_environment: attrs["default_environment"],
375
+ updated_at: attrs["updated_at"]
376
+ )
377
+ end
378
+ end
379
+
380
+ class ConfigNamespace
381
+ include HttpHelpers
382
+
383
+ def initialize(http)
384
+ @http = http
385
+ end
386
+
387
+ def list
388
+ list_resp = http_list("/api/configs/v1")
389
+ list_resp.map { |r| Smplkit::Config::Helpers.config_from_json(self, r) }
390
+ end
391
+
392
+ def get(key)
393
+ resp = http_get("/api/configs/v1/#{key}")
394
+ Smplkit::Config::Helpers.config_from_json(self, resp["data"])
395
+ end
396
+
397
+ def delete(key)
398
+ http_delete("/api/configs/v1/#{key}")
399
+ end
400
+
401
+ def new_config(key, name: nil, description: nil, parent: nil)
402
+ Smplkit::Config::Config.new(
403
+ self, key: key, name: name, description: description,
404
+ parent_id: parent.is_a?(Smplkit::Config::Config) ? parent.key : parent
405
+ )
406
+ end
407
+
408
+ def _create_config(config)
409
+ body = Smplkit::Config::Helpers.build_config_request_body(config)
410
+ resp = http_post("/api/configs/v1", body)
411
+ Smplkit::Config::Helpers.config_from_json(self, resp["data"])
412
+ end
413
+
414
+ def _update_config(config)
415
+ body = Smplkit::Config::Helpers.build_config_request_body(config)
416
+ resp = http_put("/api/configs/v1/#{config.key}", body)
417
+ Smplkit::Config::Helpers.config_from_json(self, resp["data"])
418
+ end
419
+
420
+ def fetch_chain(key)
421
+ resp = http_get("/api/configs/v1/#{key}/chain")
422
+ (resp["data"] || []).map { |r| r["attributes"] || {} }
423
+ end
424
+ end
425
+
426
+ class FlagsNamespace
427
+ include HttpHelpers
428
+
429
+ def initialize(http)
430
+ @http = http
431
+ @buffer = Management::FlagRegistrationBuffer.new
432
+ end
433
+
434
+ def register(declaration)
435
+ @buffer.add(declaration)
436
+ flush if @buffer.pending_count >= Management::FLAG_BATCH_FLUSH_SIZE
437
+ end
438
+
439
+ def flush
440
+ batch = @buffer.drain
441
+ return if batch.empty?
442
+
443
+ body = { "data" => { "type" => "flag_bulk_register", "attributes" => { "flags" => batch } } }
444
+ http_post("/api/flags/v1/bulk", body)
445
+ rescue StandardError => e
446
+ Smplkit.debug("registration", "flag flush failed: #{e.class}: #{e.message}")
447
+ end
448
+
449
+ def list
450
+ list_resp = http_list("/api/flags/v1")
451
+ list_resp.map { |r| flag_from_resource(r) }
452
+ end
453
+
454
+ def get(id)
455
+ resp = http_get("/api/flags/v1/#{id}")
456
+ flag_from_resource(resp["data"])
457
+ end
458
+
459
+ def delete(id)
460
+ http_delete("/api/flags/v1/#{id}")
461
+ end
462
+
463
+ def new_boolean_flag(id, default:, name: nil, description: nil, values: nil)
464
+ Smplkit::Flags::BooleanFlag.new(
465
+ self, id: id, name: name || id, type: "BOOLEAN", default: default,
466
+ description: description, values: values
467
+ )
468
+ end
469
+
470
+ def new_string_flag(id, default:, name: nil, description: nil, values: nil)
471
+ Smplkit::Flags::StringFlag.new(
472
+ self, id: id, name: name || id, type: "STRING", default: default,
473
+ description: description, values: values
474
+ )
475
+ end
476
+
477
+ def new_number_flag(id, default:, name: nil, description: nil, values: nil)
478
+ Smplkit::Flags::NumberFlag.new(
479
+ self, id: id, name: name || id, type: "NUMERIC", default: default,
480
+ description: description, values: values
481
+ )
482
+ end
483
+
484
+ def new_json_flag(id, default:, name: nil, description: nil, values: nil)
485
+ Smplkit::Flags::JsonFlag.new(
486
+ self, id: id, name: name || id, type: "JSON", default: default,
487
+ description: description, values: values
488
+ )
489
+ end
490
+
491
+ def _create_flag(flag)
492
+ body = Smplkit::Flags::Helpers.build_flag_request_body(flag)
493
+ resp = http_post("/api/flags/v1", body)
494
+ flag_from_resource(resp["data"])
495
+ end
496
+
497
+ def _update_flag(flag)
498
+ body = Smplkit::Flags::Helpers.build_flag_request_body(flag)
499
+ resp = http_put("/api/flags/v1/#{flag.id}", body)
500
+ flag_from_resource(resp["data"])
501
+ end
502
+
503
+ def fetch_flag(id)
504
+ resp = http_get("/api/flags/v1/#{id}")
505
+ Smplkit::Flags::Helpers.flag_dict_from_json(resp["data"])
506
+ end
507
+
508
+ def list_flags
509
+ body = http_list("/api/flags/v1")
510
+ body.map { |r| Smplkit::Flags::Helpers.flag_dict_from_json(r) }
511
+ end
512
+
513
+ private
514
+
515
+ def flag_from_resource(resource)
516
+ d = Smplkit::Flags::Helpers.flag_dict_from_json(resource)
517
+ klass =
518
+ case d["type"]
519
+ when "BOOLEAN" then Smplkit::Flags::BooleanFlag
520
+ when "STRING" then Smplkit::Flags::StringFlag
521
+ when "NUMERIC" then Smplkit::Flags::NumberFlag
522
+ else Smplkit::Flags::JsonFlag
523
+ end
524
+ klass.new(
525
+ self,
526
+ id: d["id"], name: d["name"], type: d["type"], default: d["default"],
527
+ description: d["description"], values: d["values"], environments: d["environments"],
528
+ created_at: (resource["attributes"] || {})["created_at"],
529
+ updated_at: (resource["attributes"] || {})["updated_at"]
530
+ )
531
+ end
532
+ end
533
+
534
+ class LoggersNamespace
535
+ include HttpHelpers
536
+
537
+ def initialize(http)
538
+ @http = http
539
+ @buffer = Management::LoggerRegistrationBuffer.new
540
+ end
541
+
542
+ def register(source)
543
+ sources = source.is_a?(Array) ? source : [source]
544
+ sources.each { |s| @buffer.add(s) }
545
+ flush if @buffer.pending_count >= Management::LOGGER_BATCH_FLUSH_SIZE
546
+ end
547
+
548
+ def flush
549
+ batch = @buffer.drain
550
+ return if batch.empty?
551
+
552
+ body = { "data" => { "type" => "logger_bulk_register", "attributes" => { "loggers" => batch } } }
553
+ http_post("/api/loggers/v1/bulk", body)
554
+ rescue StandardError => e
555
+ Smplkit.debug("registration", "logger flush failed: #{e.class}: #{e.message}")
556
+ end
557
+
558
+ def list
559
+ list_resp = http_list("/api/loggers/v1")
560
+ list_resp.map { |r| Smplkit::Logging::Helpers.logger_resource_to_model(self, r) }
561
+ end
562
+
563
+ def get(id)
564
+ normalized = Smplkit::Logging::Normalize.normalize_logger_name(id)
565
+ resp = http_get("/api/loggers/v1/#{normalized}")
566
+ Smplkit::Logging::Helpers.logger_resource_to_model(self, resp["data"])
567
+ end
568
+
569
+ def delete(id)
570
+ normalized = Smplkit::Logging::Normalize.normalize_logger_name(id)
571
+ http_delete("/api/loggers/v1/#{normalized}")
572
+ end
573
+
574
+ def _update_logger(logger)
575
+ body = Smplkit::Logging::Helpers.build_logger_body(logger)
576
+ resp = http_put("/api/loggers/v1/#{logger.id || logger.name}", body)
577
+ Smplkit::Logging::Helpers.logger_resource_to_model(self, resp["data"])
578
+ end
579
+ end
580
+
581
+ class LogGroupsNamespace
582
+ include HttpHelpers
583
+
584
+ def initialize(http)
585
+ @http = http
586
+ end
587
+
588
+ def list
589
+ list_resp = http_list("/api/log_groups/v1")
590
+ list_resp.map { |r| Smplkit::Logging::Helpers.log_group_resource_to_model(self, r) }
591
+ end
592
+
593
+ def get(key)
594
+ resp = http_get("/api/log_groups/v1/#{key}")
595
+ Smplkit::Logging::Helpers.log_group_resource_to_model(self, resp["data"])
596
+ end
597
+
598
+ def delete(key)
599
+ http_delete("/api/log_groups/v1/#{key}")
600
+ end
601
+
602
+ def new_log_group(key, name: nil, level: nil, description: nil, parent: nil)
603
+ Smplkit::Logging::SmplLogGroup.new(
604
+ self, key: key, name: name || Smplkit::Helpers.key_to_display_name(key),
605
+ level: level && Smplkit::LogLevel.coerce(level), description: description,
606
+ parent_id: parent.is_a?(Smplkit::Logging::SmplLogGroup) ? parent.key : parent
607
+ )
608
+ end
609
+
610
+ def _create_log_group(group)
611
+ body = Smplkit::Logging::Helpers.build_log_group_body(group)
612
+ resp = http_post("/api/log_groups/v1", body)
613
+ Smplkit::Logging::Helpers.log_group_resource_to_model(self, resp["data"])
614
+ end
615
+
616
+ def _update_log_group(group)
617
+ body = Smplkit::Logging::Helpers.build_log_group_body(group)
618
+ resp = http_put("/api/log_groups/v1/#{group.key}", body)
619
+ Smplkit::Logging::Helpers.log_group_resource_to_model(self, resp["data"])
620
+ end
621
+ end
622
+ end
623
+ end