smplkit 3.0.19 → 3.0.21

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: 8dc0e8067c66c43e659ad29aff4756448763e90c251e300d598762a7aad1130b
4
- data.tar.gz: 0a658e3773fcb670b7891c6fc5b18ffeea16780a3195c8e0aa866132e2018fe9
3
+ metadata.gz: 38771e2469f7abb018b9c57b1a841a8a9da9a350b47be1206254ee60aad6df17
4
+ data.tar.gz: d19aedd7e56e0fec286c70640d9268ad8502003da974c1fa65fe7ec511335146
5
5
  SHA512:
6
- metadata.gz: 6f1ef7cc9470990ee57fbe8587fff5f4a0ac5f9ab2199186b4f585fbe8dfc8dc9efd74acc8787f3c4a3fab4b9acc4c2860118e6f5b91c4934f8684d0c8d88f7f
7
- data.tar.gz: 9c8cf307db3e470c047b518aeb079b558d7c19f1687293e1103d1570104fc2c2bfde6f2591f090a94bf46cc3b438123d38e7caa7db2f7b1a923aad91b096e4c5
6
+ metadata.gz: d320d1ddd09c4ce053cd67e209f9515e712021737f4e6b10b13bbb3a513296596eeafe68048cdf1911705d3f7ed55bbf12eda73d62417a962bc8722abeeaff5c
7
+ data.tar.gz: e5bb79dd560c8109c75ad76d20047add2746b421c2891435560268b97ef12ed1a82c7849713bcb87ecd75adee8c53781b21e37821cc07f14d4cb512b98fe3be2
@@ -16,10 +16,10 @@ require 'time'
16
16
  module SmplkitGeneratedClient::Audit
17
17
  # An audit event — a record that something happened, attributed to an actor and a resource. When recording a snapshot of the resource at the time of the event, place it inside `data`. smplkit's own integrations nest it under `data.snapshot`, but the slot is yours to use however you like.
18
18
  class Event < ApiModelBase
19
- # Slug for what happened, e.g. `user.created`. Lowercase, dot-separated.
19
+ # What happened, e.g. `user.created`. Any non-empty string.
20
20
  attr_accessor :action
21
21
 
22
- # Slug for the kind of resource the event is about, e.g. `user`. Lowercase, dot-separated.
22
+ # Kind of resource the event is about, e.g. `user`. Any non-empty string.
23
23
  attr_accessor :resource_type
24
24
 
25
25
  # Identifier of the specific resource the event is about.
@@ -31,6 +31,15 @@ module SmplkitGeneratedClient::Audit
31
31
  # When the event actually happened. Defaults to the server receipt time (`created_at`).
32
32
  attr_accessor :occurred_at
33
33
 
34
+ # Kind of actor that caused the event, e.g. `USER`, `API_KEY`, `SYSTEM`, or any other label you choose. Free-form string; the API does not constrain or interpret it.
35
+ attr_accessor :actor_type
36
+
37
+ # Identifier of the actor that caused the event. Free-form string — any identifier scheme is accepted.
38
+ attr_accessor :actor_id
39
+
40
+ # Human-readable label for the actor (e.g. an email address or API key name) at the time the event was recorded.
41
+ attr_accessor :actor_label
42
+
34
43
  # Free-form payload attached to the event. Use it for resource snapshots (by convention under `data.snapshot`), request identifiers, or any other context the event needs to carry.
35
44
  attr_accessor :data
36
45
 
@@ -40,15 +49,6 @@ module SmplkitGeneratedClient::Audit
40
49
  # When the event was received and recorded.
41
50
  attr_accessor :created_at
42
51
 
43
- # Kind of credential that emitted the event, e.g. `USER` or `API_KEY`. Resolved server-side from the request credential.
44
- attr_accessor :actor_type
45
-
46
- # Identifier of the actor that emitted the event.
47
- attr_accessor :actor_id
48
-
49
- # Human-readable label for the actor (e.g. the user's email address or the API key name) at the time the event was recorded.
50
- attr_accessor :actor_label
51
-
52
52
  # The idempotency key used to deduplicate the record. Echoes the `Idempotency-Key` header if one was supplied, otherwise a key derived from the event's content.
53
53
  attr_accessor :idempotency_key
54
54
 
@@ -60,12 +60,12 @@ module SmplkitGeneratedClient::Audit
60
60
  :'resource_id' => :'resource_id',
61
61
  :'description' => :'description',
62
62
  :'occurred_at' => :'occurred_at',
63
- :'data' => :'data',
64
- :'do_not_forward' => :'do_not_forward',
65
- :'created_at' => :'created_at',
66
63
  :'actor_type' => :'actor_type',
67
64
  :'actor_id' => :'actor_id',
68
65
  :'actor_label' => :'actor_label',
66
+ :'data' => :'data',
67
+ :'do_not_forward' => :'do_not_forward',
68
+ :'created_at' => :'created_at',
69
69
  :'idempotency_key' => :'idempotency_key'
70
70
  }
71
71
  end
@@ -88,12 +88,12 @@ module SmplkitGeneratedClient::Audit
88
88
  :'resource_id' => :'String',
89
89
  :'description' => :'String',
90
90
  :'occurred_at' => :'Time',
91
- :'data' => :'Hash<String, Object>',
92
- :'do_not_forward' => :'Boolean',
93
- :'created_at' => :'Time',
94
91
  :'actor_type' => :'String',
