smplkit 3.0.95 → 3.0.96
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/account/client.rb +121 -0
- data/lib/smplkit/account/models.rb +53 -0
- data/lib/smplkit/api_support.rb +83 -0
- data/lib/smplkit/audit/client.rb +9 -10
- data/lib/smplkit/{management/audit.rb → audit/forwarders.rb} +73 -76
- data/lib/smplkit/audit/models.rb +40 -1
- data/lib/smplkit/buffers.rb +235 -0
- data/lib/smplkit/client.rb +126 -67
- data/lib/smplkit/config/client.rb +617 -182
- data/lib/smplkit/config_resolution.rb +11 -5
- data/lib/smplkit/errors.rb +8 -0
- data/lib/smplkit/flags/client.rb +472 -114
- data/lib/smplkit/flags/types.rb +6 -7
- data/lib/smplkit/{management/jobs.rb → jobs/client.rb} +148 -89
- data/lib/smplkit/logging/client.rb +647 -192
- data/lib/smplkit/logging/helpers.rb +1 -0
- data/lib/smplkit/logging/models.rb +92 -1
- data/lib/smplkit/logging/sources.rb +1 -1
- data/lib/smplkit/platform/client.rb +472 -0
- data/lib/smplkit/platform/models.rb +182 -0
- data/lib/smplkit/{management → platform}/types.rb +7 -4
- data/lib/smplkit/transport.rb +99 -0
- data/lib/smplkit.rb +18 -6
- metadata +11 -7
- data/lib/smplkit/management/buffer.rb +0 -198
- data/lib/smplkit/management/client.rb +0 -1074
- data/lib/smplkit/management/models.rb +0 -178
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a1d765730dd1bb26ebf38f16627878d7d33220bc20bd005b98b12303a18e88c1
|
|
4
|
+
data.tar.gz: 1109c9cd1b02865ae3d2148a8232509b185d828ed8762c025cf82dcfc9c9c09e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '099f6f60a8acdbd5804cd32743d7f6d6e28261de9901f08a354536da0e49a8b3cec0809790106b072cd7275daa60fdd4279b979dca22590ae74d87382bed04ed'
|
|
7
|
+
data.tar.gz: '089e631caa282df42aea0344a2e38fe17795e2d773d6799e6684ee1d389d418b0d4c1714fcf398c70b8961fdd4919b11c1b7b56f26b8e7a6e1b631853ac12447'
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "faraday"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
module Smplkit
|
|
7
|
+
module Account
|
|
8
|
+
# Resolve the (app_base_url, api_key, extra_headers) for the settings client.
|
|
9
|
+
#
|
|
10
|
+
# +base_url+/+api_key+ are used directly when both are supplied (the path
|
|
11
|
+
# the top-level client takes after it has already resolved them); otherwise
|
|
12
|
+
# the management config resolver fills in whatever is missing.
|
|
13
|
+
def self.resolve_account_target(api_key:, base_url:, profile:, base_domain:, scheme:, debug:, extra_headers:)
|
|
14
|
+
cfg = ConfigResolution.resolve_management_config(
|
|
15
|
+
profile: profile, api_key: api_key, base_domain: base_domain, scheme: scheme, debug: debug
|
|
16
|
+
)
|
|
17
|
+
resolved_key = api_key.nil? ? cfg.api_key : api_key
|
|
18
|
+
app_url = base_url.nil? ? ConfigResolution.service_url(cfg.scheme, "app", cfg.base_domain) : base_url
|
|
19
|
+
headers = {}
|
|
20
|
+
headers.merge!(cfg.extra_headers || {})
|
|
21
|
+
headers.merge!(extra_headers || {})
|
|
22
|
+
[app_url.sub(%r{/+\z}, ""), resolved_key, headers]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Sync account-settings get/save (+client.account.settings+).
|
|
26
|
+
#
|
|
27
|
+
# The endpoint isn't JSON:API — body is a raw JSON object — so we use an
|
|
28
|
+
# HTTP client directly rather than going through a generated client.
|
|
29
|
+
class SettingsClient
|
|
30
|
+
SETTINGS_PATH = "/api/v1/accounts/current/settings"
|
|
31
|
+
|
|
32
|
+
def initialize(app_base_url, api_key, extra_headers = nil)
|
|
33
|
+
@base_url = app_base_url
|
|
34
|
+
@headers = {
|
|
35
|
+
"Authorization" => "Bearer #{api_key}",
|
|
36
|
+
"Content-Type" => "application/json"
|
|
37
|
+
}.merge(extra_headers || {})
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def get
|
|
41
|
+
resp = connection.get(SETTINGS_PATH)
|
|
42
|
+
Errors.raise_for_status(resp.status, resp.body.to_s)
|
|
43
|
+
AccountSettings.new(self, data: parse_body(resp.body))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def _save(data)
|
|
47
|
+
resp = connection.put(SETTINGS_PATH) { |req| req.body = JSON.generate(data) }
|
|
48
|
+
Errors.raise_for_status(resp.status, resp.body.to_s)
|
|
49
|
+
AccountSettings.new(self, data: parse_body(resp.body))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
# A short-lived connection per call — mirrors the Python settings client
|
|
55
|
+
# opening and closing its own HTTP client each time.
|
|
56
|
+
def connection
|
|
57
|
+
Faraday.new(url: @base_url, headers: @headers, request: { timeout: 30 })
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def parse_body(body)
|
|
61
|
+
return {} if body.nil? || body.to_s.empty?
|
|
62
|
+
|
|
63
|
+
parsed = JSON.parse(body)
|
|
64
|
+
parsed.is_a?(Hash) ? parsed : {}
|
|
65
|
+
rescue JSON::ParserError
|
|
66
|
+
{}
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# The Smpl Account client (sync).
|
|
71
|
+
#
|
|
72
|
+
# Exposes the authenticated account's own configuration, reachable as
|
|
73
|
+
# +client.account+ (+Smplkit::Client+) or constructed directly:
|
|
74
|
+
#
|
|
75
|
+
# account = Smplkit::AccountClient.new(api_key: "sk_...")
|
|
76
|
+
# settings = account.settings.get
|
|
77
|
+
# settings.environment_order = ["production", "staging"]
|
|
78
|
+
# settings.save
|
|
79
|
+
#
|
|
80
|
+
# Sub-client: +settings+ (get/save). Pure CRUD — no +install+ required.
|
|
81
|
+
#
|
|
82
|
+
# @param api_key [String, nil] API key. When omitted, resolved from
|
|
83
|
+
# +SMPLKIT_API_KEY+ or +~/.smplkit+.
|
|
84
|
+
# @param base_url [String, nil] Full app-service base URL. Usually resolved
|
|
85
|
+
# from +base_domain+/+scheme+; supplied directly by the top-level clients
|
|
86
|
+
# which have already computed it.
|
|
87
|
+
# @param profile [String, nil] Named +~/.smplkit+ profile section.
|
|
88
|
+
# @param base_domain [String, nil] Base domain for API requests (default
|
|
89
|
+
# +"smplkit.com"+).
|
|
90
|
+
# @param scheme [String, nil] URL scheme (default +"https"+).
|
|
91
|
+
# @param debug [Boolean, nil] Enable SDK debug logging.
|
|
92
|
+
# @param extra_headers [Hash, nil] Extra headers attached to every request.
|
|
93
|
+
class AccountClient
|
|
94
|
+
attr_reader :settings
|
|
95
|
+
|
|
96
|
+
def initialize(api_key: nil, base_url: nil, profile: nil, base_domain: nil,
|
|
97
|
+
scheme: nil, debug: nil, extra_headers: nil)
|
|
98
|
+
app_url, resolved_key, headers = Account.resolve_account_target(
|
|
99
|
+
api_key: api_key, base_url: base_url, profile: profile,
|
|
100
|
+
base_domain: base_domain, scheme: scheme, debug: debug, extra_headers: extra_headers
|
|
101
|
+
)
|
|
102
|
+
@settings = SettingsClient.new(app_url, resolved_key, headers.empty? ? nil : headers)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# No-op — the settings client opens a short-lived HTTP client per call.
|
|
106
|
+
def close; end
|
|
107
|
+
|
|
108
|
+
# Construct, yield to the block, and close on exit.
|
|
109
|
+
def self.open(**kwargs)
|
|
110
|
+
client = new(**kwargs)
|
|
111
|
+
begin
|
|
112
|
+
yield client
|
|
113
|
+
ensure
|
|
114
|
+
client.close
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
AccountClient = Account::AccountClient
|
|
121
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Smplkit
|
|
4
|
+
module Account
|
|
5
|
+
# Active-record account-settings model.
|
|
6
|
+
#
|
|
7
|
+
# The wire format is opaque JSON. Documented keys are exposed as typed
|
|
8
|
+
# properties; unknown keys live in +raw+. +save+ writes the full settings
|
|
9
|
+
# object back.
|
|
10
|
+
class AccountSettings
|
|
11
|
+
def initialize(client = nil, data: nil)
|
|
12
|
+
@client = client
|
|
13
|
+
@data = data ? data.dup : {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# The full settings dict. Mutations are persisted on save.
|
|
17
|
+
def raw
|
|
18
|
+
@data
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def raw=(value)
|
|
22
|
+
@data = value.dup
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Canonical ordering of STANDARD environments. Empty list if unset.
|
|
26
|
+
def environment_order
|
|
27
|
+
Array(@data["environment_order"] || [])
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def environment_order=(value)
|
|
31
|
+
@data["environment_order"] = value.to_a
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def save
|
|
35
|
+
raise "AccountSettings was constructed without a client; cannot save" if @client.nil?
|
|
36
|
+
|
|
37
|
+
other = @client._save(@data)
|
|
38
|
+
_apply(other)
|
|
39
|
+
self
|
|
40
|
+
end
|
|
41
|
+
alias save! save
|
|
42
|
+
|
|
43
|
+
def to_s
|
|
44
|
+
"AccountSettings(#{@data.inspect})"
|
|
45
|
+
end
|
|
46
|
+
alias inspect to_s
|
|
47
|
+
|
|
48
|
+
def _apply(other)
|
|
49
|
+
@data = other.raw.dup
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Smplkit
|
|
4
|
+
# Ruby-internal adapters bridging the generated client layer to the wrapper.
|
|
5
|
+
module ApiSupport
|
|
6
|
+
# Default page[size] the runtime asks for when walking a list endpoint to
|
|
7
|
+
# completion. The platform caps page[size] at 1000; using the same value
|
|
8
|
+
# here makes the minimum number of round-trips per exhaustive fetch.
|
|
9
|
+
RUNTIME_PAGE_SIZE = 1000
|
|
10
|
+
|
|
11
|
+
# Wraps a generated-API call and converts any +ApiError+ raised by the
|
|
12
|
+
# generated layer into the +Smplkit::Error+ hierarchy. Connection-level
|
|
13
|
+
# failures (no response from the server) become +Smplkit::ConnectionError+;
|
|
14
|
+
# status-coded failures route through +Errors.raise_for_status+ which emits
|
|
15
|
+
# +NotFoundError+ / +ConflictError+ / +ValidationError+ / +Error+ depending
|
|
16
|
+
# on the JSON:API body.
|
|
17
|
+
module ErrorMapping
|
|
18
|
+
module_function
|
|
19
|
+
|
|
20
|
+
def call
|
|
21
|
+
yield
|
|
22
|
+
rescue StandardError => e
|
|
23
|
+
raise unless generated_api_error?(e)
|
|
24
|
+
|
|
25
|
+
raise Smplkit::ConnectionError, e.message.to_s if e.code.nil? || e.code.zero?
|
|
26
|
+
|
|
27
|
+
Smplkit::Errors.raise_for_status(e.code, e.response_body.to_s)
|
|
28
|
+
# raise_for_status only returns on 2xx; if we get here the generated
|
|
29
|
+
# layer raised on a 2xx (shouldn't happen) so re-raise the original.
|
|
30
|
+
raise
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def generated_api_error?(err)
|
|
34
|
+
klass_name = err.class.name.to_s
|
|
35
|
+
klass_name.start_with?("SmplkitGeneratedClient::") && klass_name.end_with?("::ApiError")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Walk a generated paginated list endpoint to completion.
|
|
40
|
+
#
|
|
41
|
+
# The block receives a per-page +opts+ hash with +page_number+ and
|
|
42
|
+
# +page_size+ filled in, calls the generated list method through
|
|
43
|
+
# {ErrorMapping.call}, and returns the response object. Pages stop when the
|
|
44
|
+
# server returns fewer rows than requested — the platform's standard
|
|
45
|
+
# last-page signal across every offset-paginated list endpoint. Returns the
|
|
46
|
+
# concatenated +response.data+ rows.
|
|
47
|
+
module PaginatedFetch
|
|
48
|
+
module_function
|
|
49
|
+
|
|
50
|
+
def collect(page_size: RUNTIME_PAGE_SIZE)
|
|
51
|
+
rows = []
|
|
52
|
+
page_number = 1
|
|
53
|
+
loop do
|
|
54
|
+
opts = { page_number: page_number, page_size: page_size }
|
|
55
|
+
response = ErrorMapping.call { yield(opts) }
|
|
56
|
+
page = response.data || []
|
|
57
|
+
rows.concat(page)
|
|
58
|
+
break if page.length < page_size
|
|
59
|
+
|
|
60
|
+
page_number += 1
|
|
61
|
+
end
|
|
62
|
+
rows
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Deep-stringify Hash keys so resources returned by generated +to_hash+
|
|
67
|
+
# (symbol-keyed) match what the wrapper helpers expect (string-keyed).
|
|
68
|
+
module ResourceShim
|
|
69
|
+
module_function
|
|
70
|
+
|
|
71
|
+
def stringify(value)
|
|
72
|
+
Smplkit::Helpers.deep_stringify_keys(value)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Convenience: produce a string-keyed Hash from a generated model.
|
|
76
|
+
def from_model(model)
|
|
77
|
+
return {} if model.nil?
|
|
78
|
+
|
|
79
|
+
stringify(model.to_hash)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
data/lib/smplkit/audit/client.rb
CHANGED
|
@@ -2,18 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
module Smplkit
|
|
4
4
|
module Audit
|
|
5
|
-
# Audit
|
|
5
|
+
# The Smpl Audit client — accessed via +client.audit+.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# SIEM forwarder CRUD lives on {Smplkit::ManagementClient} under
|
|
14
|
-
# +mgmt.audit.forwarders.*+.
|
|
7
|
+
# One client exposes the full surface — there is no runtime/management
|
|
8
|
+
# split for audit. Owns event recording and read-side queries:
|
|
9
|
+
# fire-and-forget +#events.record+, plus the audit-log +list+ / +get+ and
|
|
10
|
+
# the distinct-value listings (+resource_types+, +event_types+,
|
|
11
|
+
# +categories+) that back the Activity tab filter dropdowns, plus SIEM
|
|
12
|
+
# forwarder CRUD on +#forwarders+. ADR-047 §2.7.
|
|
15
13
|
class AuditClient
|
|
16
|
-
attr_reader :events, :resource_types, :event_types, :categories
|
|
14
|
+
attr_reader :events, :resource_types, :event_types, :categories, :forwarders
|
|
17
15
|
|
|
18
16
|
SDK_OWNED_HEADERS = %w[authorization content-type user-agent].freeze
|
|
19
17
|
|
|
@@ -41,6 +39,7 @@ module Smplkit
|
|
|
41
39
|
@resource_types = ResourceTypes.new(SmplkitGeneratedClient::Audit::ResourceTypesApi.new(api_client))
|
|
42
40
|
@event_types = EventTypes.new(SmplkitGeneratedClient::Audit::EventTypesApi.new(api_client))
|
|
43
41
|
@categories = Categories.new(SmplkitGeneratedClient::Audit::CategoriesApi.new(api_client))
|
|
42
|
+
@forwarders = ForwardersClient.new(SmplkitGeneratedClient::Audit::ForwardersApi.new(api_client))
|
|
44
43
|
end
|
|
45
44
|
|
|
46
45
|
def _close
|
|
@@ -1,40 +1,37 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# SIEM forwarder CRUD for the Smpl Audit client.
|
|
4
|
+
#
|
|
5
|
+
# Forwarders are part of the single unified audit surface — there is no
|
|
6
|
+
# runtime/management split for audit (see +Smplkit::Audit::AuditClient+). This
|
|
7
|
+
# file holds the forwarder CRUD sub-client that the unified +AuditClient+
|
|
8
|
+
# exposes as +.forwarders+:
|
|
9
|
+
#
|
|
10
|
+
# * +ForwardersClient+ — +forwarders.new/get/list/save/delete+
|
|
11
|
+
#
|
|
12
|
+
# The forwarder model classes (+Forwarder+, +ForwarderEnvironment+, …) live in
|
|
13
|
+
# +lib/smplkit/audit/models.rb+.
|
|
3
14
|
module Smplkit
|
|
4
|
-
module
|
|
5
|
-
#
|
|
15
|
+
module Audit
|
|
16
|
+
# Surface for +client.audit.forwarders.*+ — manage the customer's
|
|
17
|
+
# configured SIEM forwarders.
|
|
6
18
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
attr_reader :forwarders
|
|
13
|
-
|
|
14
|
-
def initialize(api_client)
|
|
15
|
-
@forwarders = ForwardersNamespace.new(
|
|
16
|
-
SmplkitGeneratedClient::Audit::ForwardersApi.new(api_client)
|
|
17
|
-
)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# +mgmt.audit.forwarders.*+ — manage the customer's configured SIEM
|
|
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}.
|
|
29
|
-
class ForwardersNamespace
|
|
19
|
+
# The active-record entry point is {#new}: instantiate a draft, mutate
|
|
20
|
+
# fields, then call {Smplkit::Audit::Forwarder#save}. The client exposes
|
|
21
|
+
# {#list}, {#get}, and {#delete} directly; the +_create_forwarder+ /
|
|
22
|
+
# +_update_forwarder+ helpers are invoked by {Smplkit::Audit::Forwarder#save}.
|
|
23
|
+
class ForwardersClient
|
|
30
24
|
def initialize(api)
|
|
31
25
|
@api = api
|
|
32
26
|
end
|
|
33
27
|
|
|
34
|
-
# Construct an unsaved {Smplkit::Audit::Forwarder} bound to this
|
|
35
|
-
#
|
|
28
|
+
# Construct an unsaved {Smplkit::Audit::Forwarder} bound to this client.
|
|
29
|
+
# Call +#save+ on the returned instance to persist.
|
|
36
30
|
#
|
|
37
|
-
# @param
|
|
31
|
+
# @param id [String] Caller-supplied unique identifier (the forwarder's
|
|
32
|
+
# key). Unique within the account; immutable. The audit service returns
|
|
33
|
+
# 409 if another live forwarder already uses this id.
|
|
34
|
+
# @param name [String] Display name. Defaults to +id+ when not supplied.
|
|
38
35
|
# @param forwarder_type [String] One of {Smplkit::Audit::ForwarderType::VALUES}.
|
|
39
36
|
# @param configuration [Smplkit::Audit::HttpConfiguration] Destination
|
|
40
37
|
# request configuration. Headers carry credentials and are encrypted at
|
|
@@ -56,11 +53,9 @@ module Smplkit
|
|
|
56
53
|
# @param filter [Hash, nil] Optional JSON Logic filter; events that don't
|
|
57
54
|
# match are recorded as +filtered_out+ deliveries.
|
|
58
55
|
# @param transform [Object, nil] Optional template applied to each event
|
|
59
|
-
# before delivery.
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
# +TransformType::JSONATA+, +transform+ must be a +String+ (the JSONata
|
|
63
|
-
# expression).
|
|
56
|
+
# before delivery. Must be paired with a non-nil +transform_type+; when
|
|
57
|
+
# +transform_type+ is +TransformType::JSONATA+, +transform+ must be a
|
|
58
|
+
# +String+ (the JSONata expression).
|
|
64
59
|
# @param transform_type [String, nil] Engine that evaluates +transform+ —
|
|
65
60
|
# one of {Smplkit::Audit::TransformType::VALUES}. Must be paired with a
|
|
66
61
|
# non-nil +transform+.
|
|
@@ -68,12 +63,12 @@ module Smplkit
|
|
|
68
63
|
# nil or both set, or when +transform_type+ is +JSONATA+ and +transform+
|
|
69
64
|
# is not a +String+.
|
|
70
65
|
# @return [Smplkit::Audit::Forwarder]
|
|
71
|
-
def
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
66
|
+
def new(id, forwarder_type:, configuration:, name: nil,
|
|
67
|
+
environments: nil, description: nil,
|
|
68
|
+
forward_smplkit_events: false,
|
|
69
|
+
filter: nil, transform: nil, transform_type: nil)
|
|
70
|
+
Forwarder.send(:validate_transform_pair!, transform, transform_type)
|
|
71
|
+
Forwarder.new(
|
|
77
72
|
self,
|
|
78
73
|
id: id,
|
|
79
74
|
name: name || id,
|
|
@@ -91,33 +86,31 @@ module Smplkit
|
|
|
91
86
|
# List forwarders for the authenticated account.
|
|
92
87
|
#
|
|
93
88
|
# Offset paginated per ADR-014: pass +page_number+ (1-based) and
|
|
94
|
-
# +page_size+ (default 1000, max 1000). Pass +meta_total: true+ to
|
|
95
|
-
#
|
|
96
|
-
#
|
|
89
|
+
# +page_size+ (default 1000, max 1000). Pass +meta_total: true+ to populate
|
|
90
|
+
# +total+ and +total_pages+ in the returned +pagination+ block (costs an
|
|
91
|
+
# extra COUNT query server-side).
|
|
97
92
|
#
|
|
98
93
|
# @return [ForwarderListPage]
|
|
99
94
|
def list(forwarder_type: nil, page_number: nil, page_size: nil, meta_total: nil)
|
|
100
95
|
opts = {}
|
|
101
|
-
opts[:filter_forwarder_type] =
|
|
96
|
+
opts[:filter_forwarder_type] = ForwarderType.coerce(forwarder_type) if forwarder_type
|
|
102
97
|
opts[:page_number] = page_number if page_number
|
|
103
98
|
opts[:page_size] = page_size if page_size
|
|
104
99
|
opts[:meta_total] = meta_total unless meta_total.nil?
|
|
105
100
|
|
|
106
|
-
resp =
|
|
107
|
-
forwarders = (resp.data || []).map
|
|
108
|
-
|
|
109
|
-
end
|
|
110
|
-
ForwarderListPage.new(forwarders, Smplkit::Audit.extract_pagination(resp.meta))
|
|
101
|
+
resp = Audit.call_api { @api.list_forwarders(opts) }
|
|
102
|
+
forwarders = (resp.data || []).map { |r| Forwarder.from_resource(r, client: self) }
|
|
103
|
+
ForwarderListPage.new(forwarders, Audit.extract_pagination(resp.meta))
|
|
111
104
|
end
|
|
112
105
|
|
|
113
|
-
# Fetch a single forwarder by id. The returned instance is bound to
|
|
114
|
-
#
|
|
106
|
+
# Fetch a single forwarder by id. The returned instance is bound to this
|
|
107
|
+
# client, so +forwarder.save+ and +forwarder.delete+ work.
|
|
115
108
|
#
|
|
116
109
|
# @param forwarder_id [String]
|
|
117
110
|
# @return [Smplkit::Audit::Forwarder]
|
|
118
111
|
def get(forwarder_id)
|
|
119
|
-
resp =
|
|
120
|
-
|
|
112
|
+
resp = Audit.call_api { @api.get_forwarder(forwarder_id) }
|
|
113
|
+
Forwarder.from_resource(resp.data, client: self)
|
|
121
114
|
end
|
|
122
115
|
|
|
123
116
|
# Soft-delete a forwarder.
|
|
@@ -125,7 +118,7 @@ module Smplkit
|
|
|
125
118
|
# @param forwarder_id [String]
|
|
126
119
|
# @return [nil]
|
|
127
120
|
def delete(forwarder_id)
|
|
128
|
-
|
|
121
|
+
Audit.call_api { @api.delete_forwarder(forwarder_id) }
|
|
129
122
|
nil
|
|
130
123
|
end
|
|
131
124
|
|
|
@@ -136,22 +129,21 @@ module Smplkit
|
|
|
136
129
|
raise ArgumentError, "Forwarder.id is required on create (caller-supplied key)"
|
|
137
130
|
end
|
|
138
131
|
|
|
139
|
-
resp =
|
|
140
|
-
|
|
132
|
+
resp = Audit.call_api { @api.create_forwarder(build_create_body(forwarder)) }
|
|
133
|
+
Forwarder.from_resource(resp.data, client: self)
|
|
141
134
|
end
|
|
142
135
|
|
|
143
|
-
# @api private — Full-replace PUT for an existing forwarder. Called
|
|
144
|
-
#
|
|
136
|
+
# @api private — Full-replace PUT for an existing forwarder. Called by
|
|
137
|
+
# {Smplkit::Audit::Forwarder#save} on instances with +created_at+.
|
|
145
138
|
#
|
|
146
|
-
# Header values must be re-supplied as plaintext; the GET path
|
|
147
|
-
#
|
|
148
|
-
#
|
|
149
|
-
# round-trip them.
|
|
139
|
+
# Header values must be re-supplied as plaintext; the GET path redacts
|
|
140
|
+
# them, so a PUT body containing +"<redacted>"+ would persist that literal.
|
|
141
|
+
# Track real header values client-side and round-trip them.
|
|
150
142
|
def _update_forwarder(forwarder)
|
|
151
143
|
raise ArgumentError, "cannot update a Forwarder with no id" if forwarder.id.nil?
|
|
152
144
|
|
|
153
|
-
resp =
|
|
154
|
-
|
|
145
|
+
resp = Audit.call_api { @api.update_forwarder(forwarder.id, build_body(forwarder)) }
|
|
146
|
+
Forwarder.from_resource(resp.data, client: self)
|
|
155
147
|
end
|
|
156
148
|
|
|
157
149
|
private
|
|
@@ -160,16 +152,15 @@ module Smplkit
|
|
|
160
152
|
#
|
|
161
153
|
# Accepts either {Smplkit::Audit::ForwarderEnvironment} values or plain
|
|
162
154
|
# hashes (+{ enabled: true, configuration: HttpConfiguration.new(...) }+)
|
|
163
|
-
# so callers can use the lightweight hash form without importing the
|
|
164
|
-
# model.
|
|
155
|
+
# so callers can use the lightweight hash form without importing the model.
|
|
165
156
|
def normalize_environments(environments)
|
|
166
157
|
return {} if environments.nil? || environments.empty?
|
|
167
158
|
|
|
168
159
|
environments.each_with_object({}) do |(env_key, value), out|
|
|
169
|
-
out[env_key.to_s] = if value.is_a?(
|
|
160
|
+
out[env_key.to_s] = if value.is_a?(ForwarderEnvironment)
|
|
170
161
|
value
|
|
171
162
|
else
|
|
172
|
-
|
|
163
|
+
ForwarderEnvironment.new(
|
|
173
164
|
enabled: value[:enabled] || value["enabled"] || false,
|
|
174
165
|
configuration: value[:configuration] || value["configuration"]
|
|
175
166
|
)
|
|
@@ -186,7 +177,7 @@ module Smplkit
|
|
|
186
177
|
(environments || {}).each_with_object({}) do |(env_key, env), out|
|
|
187
178
|
out[env_key.to_s] = SmplkitGeneratedClient::Audit::ForwarderEnvironment.new(
|
|
188
179
|
enabled: env.enabled,
|
|
189
|
-
configuration: env.configuration.nil? ? nil :
|
|
180
|
+
configuration: env.configuration.nil? ? nil : HttpConfiguration.to_wire(env.configuration)
|
|
190
181
|
)
|
|
191
182
|
end
|
|
192
183
|
end
|
|
@@ -197,20 +188,20 @@ module Smplkit
|
|
|
197
188
|
SmplkitGeneratedClient::Audit::Forwarder.new(
|
|
198
189
|
name: forwarder.name,
|
|
199
190
|
description: forwarder.description,
|
|
200
|
-
forwarder_type:
|
|
191
|
+
forwarder_type: ForwarderType.coerce(forwarder.forwarder_type),
|
|
201
192
|
forward_smplkit_events: forwarder.forward_smplkit_events,
|
|
202
193
|
environments: environments_to_wire(forwarder.environments),
|
|
203
194
|
filter: forwarder.filter,
|
|
204
|
-
transform_type:
|
|
195
|
+
transform_type: TransformType.coerce(forwarder.transform_type),
|
|
205
196
|
transform: forwarder.transform,
|
|
206
|
-
configuration:
|
|
197
|
+
configuration: HttpConfiguration.to_wire(forwarder.configuration)
|
|
207
198
|
)
|
|
208
199
|
end
|
|
209
200
|
|
|
210
201
|
def build_create_body(forwarder)
|
|
211
|
-
# Create uses the distinct ForwarderCreateRequest envelope; the
|
|
212
|
-
#
|
|
213
|
-
#
|
|
202
|
+
# Create uses the distinct ForwarderCreateRequest envelope; the audit
|
|
203
|
+
# service requires data.id (the customer-supplied key) on create and
|
|
204
|
+
# 409s on conflict.
|
|
214
205
|
resource = SmplkitGeneratedClient::Audit::ForwarderCreateResource.new(
|
|
215
206
|
id: forwarder.id.to_s,
|
|
216
207
|
type: "forwarder",
|
|
@@ -230,13 +221,19 @@ module Smplkit
|
|
|
230
221
|
end
|
|
231
222
|
end
|
|
232
223
|
|
|
233
|
-
# A single page returned from {
|
|
224
|
+
# A single page returned from {ForwardersClient#list}.
|
|
234
225
|
#
|
|
235
226
|
# @!attribute [rw] forwarders
|
|
236
227
|
# @return [Array<Smplkit::Audit::Forwarder>] Forwarders in this page.
|
|
237
228
|
# @!attribute [rw] pagination
|
|
238
229
|
# @return [Hash] +meta.pagination+ block (+:page+, +:size+, and — only when
|
|
239
230
|
# the caller passed +meta_total: true+ — +:total+ / +:total_pages+).
|
|
240
|
-
ForwarderListPage = Struct.new(:forwarders, :pagination)
|
|
231
|
+
ForwarderListPage = Struct.new(:forwarders, :pagination) do
|
|
232
|
+
include Enumerable
|
|
233
|
+
|
|
234
|
+
def each(&) = forwarders.each(&)
|
|
235
|
+
def length = forwarders.length
|
|
236
|
+
alias_method :size, :length
|
|
237
|
+
end
|
|
241
238
|
end
|
|
242
239
|
end
|
data/lib/smplkit/audit/models.rb
CHANGED
|
@@ -422,7 +422,7 @@ module Smplkit
|
|
|
422
422
|
# A SIEM streaming forwarder configured on the customer's account.
|
|
423
423
|
#
|
|
424
424
|
# Active-record style: instantiate via
|
|
425
|
-
# +
|
|
425
|
+
# +client.audit.forwarders.new(...)+, mutate fields directly,
|
|
426
426
|
# and call {#save} to persist or {#delete} to remove. Header values in
|
|
427
427
|
# +configuration.headers+ are returned redacted on reads — the GET path
|
|
428
428
|
# on the audit API replaces every header value with +"<redacted>"+.
|
|
@@ -558,6 +558,45 @@ module Smplkit
|
|
|
558
558
|
end
|
|
559
559
|
alias delete! delete
|
|
560
560
|
|
|
561
|
+
# Set this forwarder's destination configuration in memory.
|
|
562
|
+
#
|
|
563
|
+
# With +environment+ omitted, replaces the base {#configuration}. With
|
|
564
|
+
# +environment+ given, sets the per-environment override's configuration
|
|
565
|
+
# on {#environments}, creating the override entry if it doesn't exist yet
|
|
566
|
+
# (preserving any already-set +enabled+ on it). Call {#save} to persist.
|
|
567
|
+
def set_configuration(configuration, environment: nil)
|
|
568
|
+
if environment.nil?
|
|
569
|
+
@configuration = configuration
|
|
570
|
+
else
|
|
571
|
+
_environment_override(environment).configuration = configuration
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
# Set this forwarder's enablement in memory.
|
|
576
|
+
#
|
|
577
|
+
# With +environment+ omitted, sets the base {#enabled} (which the server
|
|
578
|
+
# pins false regardless — enablement is per-environment). With
|
|
579
|
+
# +environment+ given, sets the per-environment override's +enabled+ on
|
|
580
|
+
# {#environments}, creating the override entry if it doesn't exist yet
|
|
581
|
+
# (preserving any already-set +configuration+ on it). Call {#save} to
|
|
582
|
+
# persist.
|
|
583
|
+
def set_enabled(enabled, environment: nil)
|
|
584
|
+
if environment.nil?
|
|
585
|
+
@enabled = enabled
|
|
586
|
+
else
|
|
587
|
+
_environment_override(environment).enabled = enabled
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
# Return the override for +environment+, creating an empty one if absent.
|
|
592
|
+
#
|
|
593
|
+
# The per-environment mutators reach through here so an existing
|
|
594
|
+
# override's other field is preserved when only one of +enabled+ /
|
|
595
|
+
# +configuration+ is being set.
|
|
596
|
+
def _environment_override(environment)
|
|
597
|
+
@environments[environment] ||= ForwarderEnvironment.new
|
|
598
|
+
end
|
|
599
|
+
|
|
561
600
|
# @api private
|
|
562
601
|
def _apply(other)
|
|
563
602
|
@id = other.id
|