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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE +21 -0
- data/README.md +105 -0
- data/lib/smplkit/client.rb +218 -0
- data/lib/smplkit/config/client.rb +238 -0
- data/lib/smplkit/config/helpers.rb +108 -0
- data/lib/smplkit/config/models.rb +192 -0
- data/lib/smplkit/config_resolution.rb +202 -0
- data/lib/smplkit/context.rb +68 -0
- data/lib/smplkit/debug.rb +50 -0
- data/lib/smplkit/errors.rb +114 -0
- data/lib/smplkit/flags/client.rb +480 -0
- data/lib/smplkit/flags/helpers.rb +76 -0
- data/lib/smplkit/flags/models.rb +258 -0
- data/lib/smplkit/flags/types.rb +233 -0
- data/lib/smplkit/generators/install_generator.rb +42 -0
- data/lib/smplkit/helpers.rb +15 -0
- data/lib/smplkit/log_level.rb +57 -0
- data/lib/smplkit/logging/adapters/base.rb +63 -0
- data/lib/smplkit/logging/adapters/semantic_logger_adapter.rb +88 -0
- data/lib/smplkit/logging/adapters/stdlib_logger_adapter.rb +143 -0
- data/lib/smplkit/logging/client.rb +142 -0
- data/lib/smplkit/logging/helpers.rb +69 -0
- data/lib/smplkit/logging/levels.rb +86 -0
- data/lib/smplkit/logging/models.rb +124 -0
- data/lib/smplkit/logging/normalize.rb +16 -0
- data/lib/smplkit/logging/sources.rb +44 -0
- data/lib/smplkit/management/buffer.rb +111 -0
- data/lib/smplkit/management/client.rb +623 -0
- data/lib/smplkit/management/models.rb +133 -0
- data/lib/smplkit/management/types.rb +65 -0
- data/lib/smplkit/metrics.rb +78 -0
- data/lib/smplkit/railtie.rb +48 -0
- data/lib/smplkit/version.rb +5 -0
- data/lib/smplkit/ws.rb +92 -0
- data/lib/smplkit.rb +43 -0
- data/sig/smplkit.rbs +141 -0
- metadata +139 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Smplkit
|
|
4
|
+
module Management
|
|
5
|
+
# An environment resource — a customer-defined deploy target (production,
|
|
6
|
+
# staging, etc.) for which configs and flags can have overrides.
|
|
7
|
+
class Environment
|
|
8
|
+
attr_accessor :id, :key, :name, :color, :classification, :description, :created_at, :updated_at
|
|
9
|
+
|
|
10
|
+
def initialize(client = nil, key:, id: nil, name: nil, color: nil,
|
|
11
|
+
classification: EnvironmentClassification::STANDARD,
|
|
12
|
+
description: nil, created_at: nil, updated_at: nil)
|
|
13
|
+
@client = client
|
|
14
|
+
@id = id
|
|
15
|
+
@key = key
|
|
16
|
+
@name = name
|
|
17
|
+
@color = color
|
|
18
|
+
@classification = classification
|
|
19
|
+
@description = description
|
|
20
|
+
@created_at = created_at
|
|
21
|
+
@updated_at = updated_at
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def save
|
|
25
|
+
raise "Environment was constructed without a client; cannot save" if @client.nil?
|
|
26
|
+
|
|
27
|
+
updated =
|
|
28
|
+
if @created_at.nil?
|
|
29
|
+
@client._create_environment(self)
|
|
30
|
+
else
|
|
31
|
+
@client._update_environment(self)
|
|
32
|
+
end
|
|
33
|
+
_apply(updated)
|
|
34
|
+
self
|
|
35
|
+
end
|
|
36
|
+
alias save! save
|
|
37
|
+
|
|
38
|
+
def delete
|
|
39
|
+
raise "Environment was constructed without a client; cannot delete" if @client.nil?
|
|
40
|
+
|
|
41
|
+
@client.delete(@key)
|
|
42
|
+
end
|
|
43
|
+
alias delete! delete
|
|
44
|
+
|
|
45
|
+
def _apply(other)
|
|
46
|
+
@id = other.id
|
|
47
|
+
@key = other.key
|
|
48
|
+
@name = other.name
|
|
49
|
+
@color = other.color
|
|
50
|
+
@classification = other.classification
|
|
51
|
+
@description = other.description
|
|
52
|
+
@created_at = other.created_at
|
|
53
|
+
@updated_at = other.updated_at
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# A context type resource (e.g. "user", "account").
|
|
58
|
+
class ContextType
|
|
59
|
+
attr_accessor :id, :key, :name, :description, :created_at, :updated_at
|
|
60
|
+
|
|
61
|
+
def initialize(client = nil, key:, id: nil, name: nil, description: nil,
|
|
62
|
+
created_at: nil, updated_at: nil)
|
|
63
|
+
@client = client
|
|
64
|
+
@id = id
|
|
65
|
+
@key = key
|
|
66
|
+
@name = name
|
|
67
|
+
@description = description
|
|
68
|
+
@created_at = created_at
|
|
69
|
+
@updated_at = updated_at
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def save
|
|
73
|
+
raise "ContextType was constructed without a client; cannot save" if @client.nil?
|
|
74
|
+
|
|
75
|
+
updated =
|
|
76
|
+
if @created_at.nil?
|
|
77
|
+
@client._create_context_type(self)
|
|
78
|
+
else
|
|
79
|
+
@client._update_context_type(self)
|
|
80
|
+
end
|
|
81
|
+
_apply(updated)
|
|
82
|
+
self
|
|
83
|
+
end
|
|
84
|
+
alias save! save
|
|
85
|
+
|
|
86
|
+
def delete
|
|
87
|
+
raise "ContextType was constructed without a client; cannot delete" if @client.nil?
|
|
88
|
+
|
|
89
|
+
@client.delete(@key)
|
|
90
|
+
end
|
|
91
|
+
alias delete! delete
|
|
92
|
+
|
|
93
|
+
def _apply(other)
|
|
94
|
+
@id = other.id
|
|
95
|
+
@key = other.key
|
|
96
|
+
@name = other.name
|
|
97
|
+
@description = other.description
|
|
98
|
+
@created_at = other.created_at
|
|
99
|
+
@updated_at = other.updated_at
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# An account-wide settings resource.
|
|
104
|
+
class AccountSettings
|
|
105
|
+
attr_accessor :id, :environment_order, :default_environment, :updated_at
|
|
106
|
+
|
|
107
|
+
def initialize(client = nil, id: nil, environment_order: nil,
|
|
108
|
+
default_environment: nil, updated_at: nil)
|
|
109
|
+
@client = client
|
|
110
|
+
@id = id
|
|
111
|
+
@environment_order = environment_order || []
|
|
112
|
+
@default_environment = default_environment
|
|
113
|
+
@updated_at = updated_at
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def save
|
|
117
|
+
raise "AccountSettings was constructed without a client; cannot save" if @client.nil?
|
|
118
|
+
|
|
119
|
+
updated = @client._update_account_settings(self)
|
|
120
|
+
_apply(updated)
|
|
121
|
+
self
|
|
122
|
+
end
|
|
123
|
+
alias save! save
|
|
124
|
+
|
|
125
|
+
def _apply(other)
|
|
126
|
+
@id = other.id
|
|
127
|
+
@environment_order = other.environment_order
|
|
128
|
+
@default_environment = other.default_environment
|
|
129
|
+
@updated_at = other.updated_at
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Smplkit
|
|
4
|
+
module Management
|
|
5
|
+
# Whether an environment participates in the canonical ordering.
|
|
6
|
+
#
|
|
7
|
+
# +STANDARD+ environments are the customer's deploy targets — production,
|
|
8
|
+
# staging, development, etc.
|
|
9
|
+
# +AD_HOC+ environments are transient targets (preview branches, individual
|
|
10
|
+
# developer sandboxes) that should not appear in the standard ordering.
|
|
11
|
+
module EnvironmentClassification
|
|
12
|
+
STANDARD = "STANDARD"
|
|
13
|
+
AD_HOC = "AD_HOC"
|
|
14
|
+
|
|
15
|
+
ALL = [STANDARD, AD_HOC].freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
HEX_RE = /\A#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\z/
|
|
19
|
+
|
|
20
|
+
# A color, expressed as a CSS hex string.
|
|
21
|
+
#
|
|
22
|
+
# Smplkit::Color.new("#ef4444") # 6-digit hex
|
|
23
|
+
# Smplkit::Color.new("#fff") # 3-digit shorthand
|
|
24
|
+
# Smplkit::Color.new("#ef4444aa") # 8-digit with alpha
|
|
25
|
+
# Smplkit::Color.rgb(239, 68, 68) # RGB components
|
|
26
|
+
#
|
|
27
|
+
# Frozen — construct a fresh +Color+ to change a value.
|
|
28
|
+
class Color
|
|
29
|
+
attr_reader :hex
|
|
30
|
+
|
|
31
|
+
def initialize(hex)
|
|
32
|
+
raise TypeError, "Color hex must be a String, got #{hex.class}: #{hex.inspect}" unless hex.is_a?(String)
|
|
33
|
+
unless HEX_RE.match?(hex)
|
|
34
|
+
raise ArgumentError,
|
|
35
|
+
"Invalid color #{hex.inspect}: must be a CSS hex string like '#RGB', '#RRGGBB', or '#RRGGBBAA'"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
@hex = hex.downcase.freeze
|
|
39
|
+
freeze
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.rgb(red, green, blue)
|
|
43
|
+
[%w[red green blue], [red, green, blue]].transpose.each do |name, val|
|
|
44
|
+
unless val.is_a?(Integer) && !val.is_a?(TrueClass) && !val.is_a?(FalseClass)
|
|
45
|
+
raise TypeError,
|
|
46
|
+
"Color.rgb #{name} must be an Integer, got #{val.class}"
|
|
47
|
+
end
|
|
48
|
+
raise ArgumentError, "Color.rgb #{name} must be in range 0-255, got #{val.inspect}" unless val.between?(0,
|
|
49
|
+
255)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
new("##{red.to_s(16).rjust(2, "0")}#{green.to_s(16).rjust(2, "0")}#{blue.to_s(16).rjust(2, "0")}")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def to_s = @hex
|
|
56
|
+
def to_str = @hex
|
|
57
|
+
def ==(other) = other.is_a?(Color) && other.hex == @hex
|
|
58
|
+
alias eql? ==
|
|
59
|
+
def hash = @hex.hash
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
Color = Management::Color
|
|
64
|
+
EnvironmentClassification = Management::EnvironmentClassification
|
|
65
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "concurrent"
|
|
4
|
+
|
|
5
|
+
module Smplkit
|
|
6
|
+
# Periodically flushes accumulated SDK telemetry to the platform.
|
|
7
|
+
#
|
|
8
|
+
# Aggregation runs on a daemon thread. Public methods are thread-safe and
|
|
9
|
+
# block briefly only to enqueue metrics.
|
|
10
|
+
#
|
|
11
|
+
# When +telemetry+ is disabled in resolved config, +Smplkit::Client+ does
|
|
12
|
+
# not construct a +MetricsReporter+ and the +metrics+ accessor on
|
|
13
|
+
# sub-clients is +nil+. Sub-clients guard every call with +metrics&.+.
|
|
14
|
+
class MetricsReporter
|
|
15
|
+
DEFAULT_FLUSH_INTERVAL = 60.0
|
|
16
|
+
|
|
17
|
+
def initialize(http_client:, environment:, service:, flush_interval: DEFAULT_FLUSH_INTERVAL)
|
|
18
|
+
@http_client = http_client
|
|
19
|
+
@environment = environment
|
|
20
|
+
@service = service
|
|
21
|
+
@flush_interval = flush_interval
|
|
22
|
+
@counts = {}
|
|
23
|
+
@gauges = {}
|
|
24
|
+
@closed = Concurrent::AtomicBoolean.new(false)
|
|
25
|
+
@lock = Mutex.new
|
|
26
|
+
schedule_flush
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def record(metric, unit:, dimensions: nil)
|
|
30
|
+
return if @closed.true?
|
|
31
|
+
|
|
32
|
+
key = [metric, unit, dimensions || {}].freeze
|
|
33
|
+
@lock.synchronize { @counts[key] = (@counts[key] || 0) + 1 }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def record_gauge(metric, value, unit:, dimensions: nil)
|
|
37
|
+
return if @closed.true?
|
|
38
|
+
|
|
39
|
+
key = [metric, unit, dimensions || {}].freeze
|
|
40
|
+
@lock.synchronize { @gauges[key] = value }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def flush
|
|
44
|
+
counts_snap, gauges_snap = @lock.synchronize do
|
|
45
|
+
c = @counts.dup
|
|
46
|
+
g = @gauges.dup
|
|
47
|
+
@counts.clear
|
|
48
|
+
@gauges.clear
|
|
49
|
+
[c, g]
|
|
50
|
+
end
|
|
51
|
+
send_payload(counts_snap, gauges_snap) unless counts_snap.empty? && gauges_snap.empty?
|
|
52
|
+
rescue StandardError => e
|
|
53
|
+
Smplkit.debug("metrics", "flush failed: #{e.class}: #{e.message}")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def close
|
|
57
|
+
return if @closed.true?
|
|
58
|
+
|
|
59
|
+
@closed.make_true
|
|
60
|
+
@timer&.shutdown
|
|
61
|
+
flush
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def schedule_flush
|
|
67
|
+
@timer = Concurrent::TimerTask.new(execution_interval: @flush_interval) { flush }
|
|
68
|
+
@timer.execute
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def send_payload(_counts, _gauges)
|
|
72
|
+
# Telemetry payload submission is a no-op stub in the initial Ruby SDK
|
|
73
|
+
# release. The Python SDK posts to /api/metrics/v1; this hook is here so
|
|
74
|
+
# the same wiring can be filled in once the generated client surface is
|
|
75
|
+
# finalized.
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Smplkit
|
|
4
|
+
# Rails integration for the smplkit gem.
|
|
5
|
+
#
|
|
6
|
+
# The Railtie is loaded only when +Rails::Railtie+ is defined, so non-Rails
|
|
7
|
+
# customers pay zero load cost.
|
|
8
|
+
#
|
|
9
|
+
# Customers configure smplkit through +config.smplkit.*+ in
|
|
10
|
+
# +config/initializers/smplkit.rb+, generated by
|
|
11
|
+
# +rails generate smplkit:install+.
|
|
12
|
+
class Railtie < ::Rails::Railtie
|
|
13
|
+
config.smplkit = ActiveSupport::OrderedOptions.new
|
|
14
|
+
|
|
15
|
+
initializer "smplkit.configure" do |app|
|
|
16
|
+
Smplkit.configure_for_rails(app.config.smplkit)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
generators do
|
|
20
|
+
require_relative "generators/install_generator"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
# Construct the global +Smplkit.client+ from a Rails-friendly options
|
|
27
|
+
# struct. Falls back to +SMPLKIT_*+ env vars + +~/.smplkit+ resolution
|
|
28
|
+
# for any field the customer left blank.
|
|
29
|
+
def configure_for_rails(options)
|
|
30
|
+
client = Client.new(
|
|
31
|
+
api_key: options.api_key,
|
|
32
|
+
environment: options.environment || (defined?(::Rails) ? ::Rails.env : nil),
|
|
33
|
+
service: options.service,
|
|
34
|
+
profile: options.profile,
|
|
35
|
+
base_domain: options.base_domain,
|
|
36
|
+
scheme: options.scheme,
|
|
37
|
+
debug: options.debug,
|
|
38
|
+
telemetry: options.telemetry
|
|
39
|
+
)
|
|
40
|
+
@client = client
|
|
41
|
+
@context_provider = options.context_provider
|
|
42
|
+
client
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class << self
|
|
46
|
+
attr_accessor :client, :context_provider
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/smplkit/ws.rb
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "concurrent"
|
|
4
|
+
|
|
5
|
+
module Smplkit
|
|
6
|
+
# Manages a single WebSocket connection to the app service event gateway.
|
|
7
|
+
#
|
|
8
|
+
# A single +SharedWebSocket+ instance is shared across all product modules
|
|
9
|
+
# (config, flags) within one +Smplkit::Client+. Product modules register
|
|
10
|
+
# listeners for specific event types; the shared connection dispatches
|
|
11
|
+
# incoming events to the appropriate listeners.
|
|
12
|
+
#
|
|
13
|
+
# The connection runs on a dedicated SDK-owned thread; public methods are
|
|
14
|
+
# thread-safe and non-blocking.
|
|
15
|
+
#
|
|
16
|
+
# The app service gateway protocol:
|
|
17
|
+
# - Connect to +wss://app.<base_domain>/api/ws/v1/events?api_key={key}+
|
|
18
|
+
# - Receive +{"type": "connected"}+ on success
|
|
19
|
+
# - Receive events: +{"event": "config_changed", ...}+, etc.
|
|
20
|
+
# - No subscribe message - the API key determines the account
|
|
21
|
+
# - Heartbeat: server sends +"ping"+ (text), client responds with +"pong"+
|
|
22
|
+
#
|
|
23
|
+
# NOTE: The actual WebSocket I/O is wired to async-websocket on a worker
|
|
24
|
+
# thread. The initial Ruby SDK release defers full live-update wiring to a
|
|
25
|
+
# follow-up because async-websocket interactions need integration testing
|
|
26
|
+
# against the real platform.
|
|
27
|
+
class SharedWebSocket
|
|
28
|
+
BACKOFF_SCHEDULE = [1, 2, 4, 8, 16, 32, 60].freeze
|
|
29
|
+
|
|
30
|
+
def initialize(app_base_url:, api_key:, metrics: nil)
|
|
31
|
+
@app_base_url = app_base_url
|
|
32
|
+
@api_key = api_key
|
|
33
|
+
@metrics = metrics
|
|
34
|
+
@listeners = Concurrent::Hash.new { |h, k| h[k] = [] }
|
|
35
|
+
@listeners_lock = Mutex.new
|
|
36
|
+
@connection_status = "disconnected"
|
|
37
|
+
@closed = false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def on(event_name, &callback)
|
|
41
|
+
@listeners_lock.synchronize { @listeners[event_name] << callback }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def off(event_name, callback)
|
|
45
|
+
@listeners_lock.synchronize { @listeners[event_name].delete(callback) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def dispatch(event_name, data)
|
|
49
|
+
callbacks = @listeners_lock.synchronize { @listeners[event_name].dup }
|
|
50
|
+
callbacks.each do |cb|
|
|
51
|
+
cb.call(data)
|
|
52
|
+
rescue StandardError => e
|
|
53
|
+
Smplkit.debug("websocket", "listener for #{event_name} raised: #{e.class}: #{e.message}")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
attr_reader :connection_status
|
|
58
|
+
|
|
59
|
+
# Marked as connected for in-process testing without a real WS connection.
|
|
60
|
+
# Production wiring overrides this from the I/O thread once the gateway
|
|
61
|
+
# confirms the handshake.
|
|
62
|
+
def mark_connected!
|
|
63
|
+
@connection_status = "connected"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def start
|
|
67
|
+
Smplkit.debug("websocket", "starting shared WebSocket (Ruby SDK initial release: in-memory only)")
|
|
68
|
+
# Live wiring is deferred. Behave as if the handshake succeeded so the
|
|
69
|
+
# rest of the runtime can proceed - listeners still fire for any events
|
|
70
|
+
# other code dispatches into this instance.
|
|
71
|
+
mark_connected!
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def stop
|
|
75
|
+
@closed = true
|
|
76
|
+
@connection_status = "disconnected"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def build_ws_url
|
|
80
|
+
url = @app_base_url.dup
|
|
81
|
+
ws_url = if url.start_with?("https://")
|
|
82
|
+
"wss://#{url[("https://".length)..]}"
|
|
83
|
+
elsif url.start_with?("http://")
|
|
84
|
+
"ws://#{url[("http://".length)..]}"
|
|
85
|
+
else
|
|
86
|
+
"wss://#{url}"
|
|
87
|
+
end
|
|
88
|
+
ws_url = ws_url.chomp("/")
|
|
89
|
+
"#{ws_url}/api/ws/v1/events?api_key=#{@api_key}"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
data/lib/smplkit.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Official Ruby SDK for the smplkit platform.
|
|
4
|
+
#
|
|
5
|
+
# require "smplkit"
|
|
6
|
+
#
|
|
7
|
+
# client = Smplkit::Client.new(environment: "production", service: "my-svc")
|
|
8
|
+
# flag = client.flags.boolean_flag("checkout-v2", default: false)
|
|
9
|
+
# flag.get
|
|
10
|
+
module Smplkit
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
require_relative "smplkit/version"
|
|
14
|
+
require_relative "smplkit/errors"
|
|
15
|
+
require_relative "smplkit/debug"
|
|
16
|
+
require_relative "smplkit/helpers"
|
|
17
|
+
require_relative "smplkit/log_level"
|
|
18
|
+
require_relative "smplkit/context"
|
|
19
|
+
require_relative "smplkit/config_resolution"
|
|
20
|
+
require_relative "smplkit/metrics"
|
|
21
|
+
require_relative "smplkit/ws"
|
|
22
|
+
require_relative "smplkit/flags/types"
|
|
23
|
+
require_relative "smplkit/flags/models"
|
|
24
|
+
require_relative "smplkit/flags/helpers"
|
|
25
|
+
require_relative "smplkit/flags/client"
|
|
26
|
+
require_relative "smplkit/config/models"
|
|
27
|
+
require_relative "smplkit/config/helpers"
|
|
28
|
+
require_relative "smplkit/config/client"
|
|
29
|
+
require_relative "smplkit/logging/levels"
|
|
30
|
+
require_relative "smplkit/logging/normalize"
|
|
31
|
+
require_relative "smplkit/logging/sources"
|
|
32
|
+
require_relative "smplkit/logging/models"
|
|
33
|
+
require_relative "smplkit/logging/helpers"
|
|
34
|
+
require_relative "smplkit/logging/adapters/base"
|
|
35
|
+
require_relative "smplkit/logging/adapters/stdlib_logger_adapter"
|
|
36
|
+
require_relative "smplkit/logging/client"
|
|
37
|
+
require_relative "smplkit/management/types"
|
|
38
|
+
require_relative "smplkit/management/models"
|
|
39
|
+
require_relative "smplkit/management/buffer"
|
|
40
|
+
require_relative "smplkit/management/client"
|
|
41
|
+
require_relative "smplkit/client"
|
|
42
|
+
|
|
43
|
+
require_relative "smplkit/railtie" if defined?(Rails::Railtie)
|
data/sig/smplkit.rbs
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Top-level RBS signatures for the smplkit Ruby SDK.
|
|
2
|
+
#
|
|
3
|
+
# Per ADR-046 §2.8, sigs ship alongside the gem in +sig/+ for IDE/LSP
|
|
4
|
+
# autocompletion and Steep type-checking. Per-class sigs that go beyond
|
|
5
|
+
# the public surface declared here are deferred — see ISSUES.md.
|
|
6
|
+
|
|
7
|
+
module Smplkit
|
|
8
|
+
VERSION: String
|
|
9
|
+
|
|
10
|
+
def self.debug: (String, String) -> void
|
|
11
|
+
def self.enable_debug: () -> void
|
|
12
|
+
def self.set_request_context: (Array[Context]) -> ContextScope
|
|
13
|
+
def self.request_context: () -> Array[Context]
|
|
14
|
+
def self.configure_for_rails: (untyped) -> Client
|
|
15
|
+
|
|
16
|
+
class Client
|
|
17
|
+
def self.open: (**untyped) { (Client) -> void } -> void
|
|
18
|
+
def initialize: (?api_key: String?, ?environment: String?, ?service: String?,
|
|
19
|
+
?profile: String?, ?base_domain: String?, ?scheme: String?,
|
|
20
|
+
?debug: bool?, ?telemetry: bool?) -> void
|
|
21
|
+
def manage: () -> ManagementClient
|
|
22
|
+
def config: () -> Config::ConfigClient
|
|
23
|
+
def flags: () -> Flags::FlagsClient
|
|
24
|
+
def logging: () -> Logging::LoggingClient
|
|
25
|
+
def wait_until_ready: (?timeout: Float) -> void
|
|
26
|
+
def set_context: (Array[Context]) ?{ (ContextScope) -> void } -> ContextScope?
|
|
27
|
+
def close: () -> void
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class ManagementClient
|
|
31
|
+
def initialize: (?api_key: String?, ?base_domain: String?, ?scheme: String?,
|
|
32
|
+
?profile: String?, ?debug: bool?) -> void
|
|
33
|
+
def contexts: () -> ManagementClient::ContextsNamespace
|
|
34
|
+
def context_types: () -> ManagementClient::ContextTypesNamespace
|
|
35
|
+
def environments: () -> ManagementClient::EnvironmentsNamespace
|
|
36
|
+
def account_settings: () -> ManagementClient::AccountSettingsNamespace
|
|
37
|
+
def config: () -> ManagementClient::ConfigNamespace
|
|
38
|
+
def flags: () -> ManagementClient::FlagsNamespace
|
|
39
|
+
def loggers: () -> ManagementClient::LoggersNamespace
|
|
40
|
+
def log_groups: () -> ManagementClient::LogGroupsNamespace
|
|
41
|
+
def close: () -> void
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Context
|
|
45
|
+
attr_reader type: String
|
|
46
|
+
attr_reader key: String
|
|
47
|
+
attr_reader attributes: Hash[String, untyped]
|
|
48
|
+
attr_accessor name: String?
|
|
49
|
+
def initialize: (String, String, ?Hash[String | Symbol, untyped]?,
|
|
50
|
+
?name: String?, **untyped) -> void
|
|
51
|
+
def id: () -> String
|
|
52
|
+
def save: () -> Context
|
|
53
|
+
alias save! save
|
|
54
|
+
def delete: () -> void
|
|
55
|
+
alias delete! delete
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class ContextScope
|
|
59
|
+
def call: () { (ContextScope) -> void } -> void
|
|
60
|
+
def exit: () -> void
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
module Op
|
|
64
|
+
EQ: String
|
|
65
|
+
NEQ: String
|
|
66
|
+
LT: String
|
|
67
|
+
LTE: String
|
|
68
|
+
GT: String
|
|
69
|
+
GTE: String
|
|
70
|
+
IN: String
|
|
71
|
+
CONTAINS: String
|
|
72
|
+
ALL: Array[String]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class Rule
|
|
76
|
+
def initialize: (String, environment: String) -> void
|
|
77
|
+
def when: (*untyped) -> Rule
|
|
78
|
+
def serve: (untyped) -> Hash[String, untyped]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class FlagDeclaration
|
|
82
|
+
attr_reader id: String
|
|
83
|
+
attr_reader type: String
|
|
84
|
+
attr_reader default: untyped
|
|
85
|
+
attr_reader service: String?
|
|
86
|
+
attr_reader environment: String?
|
|
87
|
+
def initialize: (id: String, type: String, default: untyped,
|
|
88
|
+
?service: String?, ?environment: String?) -> void
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class LogLevel
|
|
92
|
+
include Comparable
|
|
93
|
+
attr_reader name: String
|
|
94
|
+
attr_reader ordinal: Integer
|
|
95
|
+
|
|
96
|
+
TRACE: LogLevel
|
|
97
|
+
DEBUG: LogLevel
|
|
98
|
+
INFO: LogLevel
|
|
99
|
+
WARN: LogLevel
|
|
100
|
+
ERROR: LogLevel
|
|
101
|
+
FATAL: LogLevel
|
|
102
|
+
SILENT: LogLevel
|
|
103
|
+
ALL: Array[LogLevel]
|
|
104
|
+
NAMES: Array[String]
|
|
105
|
+
|
|
106
|
+
def self.from_string: (String | Symbol) -> LogLevel
|
|
107
|
+
def self.coerce: (LogLevel | String) -> LogLevel
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class Error < StandardError
|
|
111
|
+
attr_reader errors: Array[ApiErrorDetail]
|
|
112
|
+
attr_reader status_code: Integer?
|
|
113
|
+
def initialize: (?String?, ?errors: Array[ApiErrorDetail]?, ?status_code: Integer?) -> void
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
class ConnectionError < Error
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
class TimeoutError < Error
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
class NotFoundError < Error
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
class ConflictError < Error
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
class ValidationError < Error
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class ApiErrorDetail
|
|
132
|
+
attr_reader status: String?
|
|
133
|
+
attr_reader title: String?
|
|
134
|
+
attr_reader detail: String?
|
|
135
|
+
attr_reader source: Hash[untyped, untyped]
|
|
136
|
+
def initialize: (?status: String?, ?title: String?, ?detail: String?,
|
|
137
|
+
?source: Hash[untyped, untyped]?) -> void
|
|
138
|
+
def to_h: () -> Hash[String, untyped]
|
|
139
|
+
def to_json: (*untyped) -> String
|
|
140
|
+
end
|
|
141
|
+
end
|