95
92
  :'actor_id' => :'String',
96
93
  :'actor_label' => :'String',
94
+ :'data' => :'Hash<String, Object>',
95
+ :'do_not_forward' => :'Boolean',
96
+ :'created_at' => :'Time',
97
97
  :'idempotency_key' => :'String'
98
98
  }
99
99
  end
@@ -103,10 +103,10 @@ module SmplkitGeneratedClient::Audit
103
103
  Set.new([
104
104
  :'description',
105
105
  :'occurred_at',
106
- :'created_at',
107
106
  :'actor_type',
108
107
  :'actor_id',
109
108
  :'actor_label',
109
+ :'created_at',
110
110
  :'idempotency_key'
111
111
  ])
112
112
  end
@@ -153,6 +153,18 @@ module SmplkitGeneratedClient::Audit
153
153
  self.occurred_at = attributes[:'occurred_at']
154
154
  end
155
155
 
156
+ if attributes.key?(:'actor_type')
157
+ self.actor_type = attributes[:'actor_type']
158
+ end
159
+
160
+ if attributes.key?(:'actor_id')
161
+ self.actor_id = attributes[:'actor_id']
162
+ end
163
+
164
+ if attributes.key?(:'actor_label')
165
+ self.actor_label = attributes[:'actor_label']
166
+ end
167
+
156
168
  if attributes.key?(:'data')
157
169
  if (value = attributes[:'data']).is_a?(Hash)
158
170
  self.data = value
@@ -169,18 +181,6 @@ module SmplkitGeneratedClient::Audit
169
181
  self.created_at = attributes[:'created_at']
170
182
  end
171
183
 
172
- if attributes.key?(:'actor_type')
173
- self.actor_type = attributes[:'actor_type']
174
- end
175
-
176
- if attributes.key?(:'actor_id')
177
- self.actor_id = attributes[:'actor_id']
178
- end
179
-
180
- if attributes.key?(:'actor_label')
181
- self.actor_label = attributes[:'actor_label']
182
- end
183
-
184
184
  if attributes.key?(:'idempotency_key')
185
185
  self.idempotency_key = attributes[:'idempotency_key']
186
186
  end
@@ -283,12 +283,12 @@ module SmplkitGeneratedClient::Audit
283
283
  resource_id == o.resource_id &&
284
284
  description == o.description &&
285
285
  occurred_at == o.occurred_at &&
286
- data == o.data &&
287
- do_not_forward == o.do_not_forward &&
288
- created_at == o.created_at &&
289
286
  actor_type == o.actor_type &&
290
287
  actor_id == o.actor_id &&
291
288
  actor_label == o.actor_label &&
289
+ data == o.data &&
290
+ do_not_forward == o.do_not_forward &&
291
+ created_at == o.created_at &&
292
292
  idempotency_key == o.idempotency_key
293
293
  end
294
294
 
@@ -301,7 +301,7 @@ module SmplkitGeneratedClient::Audit
301
301
  # Calculates hash code according to all attributes.
302
302
  # @return [Integer] Hash code
303
303
  def hash
304
- [action, resource_type, resource_id, description, occurred_at, data, do_not_forward, created_at, actor_type, actor_id, actor_label, idempotency_key].hash
304
+ [action, resource_type, resource_id, description, occurred_at, actor_type, actor_id, actor_label, data, do_not_forward, created_at, idempotency_key].hash
305
305
  end
306
306
 
307
307
  # Builds the object from hash
@@ -57,37 +57,37 @@ describe SmplkitGeneratedClient::Audit::Event do
57
57
  end
58
58
  end
59
59
 
60
- describe 'test attribute "data"' do
60
+ describe 'test attribute "actor_type"' do
61
61
  it 'should work' do
62
62
  # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
63
63
  end
64
64
  end
65
65
 
66
- describe 'test attribute "do_not_forward"' do
66
+ describe 'test attribute "actor_id"' do
67
67
  it 'should work' do
68
68
  # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
69
69
  end
70
70
  end
71
71
 
72
- describe 'test attribute "created_at"' do
72
+ describe 'test attribute "actor_label"' do
73
73
  it 'should work' do
74
74
  # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
75
75
  end
76
76
  end
77
77
 
78
- describe 'test attribute "actor_type"' do
78
+ describe 'test attribute "data"' do
79
79
  it 'should work' do
80
80
  # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
81
81
  end
82
82
  end
83
83
 
84
- describe 'test attribute "actor_id"' do
84
+ describe 'test attribute "do_not_forward"' do
85
85
  it 'should work' do
86
86
  # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
87
87
  end
88
88
  end
89
89
 
90
- describe 'test attribute "actor_label"' do
90
+ describe 'test attribute "created_at"' do
91
91
  it 'should work' do
92
92
  # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
93
93
  end
@@ -2,10 +2,6 @@
2
2
 
3
3
  module Smplkit
4
4
  module Audit
5
- # Parse the +page[after]+ cursor out of a JSON:API +links.next+
6
- # URL. Returns nil for non-string input or when the link carries
7
- # no cursor parameter; trims trailing query params at the next
8
- # ampersand so they don't leak into the token.
9
5
  # Wrap a generated-audit-API call and translate +ApiError+ into the
10
6
  # +Smplkit::Error+ hierarchy. Connection-level failures (no
