smplkit 1.0.17 → 1.0.19
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/buffer.rb +158 -0
- data/lib/smplkit/audit/client.rb +29 -0
- data/lib/smplkit/audit/events.rb +145 -0
- data/lib/smplkit/client.rb +7 -2
- data/lib/smplkit.rb +6 -2
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 30387f75831a98eef6f343ab2c4d8971826f8d01d3e90331403f99cb53d7797e
|
|
4
|
+
data.tar.gz: a03b4de90c32c53e84b9c902af5b45c89317b8b064629b16bf366d4a8e40c0c7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d4632f78bcfb874dbf478a8dfd3030c732e782ba0099810931bedac53e36aadcf62628e7ff7dc4e1f264f06e797c8fde837116731b237466a1bcf6f70604cd68
|
|
7
|
+
data.tar.gz: 5ab26d7a65e5849975ac3bf3c64166e3cb455dd0e59f7c593124d4e44b2127cea55488b9499f97cf2e1822296442509916ab3193701adf69db7d567fba8c6c58
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Smplkit
|
|
4
|
+
module Audit
|
|
5
|
+
# Bounded in-memory queue + worker thread for fire-and-forget audit
|
|
6
|
+
# emits (ADR-047 §2.6).
|
|
7
|
+
#
|
|
8
|
+
# +#enqueue+ returns immediately. The worker drains on either a
|
|
9
|
+
# periodic tick or once depth crosses the high-water mark, retries
|
|
10
|
+
# transient failures with exponential backoff, drops permanent 4xx
|
|
11
|
+
# (other than 429), and evicts the oldest item under sustained
|
|
12
|
+
# back-pressure.
|
|
13
|
+
class EventBuffer
|
|
14
|
+
MAX_BUFFER_SIZE = 1000
|
|
15
|
+
WATERMARK = 50
|
|
16
|
+
FLUSH_INTERVAL = 5.0
|
|
17
|
+
MAX_ATTEMPTS = 5
|
|
18
|
+
INITIAL_BACKOFF = 0.25
|
|
19
|
+
MAX_BACKOFF = 8.0
|
|
20
|
+
|
|
21
|
+
def initialize(api)
|
|
22
|
+
@api = api
|
|
23
|
+
@queue = []
|
|
24
|
+
@mutex = Mutex.new
|
|
25
|
+
@cond = ConditionVariable.new
|
|
26
|
+
@closed = false
|
|
27
|
+
@dropped_count = 0
|
|
28
|
+
@worker = Thread.new { run }
|
|
29
|
+
@worker.report_on_exception = false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def enqueue(body, idempotency_key)
|
|
33
|
+
depth = nil
|
|
34
|
+
@mutex.synchronize do
|
|
35
|
+
return if @closed
|
|
36
|
+
|
|
37
|
+
if @queue.size >= MAX_BUFFER_SIZE
|
|
38
|
+
@queue.shift
|
|
39
|
+
@dropped_count += 1
|
|
40
|
+
warn "[smplkit.audit] buffer full (size=#{MAX_BUFFER_SIZE}); " \
|
|
41
|
+
"dropped oldest event (total dropped=#{@dropped_count})"
|
|
42
|
+
end
|
|
43
|
+
@queue.push(PendingEvent.new(body, idempotency_key))
|
|
44
|
+
depth = @queue.size
|
|
45
|
+
@cond.signal if depth >= WATERMARK
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def flush(timeout: 5.0)
|
|
50
|
+
deadline = monotonic_now + timeout
|
|
51
|
+
loop do
|
|
52
|
+
empty = @mutex.synchronize { @queue.empty? }
|
|
53
|
+
return if empty
|
|
54
|
+
|
|
55
|
+
if monotonic_now >= deadline
|
|
56
|
+
warn "[smplkit.audit] flush timed out (timeout=#{timeout}s)"
|
|
57
|
+
return
|
|
58
|
+
end
|
|
59
|
+
@mutex.synchronize { @cond.signal }
|
|
60
|
+
sleep 0.05
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def close(timeout: 5.0)
|
|
65
|
+
flush(timeout: timeout)
|
|
66
|
+
@mutex.synchronize do
|
|
67
|
+
@closed = true
|
|
68
|
+
@cond.broadcast
|
|
69
|
+
end
|
|
70
|
+
@worker.join(timeout)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def run
|
|
76
|
+
loop do
|
|
77
|
+
drain_once
|
|
78
|
+
should_exit = false
|
|
79
|
+
sleep_for = FLUSH_INTERVAL
|
|
80
|
+
@mutex.synchronize do
|
|
81
|
+
should_exit = @closed && @queue.empty?
|
|
82
|
+
unless @queue.empty?
|
|
83
|
+
head = @queue.first
|
|
84
|
+
if head.next_retry_at
|
|
85
|
+
until_next = head.next_retry_at - monotonic_now
|
|
86
|
+
sleep_for = until_next if until_next.positive? && until_next < sleep_for
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
@cond.wait(@mutex, sleep_for) unless should_exit
|
|
90
|
+
end
|
|
91
|
+
break if should_exit
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def drain_once
|
|
96
|
+
loop do
|
|
97
|
+
item = nil
|
|
98
|
+
@mutex.synchronize do
|
|
99
|
+
return if @queue.empty?
|
|
100
|
+
|
|
101
|
+
head = @queue.first
|
|
102
|
+
return if head.next_retry_at && head.next_retry_at > monotonic_now
|
|
103
|
+
|
|
104
|
+
item = @queue.shift
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
status = 0
|
|
108
|
+
begin
|
|
109
|
+
opts = item.idempotency_key ? { idempotency_key: item.idempotency_key } : {}
|
|
110
|
+
@api.create_event(item.body, opts)
|
|
111
|
+
status = 201
|
|
112
|
+
rescue SmplkitGeneratedClient::Audit::ApiError => e
|
|
113
|
+
status = e.code || 0
|
|
114
|
+
rescue StandardError
|
|
115
|
+
status = 0
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
requeue = handle_outcome(item, status)
|
|
119
|
+
if requeue
|
|
120
|
+
@mutex.synchronize { @queue.unshift(requeue) }
|
|
121
|
+
return
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def handle_outcome(item, status)
|
|
127
|
+
return nil if status >= 200 && status < 300
|
|
128
|
+
|
|
129
|
+
if status >= 400 && status < 500 && status != 429
|
|
130
|
+
warn "[smplkit.audit] permanent failure status=#{status}; event dropped"
|
|
131
|
+
return nil
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
item.attempts += 1
|
|
135
|
+
if item.attempts >= MAX_ATTEMPTS
|
|
136
|
+
warn "[smplkit.audit] gave up after #{item.attempts} attempts (status=#{status})"
|
|
137
|
+
return nil
|
|
138
|
+
end
|
|
139
|
+
backoff = [MAX_BACKOFF, INITIAL_BACKOFF * (2**(item.attempts - 1))].min
|
|
140
|
+
jitter = rand(0.0..(backoff * 0.25))
|
|
141
|
+
item.next_retry_at = monotonic_now + backoff + jitter
|
|
142
|
+
item
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def monotonic_now
|
|
146
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Holds an outbound event between attempts.
|
|
150
|
+
PendingEvent = Struct.new(:body, :idempotency_key, :attempts, :next_retry_at) do
|
|
151
|
+
def initialize(body, idempotency_key, attempts = 0, next_retry_at = nil)
|
|
152
|
+
super
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
private_constant :PendingEvent
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Smplkit
|
|
4
|
+
module Audit
|
|
5
|
+
# Audit-product entry point — accessed via +client.audit+.
|
|
6
|
+
#
|
|
7
|
+
# Today the audit namespace is exclusively +#events+; future
|
|
8
|
+
# iterations may add SIEM exports as additional sub-clients
|
|
9
|
+
# (ADR-047 §2.7 lists SIEM streaming as a Pro-tier capability).
|
|
10
|
+
class AuditClient
|
|
11
|
+
attr_reader :events
|
|
12
|
+
|
|
13
|
+
def initialize(api_key:, base_url:, timeout: 10.0)
|
|
14
|
+
cfg = SmplkitGeneratedClient::Audit::Configuration.new
|
|
15
|
+
cfg.host = URI.parse(base_url).host
|
|
16
|
+
cfg.scheme = URI.parse(base_url).scheme
|
|
17
|
+
cfg.access_token = api_key
|
|
18
|
+
cfg.timeout = timeout
|
|
19
|
+
api_client = SmplkitGeneratedClient::Audit::ApiClient.new(cfg)
|
|
20
|
+
api = SmplkitGeneratedClient::Audit::DefaultApi.new(api_client)
|
|
21
|
+
@events = Events.new(api)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def _close
|
|
25
|
+
@events._close
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Smplkit
|
|
4
|
+
module Audit
|
|
5
|
+
# Audit events surface — accessed via +client.audit.events+.
|
|
6
|
+
#
|
|
7
|
+
# +#create+ is fire-and-forget per ADR-047 §2.6 — the call enqueues
|
|
8
|
+
# the event onto an in-memory bounded buffer and returns
|
|
9
|
+
# immediately. +#list+ and +#get+ are synchronous.
|
|
10
|
+
class Events
|
|
11
|
+
def initialize(api)
|
|
12
|
+
@api = api
|
|
13
|
+
@buffer = EventBuffer.new(api)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Enqueue an audit event for asynchronous delivery.
|
|
17
|
+
#
|
|
18
|
+
# Customer attempts to record events with +resource_type+ starting
|
|
19
|
+
# with +smpl.+ are rejected by the server with a 403 (the buffer
|
|
20
|
+
# logs and drops permanent failures).
|
|
21
|
+
def create(action:, resource_type:, resource_id:,
|
|
22
|
+
occurred_at: nil, snapshot: nil, data: nil, idempotency_key: nil)
|
|
23
|
+
raise ArgumentError, "action is required" if action.nil? || action.to_s.empty?
|
|
24
|
+
raise ArgumentError, "resource_type is required" if resource_type.nil? || resource_type.to_s.empty?
|
|
25
|
+
raise ArgumentError, "resource_id is required" if resource_id.nil? || resource_id.to_s.empty?
|
|
26
|
+
|
|
27
|
+
# Pydantic validation on the server expects an ISO-8601 string
|
|
28
|
+
# for ``occurred_at`` — Ruby's ``Time#to_s`` (which is what the
|
|
29
|
+
# generated client falls back to during JSON serialization)
|
|
30
|
+
# emits ``"2026-05-07 04:43:23 UTC"`` and trips the gate. Coerce
|
|
31
|
+
# Time/DateTime to ``.iso8601`` here so end users can pass the
|
|
32
|
+
# native Ruby type.
|
|
33
|
+
normalized_occurred_at = if occurred_at.respond_to?(:iso8601)
|
|
34
|
+
occurred_at.iso8601
|
|
35
|
+
else
|
|
36
|
+
occurred_at
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Server-side validation also rejects ``data: null`` (the field
|
|
40
|
+
# is required-non-null in the OpenAPI schema). Always default to
|
|
41
|
+
# an empty hash so users who omit ``data:`` don't trip the gate.
|
|
42
|
+
attrs = SmplkitGeneratedClient::Audit::Event.new(
|
|
43
|
+
action: action,
|
|
44
|
+
resource_type: resource_type,
|
|
45
|
+
resource_id: resource_id,
|
|
46
|
+
occurred_at: normalized_occurred_at,
|
|
47
|
+
snapshot: snapshot,
|
|
48
|
+
data: data || {}
|
|
49
|
+
)
|
|
50
|
+
resource = SmplkitGeneratedClient::Audit::EventResource.new(
|
|
51
|
+
id: "",
|
|
52
|
+
type: "event",
|
|
53
|
+
attributes: attrs
|
|
54
|
+
)
|
|
55
|
+
body = SmplkitGeneratedClient::Audit::EventResponse.new(data: resource)
|
|
56
|
+
@buffer.enqueue(body, idempotency_key)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Single-event retrieval.
|
|
60
|
+
#
|
|
61
|
+
# Raises +SmplkitGeneratedClient::Audit::ApiError+ on non-2xx
|
|
62
|
+
# responses (404 if the event is not in the caller's account).
|
|
63
|
+
def get(event_id)
|
|
64
|
+
resp = @api.get_event(event_id)
|
|
65
|
+
AuditEvent.from_resource(resp.data)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# List events with filters and cursor pagination. Returns a
|
|
69
|
+
# +Smplkit::Audit::ListEventsPage+ whose +#events+ is the page and
|
|
70
|
+
# +#next_cursor+ is the opaque token for the next page (or nil).
|
|
71
|
+
def list(action: nil, resource_type: nil, resource_id: nil,
|
|
72
|
+
actor_type: nil, actor_id: nil, occurred_at_range: nil,
|
|
73
|
+
page_size: nil, page_after: nil)
|
|
74
|
+
# Generated client opts use snake_case keys that internally map
|
|
75
|
+
# to the JSON:API ``filter[*]`` / ``page[*]`` query-string format
|
|
76
|
+
# (see default_api.rb#list_events_with_http_info). Without the
|
|
77
|
+
# underscores these silently fall through and the filters never
|
|
78
|
+
# reach the server.
|
|
79
|
+
opts = {}
|
|
80
|
+
opts[:filter_action] = action if action
|
|
81
|
+
opts[:filter_resource_type] = resource_type if resource_type
|
|
82
|
+
opts[:filter_resource_id] = resource_id if resource_id
|
|
83
|
+
opts[:filter_actor_type] = actor_type if actor_type
|
|
84
|
+
opts[:filter_actor_id] = actor_id if actor_id
|
|
85
|
+
opts[:filter_occurred_at] = occurred_at_range if occurred_at_range
|
|
86
|
+
opts[:page_size] = page_size if page_size
|
|
87
|
+
opts[:page_after] = page_after if page_after
|
|
88
|
+
|
|
89
|
+
resp = @api.list_events(opts)
|
|
90
|
+
events = (resp.data || []).map { |r| AuditEvent.from_resource(r) }
|
|
91
|
+
next_cursor = nil
|
|
92
|
+
if resp.links&._next.is_a?(String)
|
|
93
|
+
next_link = resp.links._next
|
|
94
|
+
if (idx = next_link.index("page[after]="))
|
|
95
|
+
next_cursor = next_link[(idx + "page[after]=".length)..]
|
|
96
|
+
if (amp = next_cursor.index("&"))
|
|
97
|
+
next_cursor = next_cursor[0...amp]
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
ListEventsPage.new(events, next_cursor)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Block until the in-memory buffer is drained or the timeout elapses.
|
|
105
|
+
def flush(timeout: 5.0)
|
|
106
|
+
@buffer.flush(timeout: timeout)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# @api private — drains best-effort and stops the worker thread.
|
|
110
|
+
def _close
|
|
111
|
+
@buffer.close
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Public-facing audit event resource. ADR-047 §2.3.1.
|
|
116
|
+
AuditEvent = Struct.new(
|
|
117
|
+
:id, :action, :resource_type, :resource_id,
|
|
118
|
+
:occurred_at, :created_at,
|
|
119
|
+
:actor_type, :actor_id, :actor_label,
|
|
120
|
+
:snapshot, :data, :idempotency_key,
|
|
121
|
+
keyword_init: true
|
|
122
|
+
) do
|
|
123
|
+
def self.from_resource(resource)
|
|
124
|
+
attrs = resource.attributes
|
|
125
|
+
new(
|
|
126
|
+
id: resource.id,
|
|
127
|
+
action: attrs.action,
|
|
128
|
+
resource_type: attrs.resource_type,
|
|
129
|
+
resource_id: attrs.resource_id,
|
|
130
|
+
occurred_at: attrs.occurred_at,
|
|
131
|
+
created_at: attrs.created_at,
|
|
132
|
+
actor_type: attrs.actor_type,
|
|
133
|
+
actor_id: attrs.actor_id,
|
|
134
|
+
actor_label: attrs.actor_label,
|
|
135
|
+
snapshot: attrs.snapshot,
|
|
136
|
+
data: attrs.data || {},
|
|
137
|
+
idempotency_key: attrs.idempotency_key
|
|
138
|
+
)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# One page of events plus a cursor for the next page (nil on the last page).
|
|
143
|
+
ListEventsPage = Struct.new(:events, :next_cursor)
|
|
144
|
+
end
|
|
145
|
+
end
|
data/lib/smplkit/client.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require "concurrent"
|
|
4
4
|
|
|
5
|
+
require "smplkit/audit/client"
|
|
6
|
+
|
|
5
7
|
module Smplkit
|
|
6
8
|
# Synchronous entry point for the smplkit SDK.
|
|
7
9
|
#
|
|
@@ -28,7 +30,7 @@ module Smplkit
|
|
|
28
30
|
class Client
|
|
29
31
|
PERIODIC_FLUSH_INTERVAL = 60.0
|
|
30
32
|
|
|
31
|
-
attr_reader :manage, :config, :flags, :logging
|
|
33
|
+
attr_reader :manage, :config, :flags, :logging, :audit
|
|
32
34
|
|
|
33
35
|
# Construct, yield to the block, and close on exit.
|
|
34
36
|
def self.open(**kwargs)
|
|
@@ -40,7 +42,7 @@ module Smplkit
|
|
|
40
42
|
end
|
|
41
43
|
end
|
|
42
44
|
|
|
43
|
-
def initialize(api_key: nil, environment: nil, service: nil, profile: nil,
|
|
45
|
+
def initialize(api_key: nil, environment: nil, service: nil, profile: nil, # rubocop:disable Metrics/AbcSize
|
|
44
46
|
base_domain: nil, scheme: nil, debug: nil, telemetry: nil)
|
|
45
47
|
cfg = ConfigResolution.resolve_config(
|
|
46
48
|
profile: profile, api_key: api_key, base_domain: base_domain, scheme: scheme,
|
|
@@ -70,6 +72,7 @@ module Smplkit
|
|
|
70
72
|
app_url = ConfigResolution.service_url(cfg.scheme, "app", cfg.base_domain)
|
|
71
73
|
flags_url = ConfigResolution.service_url(cfg.scheme, "flags", cfg.base_domain)
|
|
72
74
|
logging_url = ConfigResolution.service_url(cfg.scheme, "logging", cfg.base_domain)
|
|
75
|
+
audit_url = ConfigResolution.service_url(cfg.scheme, "audit", cfg.base_domain)
|
|
73
76
|
@app_base_url = app_url
|
|
74
77
|
|
|
75
78
|
@metrics = if cfg.telemetry
|
|
@@ -84,6 +87,7 @@ module Smplkit
|
|
|
84
87
|
flags_base_url: flags_url, app_base_url: app_url)
|
|
85
88
|
@logging = Logging::LoggingClient.new(self, manage: @manage, metrics: @metrics,
|
|
86
89
|
logging_base_url: logging_url, app_base_url: app_url)
|
|
90
|
+
@audit = Audit::AuditClient.new(api_key: cfg.api_key, base_url: audit_url)
|
|
87
91
|
|
|
88
92
|
@closed = false
|
|
89
93
|
schedule_periodic_flush
|
|
@@ -153,6 +157,7 @@ module Smplkit
|
|
|
153
157
|
@logging._close
|
|
154
158
|
@flags._close
|
|
155
159
|
@config._close
|
|
160
|
+
@audit._close
|
|
156
161
|
@ws_manager&.stop
|
|
157
162
|
@ws_manager = nil
|
|
158
163
|
@manage.close
|
data/lib/smplkit.rb
CHANGED
|
@@ -16,7 +16,7 @@ require_relative "smplkit/version"
|
|
|
16
16
|
# +lib/smplkit/_generated/<svc>/lib+ uses internal +require+ paths like
|
|
17
17
|
# +smplkit_<svc>_client/api_client+, so the directory has to be on
|
|
18
18
|
# +$LOAD_PATH+ before its top-level entry is required.
|
|
19
|
-
%w[app config flags logging].each do |svc|
|
|
19
|
+
%w[app audit config flags logging].each do |svc|
|
|
20
20
|
generated_lib = File.expand_path("smplkit/_generated/#{svc}/lib", __dir__)
|
|
21
21
|
$LOAD_PATH.unshift(generated_lib) if File.directory?(generated_lib)
|
|
22
22
|
end
|
|
@@ -27,7 +27,8 @@ end
|
|
|
27
27
|
module SmplkitGeneratedClient # rubocop:disable Style/OneClassPerFile
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
%w[smplkit_app_client smplkit_config_client smplkit_flags_client
|
|
30
|
+
%w[smplkit_app_client smplkit_audit_client smplkit_config_client smplkit_flags_client
|
|
31
|
+
smplkit_logging_client].each do |gem_lib|
|
|
31
32
|
require gem_lib
|
|
32
33
|
rescue LoadError
|
|
33
34
|
# Generated tree may be intentionally absent in development snapshots —
|
|
@@ -61,6 +62,9 @@ require_relative "smplkit/management/types"
|
|
|
61
62
|
require_relative "smplkit/management/models"
|
|
62
63
|
require_relative "smplkit/management/buffer"
|
|
63
64
|
require_relative "smplkit/management/client"
|
|
65
|
+
require_relative "smplkit/audit/buffer"
|
|
66
|
+
require_relative "smplkit/audit/events"
|
|
67
|
+
require_relative "smplkit/audit/client"
|
|
64
68
|
require_relative "smplkit/client"
|
|
65
69
|
|
|
66
70
|
require_relative "smplkit/railtie" if defined?(Rails::Railtie)
|
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.
|
|
4
|
+
version: 1.0.19
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Smpl Solutions LLC
|
|
@@ -560,6 +560,9 @@ files:
|
|
|
560
560
|
- lib/smplkit/_generated/logging/spec/models/usage_list_response_spec.rb
|
|
561
561
|
- lib/smplkit/_generated/logging/spec/models/usage_resource_spec.rb
|
|
562
562
|
- lib/smplkit/_generated/logging/spec/spec_helper.rb
|
|
563
|
+
- lib/smplkit/audit/buffer.rb
|
|
564
|
+
- lib/smplkit/audit/client.rb
|
|
565
|
+
- lib/smplkit/audit/events.rb
|
|
563
566
|
- lib/smplkit/client.rb
|
|
564
567
|
- lib/smplkit/config/client.rb
|
|
565
568
|
- lib/smplkit/config/helpers.rb
|