11
7
  # response code) become {Smplkit::ConnectionError}; status-coded
@@ -24,6 +20,10 @@ module Smplkit
24
20
  raise
25
21
  end
26
22
 
23
+ # Parse the +page[after]+ cursor out of a JSON:API +links.next+
24
+ # URL. Returns nil for non-string input or when the link carries
25
+ # no cursor parameter; trims trailing query params at the next
26
+ # ampersand so they don't leak into the token.
27
27
  def self.next_cursor(link)
28
28
  return nil unless link.is_a?(String)
29
29
 
@@ -49,23 +49,28 @@ module Smplkit
49
49
  out
50
50
  end
51
51
 
52
- # Public-facing enum for SIEM streaming destination types.
52
+ # Supported SIEM forwarder destination types (ADR-047 §2.12).
53
53
  #
54
- # Mirrors the +ForwarderType+ enum the audit OpenAPI spec emits
55
- # (ADR-047 §2.12). Customers pass these constantsor any string
56
- # in {VALUES} — to the management +forwarders+ surface. The wrapper
57
- # validates membership before round-tripping to the wire.
54
+ # Members are declared in alphabetical order. Customers pass these
55
+ # constants or the equivalent stringto the management
56
+ # +forwarders+ surface; the wrapper validates membership via {coerce}
57
+ # before round-tripping to the wire.
58
58
  module ForwarderType
59
- HTTP = "HTTP"
60
59
  DATADOG = "DATADOG"
60
+ ELASTIC = "ELASTIC"
61
+ HONEYCOMB = "HONEYCOMB"
62
+ HTTP = "HTTP"
63
+ NEW_RELIC = "NEW_RELIC"
61
64
  SPLUNK_HEC = "SPLUNK_HEC"
62
65
  SUMO_LOGIC = "SUMO_LOGIC"
63
- NEW_RELIC = "NEW_RELIC"
64
- HONEYCOMB = "HONEYCOMB"
65
- ELASTIC = "ELASTIC"
66
66
 
67
- VALUES = [HTTP, DATADOG, SPLUNK_HEC, SUMO_LOGIC, NEW_RELIC, HONEYCOMB, ELASTIC].freeze
67
+ VALUES = [DATADOG, ELASTIC, HONEYCOMB, HTTP, NEW_RELIC, SPLUNK_HEC, SUMO_LOGIC].freeze
68
68
 
69
+ # Validate and normalize an input to a wire-format string.
70
+ #
71
+ # @param value [String, nil] a published constant or its literal string.
72
+ # @return [String, nil] the canonical wire value (or +nil+ when input is +nil+).
73
+ # @raise [ArgumentError] when +value+ is not a member of {VALUES}.
69
74
  def self.coerce(value)
70
75
  return nil if value.nil?
71
76
 
@@ -77,7 +82,64 @@ module Smplkit
77
82
  end
78
83
  end
79
84
 
80
- # Public-facing audit event resource. ADR-047 §2.3.1.
85
+ # HTTP verb used by a forwarder's outbound delivery (ADR-047 §2.12).
86
+ #
87
+ # Mirrors the audit spec's +HttpConfigurationMethod+ enum so the
88
+ # +HttpConfiguration#method+ field is constrained to a known value
89
+ # instead of accepting any string. Members are declared in
90
+ # alphabetical order.
91
+ module HttpMethod
92
+ DELETE = "DELETE"
93
+ GET = "GET"
94
+ PATCH = "PATCH"
95
+ POST = "POST"
96
+ PUT = "PUT"
97
+
98
+ VALUES = [DELETE, GET, PATCH, POST, PUT].freeze
99
+
100
+ # Validate and normalize an input to a wire-format string.
101
+ #
102
+ # @param value [String, nil] a published constant or its literal string.
103
+ # @return [String, nil] the canonical wire value (or +nil+ when input is +nil+).
104
+ # @raise [ArgumentError] when +value+ is not a member of {VALUES}.
105
+ def self.coerce(value)
106
+ return nil if value.nil?
107
+
108
+ s = value.to_s
109
+ return s if VALUES.include?(s)
110
+
111
+ raise ArgumentError,
112
+ "Unknown HttpMethod #{value.inspect}; expected one of #{VALUES.inspect}"
113
+ end
114
+ end
115
+
116
+ # A single audit event as returned by the audit service (ADR-047 §2.3.1).
117
+ #
118
+ # @!attribute [rw] id
119
+ # @return [String] Server-assigned UUID for this event.
120
+ # @!attribute [rw] action
121
+ # @return [String] Action slug — e.g. +"user.created"+, +"invoice.paid"+.
122
+ # @!attribute [rw] resource_type
123
+ # @return [String] Type of resource the action operated on — e.g. +"invoice"+.
124
+ # @!attribute [rw] resource_id
125
+ # @return [String] Customer-facing id of the resource the action operated on.
126
+ # @!attribute [rw] occurred_at
127
+ # @return [String] ISO-8601 timestamp of when the action happened, as reported by the source.
128
+ # @!attribute [rw] created_at
129
+ # @return [String] ISO-8601 timestamp of when the audit service first ingested this event.
130
+ # @!attribute [rw] actor_type
131
+ # @return [String, nil] Type of actor (+"user"+, +"api_key"+, +"system"+, …) — +nil+ when unknown.
132
+ # @!attribute [rw] actor_id
133
+ # @return [String, nil] UUID of the actor when the actor is a tracked entity (user, api_key);
134
+ # +nil+ for system or anonymous events.
135
+ # @!attribute [rw] actor_label
136
+ # @return [String, nil] Display label for the actor — typically a name or email.
137
+ # @!attribute [rw] data
138
+ # @return [Hash{String => Object}] Free-form per-event payload defined by the customer.
139
+ # @!attribute [rw] idempotency_key
140
+ # @return [String, nil] Customer-supplied dedupe key, +nil+ if not provided.
141
+ # @!attribute [rw] do_not_forward
142
+ # @return [Boolean] When +true+, skip SIEM forwarder delivery regardless of any matching filter.
81
143
  AuditEvent = Struct.new(
82
144
  :id, :action, :resource_type, :resource_id,
83
145
  :occurred_at, :created_at,
@@ -110,6 +172,13 @@ module Smplkit
110
172
  # the customer-facing key as the resource id (ADR-014). The duplication
111
173
  # keeps SDK consumers from having to dig into the id field when
112
174
  # filtering UI controls; pick whichever name reads better in context.
175
+ #
176
+ # @!attribute [rw] id
177
+ # @return [String] JSON:API resource id (same as +resource_type+).
178
+ # @!attribute [rw] resource_type
179
+ # @return [String] The distinct resource_type slug.
180
+ # @!attribute [rw] created_at
181
+ # @return [String] ISO-8601 timestamp of the earliest sighting for this slug.
113
182
  ResourceType = Struct.new(:id, :resource_type, :created_at, keyword_init: true) do
114
183
  def self.from_resource(resource)
115
184
  attrs = resource.attributes
@@ -127,6 +196,13 @@ module Smplkit
127
196
  # +created_at+ is the earliest sighting; when the parent list call
128
197
  # filtered by +resource_type+, this is the first sighting of that
129
198
  # specific (action, resource_type) triple, not the action overall.
199
+ #
200
+ # @!attribute [rw] id
201
+ # @return [String] JSON:API resource id (same as +action+).
202
+ # @!attribute [rw] action
203
+ # @return [String] The distinct action slug.
204
+ # @!attribute [rw] created_at
205
+ # @return [String] ISO-8601 timestamp of the earliest sighting for this slug.
130
206
  Action = Struct.new(:id, :action, :created_at, keyword_init: true) do
131
207
  def self.from_resource(resource)
132
208
  attrs = resource.attributes
@@ -138,19 +214,41 @@ module Smplkit
138
214
  end
139
215
  end
140
216
 
217
+ # A single name/value HTTP header on a forwarder destination.
218
+ #
219
+ # @!attribute [rw] name
220
+ # @return [String] Header name (e.g. +"Authorization"+, +"DD-API-KEY"+).
221
+ # @!attribute [rw] value
222
+ # @return [String] Header value, plaintext on writes. The audit service
223
+ # encrypts values at rest; reads return them as +"<redacted>"+.
141
224
  HttpHeader = Struct.new(:name, :value, keyword_init: true)
142
225
 
226
+ # Forwarder destination HTTP request shape.
227
+ #
228
+ # @!attribute [rw] method
229
+ # @return [String] HTTP verb used for delivery. Defaults to {HttpMethod::POST}.
230
+ # @!attribute [rw] url
231
+ # @return [String] Destination URL the audit service sends each event to.
232
+ # @!attribute [rw] headers
233
+ # @return [Array<HttpHeader>] Headers attached to every outbound request.
234
+ # Values carry credentials and are encrypted at rest server-side; reads
235
+ # return them redacted.
236
+ # @!attribute [rw] success_status
237
+ # @return [String] Status the destination must return for delivery to count
238
+ # as success — an exact code (+"200"+, +"204"+) or a class (+"2xx"+, +"4xx"+).
239
+ # Defaults to +"2xx"+.
240
+ #
143
241
  # rubocop:disable Lint/StructNewOverride -- ``:method`` matches the
144
242
  # API attribute and shadowing Struct#method is the expected ergonomics.
145
243
  HttpConfiguration = Struct.new(:method, :url, :headers, :success_status, keyword_init: true) do
146
- def initialize(method: "POST", url: "", headers: nil, success_status: "2xx")
147
- super(method: method, url: url, headers: headers || [], success_status: success_status)
244
+ def initialize(method: HttpMethod::POST, url: "", headers: nil, success_status: "2xx")
245
+ super(method: HttpMethod.coerce(method), url: url, headers: headers || [], success_status: success_status)
148
246
  end
149
247
 
150
248
  def self.to_wire(src)
151
249
  h = src.is_a?(Hash) ? new(**src) : src
152
250
  SmplkitGeneratedClient::Audit::HttpConfiguration.new(
153
- method: h.method,
251
+ method: HttpMethod.coerce(h.method),
154
252
  url: h.url,
155
253
  headers: (h.headers || []).map do |hdr|
156
254
  name, value = if hdr.is_a?(Hash)
@@ -169,7 +267,7 @@ module Smplkit
169
267
  return new if src.nil?
170
268
 
171
269
  new(
172
- method: src.method || "POST",
270
+ method: src.method || HttpMethod::POST,
173
271
  url: src.url || "",
174
272
  headers: (src.headers || []).map { |h| HttpHeader.new(name: h.name, value: h.value) },
175
273
  success_status: src.success_status || "2xx"
@@ -178,17 +276,130 @@ module Smplkit
178
276
  end
179
277
  # rubocop:enable Lint/StructNewOverride
180
278
 
181
- # rubocop:disable Lint/StructNewOverride -- ``:filter`` matches the
182
- # API attribute and shadowing Struct#filter is the expected ergonomics.
183
- Forwarder = Struct.new(
184
- :id, :name, :description, :forwarder_type, :enabled,
185
- :filter, :transform_type, :transform, :configuration,
186
- :created_at, :updated_at, :deleted_at, :version,
187
- keyword_init: true
188
- ) do
189
- def self.from_resource(resource)
279
+ # A SIEM streaming forwarder configured on the customer's account.
280
+ #
281
+ # Active-record style: instantiate via
282
+ # +mgmt.audit.forwarders.new_forwarder(...)+, mutate fields directly,
283
+ # and call {#save} to persist or {#delete} to remove. Header values in
284
+ # +configuration.headers+ are returned redacted on reads — the GET path
285
+ # on the audit API replaces every header value with +"<redacted>"+.
286
+ # Re-supply real values before calling {#save}; the SDK does not cache
287
+ # them client-side.
288
+ class Forwarder
289
+ # @return [String, nil] Server-assigned UUID, +nil+ until {#save} has run.
290
+ attr_accessor :id
291
+
292
+ # @return [String] Display name. Free-form.
293
+ attr_accessor :name
294
+
295
+ # @return [String] One of {ForwarderType::VALUES}.
296
+ attr_accessor :forwarder_type
297
+
298
+ # @return [Boolean] When +false+, the audit service skips delivery for
299
+ # this forwarder but still records +filtered_out+ deliveries.
300
+ attr_accessor :enabled
301
+
302
+ # @return [HttpConfiguration] Destination request configuration.
303
+ attr_accessor :configuration
304
+
305
+ # @return [String, nil] Optional free-text description.
306
+ attr_accessor :description
307
+
308
+ # @return [Hash, nil] Optional JSON Logic expression evaluated per event.
309
+ # When set, events that don't match are recorded as +filtered_out+
310
+ # deliveries instead of being delivered to the destination.
311
+ attr_accessor :filter
312
+
313
+ # @return [String, nil] Optional template applied to each event before
314
+ # delivery. Shape depends on {#transform_type}; for +"JSONATA"+, a
315
+ # JSONata expression. +nil+ delivers the event JSON as-is.
316
+ attr_accessor :transform
317
+
318
+ # @return [String, nil] Engine that evaluates {#transform}. Currently
319
+ # only +"JSONATA"+ is supported.
320
+ attr_accessor :transform_type
321
+
322
+ # @return [String, nil] ISO-8601 timestamp of first persist. +nil+ for an unsaved instance.
323
+ attr_accessor :created_at
324
+
325
+ # @return [String, nil] ISO-8601 timestamp of the most recent mutation.
326
+ attr_accessor :updated_at
327
+
328
+ # @return [String, nil] Soft-delete timestamp. +nil+ for live forwarders.
329
+ attr_accessor :deleted_at
330
+
331
+ # @return [Integer, nil] Monotonic version counter, bumped on every server-side write.
332
+ attr_accessor :version
333
+
334
+ def initialize(client = nil, name:, forwarder_type:, configuration:,
335
+ id: nil, enabled: true, description: nil,
336
+ filter: nil, transform: nil, transform_type: nil,
337
+ created_at: nil, updated_at: nil, deleted_at: nil, version: nil)
338
+ @client = client
339
+ @id = id
340
+ @name = name
341
+ @forwarder_type = ForwarderType.coerce(forwarder_type)
342
+ @configuration = configuration
343
+ @enabled = enabled
344
+ @description = description
345
+ @filter = filter
346
+ @transform = transform
347
+ @transform_type = transform_type
348
+ @created_at = created_at
349
+ @updated_at = updated_at
350
+ @deleted_at = deleted_at
351
+ @version = version
352
+ end
353
+
354
+ # Create or update this forwarder on the server.
355
+ #
356
+ # Upsert behavior is driven by {#created_at}: a forwarder with no
357
+ # +created_at+ is created (POST); otherwise it's full-replace updated
358
+ # (PUT). After the call, every field is refreshed from the server
359
+ # response (including newly-assigned +id+, +created_at+, +updated_at+,
360
+ # +version+).
361
+ #
362
+ # @return [self]
363
+ def save
364
+ raise "Forwarder was constructed without a client; cannot save" if @client.nil?
365
+
366
+ updated = @created_at.nil? ? @client._create_forwarder(self) : @client._update_forwarder(self)
367
+ _apply(updated)
368
+ self
369
+ end
370
+ alias save! save
371
+
372
+ # Soft-delete this forwarder on the server.
373
+ #
374
+ # @return [nil]
375
+ def delete
376
+ raise "Forwarder was constructed without a client or id; cannot delete" if @client.nil? || @id.nil?
377
+
378
+ @client.delete(@id)
379
+ end
380
+ alias delete! delete
381
+
382
+ # @api private
383
+ def _apply(other)
384
+ @id = other.id
385
+ @name = other.name
386
+ @forwarder_type = other.forwarder_type
387
+ @configuration = other.configuration
388
+ @enabled = other.enabled
389
+ @description = other.description
390
+ @filter = other.filter
391
+ @transform = other.transform
392
+ @transform_type = other.transform_type
393
+ @created_at = other.created_at
394
+ @updated_at = other.updated_at
395
+ @deleted_at = other.deleted_at
396
+ @version = other.version
397
+ end
398
+
399
+ def self.from_resource(resource, client: nil)
190
400
  a = resource.attributes
191
401
  new(
402
+ client,
192
403
  id: resource.id,
193
404
  name: a.name,
194
405
  description: a.description,
@@ -205,6 +416,5 @@ module Smplkit
205
416
  )
206
417
  end
207
418
  end
208
- # rubocop:enable Lint/StructNewOverride
209
419
  end
210
420
  end
@@ -4,12 +4,12 @@ module Smplkit
4
4
  module Config
5
5
  # Type of a +ConfigItem+ value.
6
6
  module ItemType
7
- STRING = "STRING"
8
- NUMBER = "NUMBER"
9
7
  BOOLEAN = "BOOLEAN"
10
8
  JSON = "JSON"
9
+ NUMBER = "NUMBER"
10
+ STRING = "STRING"
11
11
 
12
- ALL = [STRING, NUMBER, BOOLEAN, JSON].freeze
12
+ ALL = [BOOLEAN, JSON, NUMBER, STRING].freeze
13
13
  end
14
14
 
15
15
  # A single typed item in a +Config+.
@@ -7,16 +7,16 @@ module Smplkit
7
7
  # can validate calls. Raw strings are still accepted for backward
8
8
  # compatibility.
9
9
  module Op
10
+ CONTAINS = "contains"
10
11
  EQ = "=="
11
- NEQ = "!="
12
- LT = "<"
13
- LTE = "<="
14
12
  GT = ">"
15
13
  GTE = ">="
16
14
  IN = "in"
17
- CONTAINS = "contains"
15
+ LT = "<"
16
+ LTE = "<="
17
+ NEQ = "!="
18
18
 
19
- ALL = [EQ, NEQ, LT, LTE, GT, GTE, IN, CONTAINS].freeze
19
+ ALL = [CONTAINS, EQ, GT, GTE, IN, LT, LTE, NEQ].freeze
20
20
  end
21
21
 
22
22
  # A typed entity referenced by targeting rules and registered with smplkit.
@@ -4,11 +4,17 @@ module Smplkit
4
4
  # Log severity levels used by the Smpl Logging service.
5
5
  #
6
6
  # Acts as a string-valued enum: each constant equals its name when used in
7
- # string contexts, and supports comparison via the +ordinal+.
7
+ # string contexts, and supports comparison via the +ordinal+. Members are
8
+ # declared in alphabetical order; severity is encoded in {#ordinal}, not
9
+ # in declaration order.
8
10
  class LogLevel
9
- NAMES = %w[TRACE DEBUG INFO WARN ERROR FATAL SILENT].freeze
11
+ NAMES = %w[DEBUG ERROR FATAL INFO SILENT TRACE WARN].freeze
10
12
 
11
- attr_reader :name, :ordinal
13
+ # @return [String] Canonical level name (e.g. +"INFO"+).
14
+ attr_reader :name
15
+
16
+ # @return [Integer] Severity ordinal — TRACE=0 (lowest) through SILENT=6 (highest).
17
+ attr_reader :ordinal
12
18
 
13
19
  def initialize(name, ordinal)
14
20
  @name = name.freeze
@@ -26,15 +32,15 @@ module Smplkit
26
32
 
27
33
  include Comparable
28
34
 
29
- TRACE = new("TRACE", 0)
30
35
  DEBUG = new("DEBUG", 1)
31
- INFO = new("INFO", 2)
32
- WARN = new("WARN", 3)
33
36
  ERROR = new("ERROR", 4)
34
37
  FATAL = new("FATAL", 5)
38
+ INFO = new("INFO", 2)
35
39
  SILENT = new("SILENT", 6)
40
+ TRACE = new("TRACE", 0)
41
+ WARN = new("WARN", 3)
36
42
 
37
- ALL = [TRACE, DEBUG, INFO, WARN, ERROR, FATAL, SILENT].freeze
43
+ ALL = [DEBUG, ERROR, FATAL, INFO, SILENT, TRACE, WARN].freeze
38
44
 
39
45
  BY_NAME = ALL.to_h { |lvl| [lvl.name, lvl] }.freeze
40
46
 
@@ -8,6 +8,7 @@ module Smplkit
8
8
  # runtime client owns event recording and read-side queries; this
9
9
  # surface owns SIEM forwarder CRUD. ADR-047 §2.7.
10
10
  class AuditNamespace
11
+ # @return [ForwardersNamespace] CRUD surface for +mgmt.audit.forwarders+.
11
12
  attr_reader :forwarders
12
13
 
13
14
  def initialize(api_client)
@@ -19,41 +20,56 @@ module Smplkit
19
20
 
20
21
  # +mgmt.audit.forwarders.*+ — manage the customer's configured SIEM
21
22
  # forwarders.
23
+ #
24
+ # The active-record entry point is {#new_forwarder}: instantiate a
25
+ # draft, mutate fields, then call {Smplkit::Audit::Forwarder#save}.
26
+ # The namespace exposes {#list}, {#get}, and {#delete} directly; the
27
+ # +_create_forwarder+ / +_update_forwarder+ helpers are private and
28
+ # invoked by {Smplkit::Audit::Forwarder#save}.
22
29
  class ForwardersNamespace
23
30
  def initialize(api)
24
31
  @api = api
25
32
  end
26
33
 
27
- # Create a forwarder.
34
+ # Construct an unsaved {Smplkit::Audit::Forwarder} bound to this
35
+ # namespace. Call +#save+ on the returned instance to persist.
28
36
  #
29
37
  # @param name [String] Display name.
30
- # @param forwarder_type [String, Smplkit::Audit::ForwarderType]
31
- # One of the published {Smplkit::Audit::ForwarderType} constants
32
- # (or the equivalent string).
33
- # @param configuration [Smplkit::Audit::HttpConfiguration, Hash]
34
- # Transport-specific delivery configuration. Today every
35
- # forwarder_type uses {HttpConfiguration}; the URL and header
36
- # values inside are stored encrypted server-side and round-trip
37
- # to GET in plaintext.
38
+ # @param forwarder_type [String] One of {Smplkit::Audit::ForwarderType::VALUES}.
39
+ # @param configuration [Smplkit::Audit::HttpConfiguration] Destination
40
+ # request configuration. Headers carry credentials and are encrypted at
41
+ # rest server-side; reads return them redacted.
42
+ # @param enabled [Boolean] Whether the forwarder is active. Defaults +true+.
38
43
  # @param description [String, nil] Optional free-text description.
39
- # @param enabled [Boolean] Whether the forwarder is active.
40
- # @param filter [Hash, nil] Optional JSON Logic filter; events
41
- # that don't match are recorded as +filtered_out+ deliveries.
42
- # @param transform_type [String, nil] Engine that evaluates
43
- # +transform+. Set to +"JSONATA"+ whenever +transform+ is set.
44
- # @param transform [String, nil] Optional template applied to the
45
- # event payload before delivery (for +JSONATA+, a JSONata
46
- # expression). Nil sends the event JSON unchanged.
47
- def create(name:, forwarder_type:, configuration:, description: nil, enabled: true,
48
- filter: nil, transform_type: nil, transform: nil)
49
- body = build_body(nil, name: name, forwarder_type: forwarder_type,
50
- configuration: configuration, description: description,
51
- enabled: enabled, filter: filter,
52
- transform_type: transform_type, transform: transform)
53
- resp = Smplkit::Audit.call_api { @api.create_forwarder(body) }
54
- Smplkit::Audit::Forwarder.from_resource(resp.data)
44
+ # @param filter [Hash, nil] Optional JSON Logic filter; events that don't
45
+ # match are recorded as +filtered_out+ deliveries.
46
+ # @param transform [String, nil] Optional JSONata template applied to the
47
+ # event payload before delivery. Nil sends the event JSON unchanged.
48
+ # @return [Smplkit::Audit::Forwarder]
49
+ def new_forwarder(name:, forwarder_type:, configuration:,
50
+ enabled: true, description: nil,
51
+ filter: nil, transform: nil)
52
+ Smplkit::Audit::Forwarder.new(
53
+ self,
54
+ name: name,
55
+ forwarder_type: forwarder_type,
56
+ configuration: configuration,
57
+ enabled: enabled,
58
+ description: description,
59
+ filter: filter,
60
+ transform: transform,
61
+ transform_type: transform.nil? ? nil : "JSONATA"
62
+ )
55
63
  end
56
64
 
65
+ # List forwarders for the authenticated account.
66
+ #
67
+ # Offset paginated per ADR-014: pass +page_number+ (1-based) and
68
+ # +page_size+ (default 1000, max 1000). Pass +meta_total: true+ to
69
+ # populate +total+ and +total_pages+ in the returned +pagination+
70
+ # block (costs an extra COUNT query server-side).
71
+ #
72
+ # @return [ForwarderListPage]
57
73
  def list(forwarder_type: nil, enabled: nil, page_number: nil, page_size: nil, meta_total: nil)
58
74
  opts = {}
59
75
  opts[:filter_forwarder_type] = Smplkit::Audit::ForwarderType.coerce(forwarder_type) if forwarder_type
@@ -63,51 +79,67 @@ module Smplkit
63
79
  opts[:meta_total] = meta_total unless meta_total.nil?
64
80
 
65
81
  resp = Smplkit::Audit.call_api { @api.list_forwarders(opts) }
66
- forwarders = (resp.data || []).map { |r| Smplkit::Audit::Forwarder.from_resource(r) }
82
+ forwarders = (resp.data || []).map do |r|
83
+ Smplkit::Audit::Forwarder.from_resource(r, client: self)
84
+ end
67
85
  ForwarderListPage.new(forwarders, Smplkit::Audit.extract_pagination(resp.meta))
68
86
  end
69
87
 
88
+ # Fetch a single forwarder by id. The returned instance is bound to
89
+ # this namespace, so +forwarder.save+ and +forwarder.delete+ work.
90
+ #
91
+ # @param forwarder_id [String]
92
+ # @return [Smplkit::Audit::Forwarder]
70
93
  def get(forwarder_id)
71
94
  resp = Smplkit::Audit.call_api { @api.get_forwarder(forwarder_id) }
72
- Smplkit::Audit::Forwarder.from_resource(resp.data)
95
+ Smplkit::Audit::Forwarder.from_resource(resp.data, client: self)
73
96
  end
74
97
 
75
- # Full-replace update. PUT semantics — every field is overwritten.
98
+ # Soft-delete a forwarder.
76
99
  #
77
- # The URL and header values inside +configuration+ are returned in
78
- # plaintext on GET, so a fetched forwarder can be round-tripped to
79
- # PUT without re-entering secrets.
80
- def update(forwarder_id, name:, forwarder_type:, configuration:, description: nil,
81
- enabled: true, filter: nil, transform_type: nil, transform: nil)
82
- body = build_body(forwarder_id, name: name, forwarder_type: forwarder_type,
83
- configuration: configuration, description: description,
84
- enabled: enabled, filter: filter,
85
- transform_type: transform_type, transform: transform)
86
- resp = Smplkit::Audit.call_api { @api.update_forwarder(forwarder_id, body) }
87
- Smplkit::Audit::Forwarder.from_resource(resp.data)
88
- end
89
-
100
+ # @param forwarder_id [String]
101
+ # @return [nil]
90
102
  def delete(forwarder_id)
91
103
  Smplkit::Audit.call_api { @api.delete_forwarder(forwarder_id) }
92
104
  nil
93
105
  end
94
106
 
107
+ # @api private — POST a new forwarder. Called by
108
+ # {Smplkit::Audit::Forwarder#save} on unsaved instances.
109
+ def _create_forwarder(forwarder)
110
+ resp = Smplkit::Audit.call_api { @api.create_forwarder(build_body(forwarder)) }
111
+ Smplkit::Audit::Forwarder.from_resource(resp.data, client: self)
112
+ end
113
+
114
+ # @api private — Full-replace PUT for an existing forwarder. Called
115
+ # by {Smplkit::Audit::Forwarder#save} on instances with +created_at+.
116
+ #
117
+ # Header values must be re-supplied as plaintext; the GET path
118
+ # redacts them, so a PUT body containing +"<redacted>"+ would
119
+ # persist that literal. Track real header values client-side and
120
+ # round-trip them.
121
+ def _update_forwarder(forwarder)
122
+ raise ArgumentError, "cannot update a Forwarder with no id" if forwarder.id.nil?
123
+
124
+ resp = Smplkit::Audit.call_api { @api.update_forwarder(forwarder.id, build_body(forwarder)) }
125
+ Smplkit::Audit::Forwarder.from_resource(resp.data, client: self)
126
+ end
127
+
95
128
  private
96
129
 
97
- def build_body(id, name:, forwarder_type:, configuration:, description:, enabled:,
98
- filter:, transform_type:, transform:)
130
+ def build_body(forwarder)
99
131
  attrs = SmplkitGeneratedClient::Audit::Forwarder.new(
100
- name: name,
101
- description: description,
102
- forwarder_type: Smplkit::Audit::ForwarderType.coerce(forwarder_type),
103
- enabled: enabled,
104
- filter: filter,
105
- transform_type: transform_type,
106
- transform: transform,
107
- configuration: Smplkit::Audit::HttpConfiguration.to_wire(configuration)
132
+ name: forwarder.name,
133
+ description: forwarder.description,
134
+ forwarder_type: Smplkit::Audit::ForwarderType.coerce(forwarder.forwarder_type),
135
+ enabled: forwarder.enabled,
136
+ filter: forwarder.filter,
137
+ transform_type: forwarder.transform_type,
138
+ transform: forwarder.transform,
139
+ configuration: Smplkit::Audit::HttpConfiguration.to_wire(forwarder.configuration)
108
140
  )
109
141
  resource = SmplkitGeneratedClient::Audit::ForwarderResource.new(
110
- id: id ? id.to_s : "",
142
+ id: forwarder.id ? forwarder.id.to_s : "",
111
143
  type: "forwarder",
112
144
  attributes: attrs
113
145
  )
@@ -115,6 +147,13 @@ module Smplkit
115
147
  end
116
148
  end
117
149
 
150
+ # A single page returned from {ForwardersNamespace#list}.
151
+ #
152
+ # @!attribute [rw] forwarders
153
+ # @return [Array<Smplkit::Audit::Forwarder>] Forwarders in this page.
154
+ # @!attribute [rw] pagination
155
+ # @return [Hash] +meta.pagination+ block (+:page+, +:size+, and — only when
156
+ # the caller passed +meta_total: true+ — +:total+ / +:total_pages+).
118
157
  ForwarderListPage = Struct.new(:forwarders, :pagination)
119
158
  end
120
159
  end
@@ -9,10 +9,10 @@ module Smplkit
9
9
  # +AD_HOC+ environments are transient targets (preview branches, individual
10
10
  # developer sandboxes) that should not appear in the standard ordering.
11
11
  module EnvironmentClassification
12
- STANDARD = "STANDARD"
13
12
  AD_HOC = "AD_HOC"
13
+ STANDARD = "STANDARD"
14
14
 
15
- ALL = [STANDARD, AD_HOC].freeze
15
+ ALL = [AD_HOC, STANDARD].freeze
16
16
  end
17
17
 
18
18
  HEX_RE = /\A#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\z/
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: 3.0.19
4
+ version: 3.0.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - Smpl Solutions LLC