smplkit 3.0.95 → 3.0.97
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 +128 -0
- data/lib/smplkit/account/models.rb +71 -0
- data/lib/smplkit/api_support.rb +91 -0
- data/lib/smplkit/audit/buffer.rb +3 -1
- data/lib/smplkit/audit/categories.rb +21 -10
- data/lib/smplkit/audit/client.rb +18 -9
- data/lib/smplkit/audit/event_types.rb +26 -10
- data/lib/smplkit/audit/events.rb +93 -17
- data/lib/smplkit/{management/audit.rb → audit/forwarders.rb} +93 -85
- data/lib/smplkit/audit/models.rb +86 -32
- data/lib/smplkit/audit/resource_types.rb +21 -9
- data/lib/smplkit/buffers.rb +250 -0
- data/lib/smplkit/client.rb +161 -70
- data/lib/smplkit/config/client.rb +874 -186
- data/lib/smplkit/config/helpers.rb +44 -6
- data/lib/smplkit/config/models.rb +114 -7
- data/lib/smplkit/config_resolution.rb +17 -9
- data/lib/smplkit/errors.rb +14 -3
- data/lib/smplkit/flags/client.rb +602 -116
- data/lib/smplkit/flags/models.rb +110 -8
- data/lib/smplkit/flags/types.rb +8 -9
- data/lib/smplkit/jobs/client.rb +306 -0
- data/lib/smplkit/jobs/models.rb +47 -18
- data/lib/smplkit/logging/client.rb +755 -191
- data/lib/smplkit/logging/helpers.rb +5 -1
- data/lib/smplkit/logging/levels.rb +3 -1
- data/lib/smplkit/logging/models.rb +163 -6
- data/lib/smplkit/logging/normalize.rb +3 -1
- data/lib/smplkit/logging/resolution.rb +4 -4
- data/lib/smplkit/logging/sources.rb +1 -1
- data/lib/smplkit/platform/client.rb +597 -0
- data/lib/smplkit/platform/models.rb +282 -0
- data/lib/smplkit/{management → platform}/types.rb +21 -4
- data/lib/smplkit/transport.rb +103 -0
- data/lib/smplkit/ws.rb +1 -1
- 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/jobs.rb +0 -226
- data/lib/smplkit/management/models.rb +0 -178
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The Smpl Platform client — cross-cutting CRUD on +client.platform+.
|
|
4
|
+
#
|
|
5
|
+
# +PlatformClient+ groups the account-wide configuration resources that aren't
|
|
6
|
+
# owned by a single product, mirroring the product UI's Platform area:
|
|
7
|
+
#
|
|
8
|
+
# - +platform.environments+ — environment CRUD
|
|
9
|
+
# - +platform.services+ — service CRUD
|
|
10
|
+
# - +platform.contexts+ — evaluation-context registration + read/delete
|
|
11
|
+
# - +platform.context_types+ — context-type CRUD
|
|
12
|
+
#
|
|
13
|
+
# All four are pure CRUD — no +install+ gate. Every sub-client speaks to the
|
|
14
|
+
# app service, so the client needs exactly one app transport (plus the
|
|
15
|
+
# context-registration buffer that +contexts+ drains).
|
|
16
|
+
#
|
|
17
|
+
# The client supports two construction shapes:
|
|
18
|
+
#
|
|
19
|
+
# * *Wired* into +Smplkit::Client+ — borrows the parent's app transport and an
|
|
20
|
+
# externally-supplied context buffer. This is the common path;
|
|
21
|
+
# +client.flags+ borrows +client.platform.contexts+ as its evaluation-context
|
|
22
|
+
# registration seam.
|
|
23
|
+
# * *Standalone* — +PlatformClient.new(api_key: ..., base_url: ..., ...)+ builds
|
|
24
|
+
# and owns its own app transport and buffer. +close+ tears down only the
|
|
25
|
+
# owned transport.
|
|
26
|
+
module Smplkit
|
|
27
|
+
module Platform
|
|
28
|
+
# Resolve the two-arg or composite-id form to +[type, key]+.
|
|
29
|
+
#
|
|
30
|
+
# @api private
|
|
31
|
+
def self.split_context_id(id_or_type, key)
|
|
32
|
+
return [id_or_type, key] unless key.nil?
|
|
33
|
+
|
|
34
|
+
unless id_or_type.include?(":")
|
|
35
|
+
raise ArgumentError,
|
|
36
|
+
"context id must be 'type:key' (got #{id_or_type.inspect}); " \
|
|
37
|
+
"alternatively pass type and key as separate args"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
id_or_type.split(":", 2)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Build a standalone app transport from resolved config.
|
|
44
|
+
#
|
|
45
|
+
# +base_url+/+api_key+ are used directly when supplied (the path the
|
|
46
|
+
# top-level client takes after it has already resolved them); otherwise the
|
|
47
|
+
# config resolver fills in whatever is missing (+~/.smplkit+ / env vars /
|
|
48
|
+
# defaults).
|
|
49
|
+
#
|
|
50
|
+
# @api private
|
|
51
|
+
def self.app_transport(api_key:, base_url:, profile:, base_domain:, scheme:, debug:, extra_headers:)
|
|
52
|
+
cfg = ConfigResolution.resolve_client_config(
|
|
53
|
+
profile: profile, api_key: api_key, base_domain: base_domain, scheme: scheme, debug: debug
|
|
54
|
+
)
|
|
55
|
+
resolved_key = api_key.nil? ? cfg.api_key : api_key
|
|
56
|
+
merged = {}
|
|
57
|
+
merged.merge!(cfg.extra_headers || {})
|
|
58
|
+
merged.merge!(extra_headers || {})
|
|
59
|
+
tcfg = ConfigResolution::ResolvedClientConfig.new(
|
|
60
|
+
api_key: resolved_key, base_domain: cfg.base_domain, scheme: cfg.scheme,
|
|
61
|
+
debug: cfg.debug, extra_headers: merged
|
|
62
|
+
)
|
|
63
|
+
Transport.build_api_client(SmplkitGeneratedClient::App, "app", tcfg, base_url: base_url)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# -----------------------------------------------------------------------
|
|
67
|
+
# Environments
|
|
68
|
+
# -----------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
# Sync environment CRUD (+client.platform.environments+).
|
|
71
|
+
class EnvironmentsClient
|
|
72
|
+
def initialize(app_http)
|
|
73
|
+
@api = SmplkitGeneratedClient::App::EnvironmentsApi.new(app_http)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Build an unsaved +Environment+; call +.save+ to persist it.
|
|
77
|
+
#
|
|
78
|
+
# @param id [String] Stable, human-readable identifier for the
|
|
79
|
+
# environment (for example +"production"+).
|
|
80
|
+
# @param name [String] Display name shown in the Console.
|
|
81
|
+
# @param color [Color, String, nil] Accent color for the environment, as
|
|
82
|
+
# a +Color+ or a CSS hex string. Defaults to no color.
|
|
83
|
+
# @param classification [String] Whether the environment participates in
|
|
84
|
+
# the standard environment ordering. Defaults to
|
|
85
|
+
# +EnvironmentClassification::STANDARD+.
|
|
86
|
+
# @return [Environment] An unsaved environment bound to this client.
|
|
87
|
+
def new(id, name:, color: nil, classification: EnvironmentClassification::STANDARD)
|
|
88
|
+
Environment.new(self, id: id, name: name, color: color, classification: classification)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# List environments in the account.
|
|
92
|
+
#
|
|
93
|
+
# @param page_number [Integer, nil] 1-based page to fetch. Defaults to
|
|
94
|
+
# the first page.
|
|
95
|
+
# @param page_size [Integer, nil] Maximum number of environments per
|
|
96
|
+
# page. Defaults to the server's page size.
|
|
97
|
+
# @return [Array<Environment>] The environments on the requested page.
|
|
98
|
+
def list(page_number: nil, page_size: nil)
|
|
99
|
+
opts = {}
|
|
100
|
+
opts[:page_number] = page_number unless page_number.nil?
|
|
101
|
+
opts[:page_size] = page_size unless page_size.nil?
|
|
102
|
+
response = ApiSupport::ErrorMapping.call { @api.list_environments(opts) }
|
|
103
|
+
(response.data || []).map { |r| from_resource(ApiSupport::ResourceShim.from_model(r)) }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Fetch a single environment by id.
|
|
107
|
+
#
|
|
108
|
+
# @param id [String] Identifier of the environment to fetch.
|
|
109
|
+
# @return [Environment] The matching environment.
|
|
110
|
+
# @raise [Smplkit::NotFoundError] If no environment with that id exists.
|
|
111
|
+
def get(id)
|
|
112
|
+
response = ApiSupport::ErrorMapping.call { @api.get_environment(id) }
|
|
113
|
+
from_resource(ApiSupport::ResourceShim.from_model(response.data))
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Delete an environment by id.
|
|
117
|
+
#
|
|
118
|
+
# @param id [String] Identifier of the environment to delete.
|
|
119
|
+
# @return [void]
|
|
120
|
+
def delete(id)
|
|
121
|
+
ApiSupport::ErrorMapping.call { @api.delete_environment(id) }
|
|
122
|
+
nil
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @api private
|
|
126
|
+
def _create(env)
|
|
127
|
+
response = ApiSupport::ErrorMapping.call { @api.create_environment(body_for(env)) }
|
|
128
|
+
from_resource(ApiSupport::ResourceShim.from_model(response.data))
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# @api private
|
|
132
|
+
def _update(env)
|
|
133
|
+
raise "cannot update an Environment with no id" if env.id.nil?
|
|
134
|
+
|
|
135
|
+
response = ApiSupport::ErrorMapping.call { @api.update_environment(env.id, body_for(env)) }
|
|
136
|
+
from_resource(ApiSupport::ResourceShim.from_model(response.data))
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def body_for(env)
|
|
142
|
+
SmplkitGeneratedClient::App::EnvironmentRequest.new(
|
|
143
|
+
data: SmplkitGeneratedClient::App::EnvironmentResource.new(
|
|
144
|
+
type: "environment",
|
|
145
|
+
id: env.id,
|
|
146
|
+
attributes: SmplkitGeneratedClient::App::Environment.new(
|
|
147
|
+
name: env.name,
|
|
148
|
+
color: env.color&.hex,
|
|
149
|
+
classification: env.classification
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def from_resource(resource)
|
|
156
|
+
attrs = resource["attributes"] || {}
|
|
157
|
+
classification =
|
|
158
|
+
attrs["classification"] == "AD_HOC" ? EnvironmentClassification::AD_HOC : EnvironmentClassification::STANDARD
|
|
159
|
+
Environment.new(
|
|
160
|
+
self,
|
|
161
|
+
id: resource["id"], name: attrs["name"], color: attrs["color"],
|
|
162
|
+
classification: classification,
|
|
163
|
+
created_at: attrs["created_at"], updated_at: attrs["updated_at"]
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# -----------------------------------------------------------------------
|
|
169
|
+
# Services
|
|
170
|
+
# -----------------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
# Sync service CRUD (+client.platform.services+).
|
|
173
|
+
class ServicesClient
|
|
174
|
+
def initialize(app_http)
|
|
175
|
+
@api = SmplkitGeneratedClient::App::ServicesApi.new(app_http)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Build an unsaved +Service+; call +.save+ to persist it.
|
|
179
|
+
#
|
|
180
|
+
# @param id [String] Stable, human-readable identifier for the service.
|
|
181
|
+
# @param name [String] Display name shown in the Console.
|
|
182
|
+
# @return [Service] An unsaved service bound to this client.
|
|
183
|
+
def new(id, name:)
|
|
184
|
+
Service.new(self, id: id, name: name)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# List services in the account.
|
|
188
|
+
#
|
|
189
|
+
# @param page_number [Integer, nil] 1-based page to fetch. Defaults to
|
|
190
|
+
# the first page.
|
|
191
|
+
# @param page_size [Integer, nil] Maximum number of services per page.
|
|
192
|
+
# Defaults to the server's page size.
|
|
193
|
+
# @return [Array<Service>] The services on the requested page.
|
|
194
|
+
def list(page_number: nil, page_size: nil)
|
|
195
|
+
opts = {}
|
|
196
|
+
opts[:page_number] = page_number unless page_number.nil?
|
|
197
|
+
opts[:page_size] = page_size unless page_size.nil?
|
|
198
|
+
response = ApiSupport::ErrorMapping.call { @api.list_services(opts) }
|
|
199
|
+
(response.data || []).map { |r| from_resource(ApiSupport::ResourceShim.from_model(r)) }
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Fetch a single service by id.
|
|
203
|
+
#
|
|
204
|
+
# @param id [String] Identifier of the service to fetch.
|
|
205
|
+
# @return [Service] The matching service.
|
|
206
|
+
# @raise [Smplkit::NotFoundError] If no service with that id exists.
|
|
207
|
+
def get(id)
|
|
208
|
+
response = ApiSupport::ErrorMapping.call { @api.get_service(id) }
|
|
209
|
+
from_resource(ApiSupport::ResourceShim.from_model(response.data))
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Delete a service by id.
|
|
213
|
+
#
|
|
214
|
+
# @param id [String] Identifier of the service to delete.
|
|
215
|
+
# @return [void]
|
|
216
|
+
def delete(id)
|
|
217
|
+
ApiSupport::ErrorMapping.call { @api.delete_service(id) }
|
|
218
|
+
nil
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# @api private
|
|
222
|
+
def _create(svc)
|
|
223
|
+
response = ApiSupport::ErrorMapping.call { @api.create_service(create_body_for(svc)) }
|
|
224
|
+
from_resource(ApiSupport::ResourceShim.from_model(response.data))
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# @api private
|
|
228
|
+
def _update(svc)
|
|
229
|
+
raise "cannot update a Service with no id" if svc.id.nil?
|
|
230
|
+
|
|
231
|
+
response = ApiSupport::ErrorMapping.call { @api.update_service(svc.id, body_for(svc)) }
|
|
232
|
+
from_resource(ApiSupport::ResourceShim.from_model(response.data))
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
private
|
|
236
|
+
|
|
237
|
+
def body_for(svc)
|
|
238
|
+
SmplkitGeneratedClient::App::ServiceRequest.new(
|
|
239
|
+
data: SmplkitGeneratedClient::App::ServiceResource.new(
|
|
240
|
+
type: "service",
|
|
241
|
+
id: svc.id,
|
|
242
|
+
attributes: SmplkitGeneratedClient::App::Service.new(name: svc.name)
|
|
243
|
+
)
|
|
244
|
+
)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def create_body_for(svc)
|
|
248
|
+
SmplkitGeneratedClient::App::ServiceCreateRequest.new(
|
|
249
|
+
data: SmplkitGeneratedClient::App::ServiceCreateResource.new(
|
|
250
|
+
type: "service",
|
|
251
|
+
id: svc.id,
|
|
252
|
+
attributes: SmplkitGeneratedClient::App::Service.new(name: svc.name)
|
|
253
|
+
)
|
|
254
|
+
)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def from_resource(resource)
|
|
258
|
+
attrs = resource["attributes"] || {}
|
|
259
|
+
Service.new(
|
|
260
|
+
self,
|
|
261
|
+
id: resource["id"], name: attrs["name"],
|
|
262
|
+
created_at: attrs["created_at"], updated_at: attrs["updated_at"]
|
|
263
|
+
)
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# -----------------------------------------------------------------------
|
|
268
|
+
# Context Types
|
|
269
|
+
# -----------------------------------------------------------------------
|
|
270
|
+
|
|
271
|
+
# Sync context-type CRUD (+client.platform.context_types+).
|
|
272
|
+
class ContextTypesClient
|
|
273
|
+
def initialize(app_http)
|
|
274
|
+
@api = SmplkitGeneratedClient::App::ContextTypesApi.new(app_http)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Build an unsaved +ContextType+; call +.save+ to persist it.
|
|
278
|
+
#
|
|
279
|
+
# @param id [String] Stable, human-readable identifier for the context
|
|
280
|
+
# type (for example +"user"+).
|
|
281
|
+
# @param name [String, nil] Display name shown in the Console. Defaults
|
|
282
|
+
# to +id+ when omitted.
|
|
283
|
+
# @param attributes [Hash, nil] Known-attribute slots, keyed by attribute
|
|
284
|
+
# name, with a metadata dict per slot. Defaults to no declared
|
|
285
|
+
# attributes.
|
|
286
|
+
# @return [ContextType] An unsaved context type bound to this client.
|
|
287
|
+
def new(id, name: nil, attributes: nil)
|
|
288
|
+
ContextType.new(self, id: id, name: name || id, attributes: attributes || {})
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# List context types in the account.
|
|
292
|
+
#
|
|
293
|
+
# @param page_number [Integer, nil] 1-based page to fetch. Defaults to
|
|
294
|
+
# the first page.
|
|
295
|
+
# @param page_size [Integer, nil] Maximum number of context types per
|
|
296
|
+
# page. Defaults to the server's page size.
|
|
297
|
+
# @return [Array<ContextType>] The context types on the requested page.
|
|
298
|
+
def list(page_number: nil, page_size: nil)
|
|
299
|
+
opts = {}
|
|
300
|
+
opts[:page_number] = page_number unless page_number.nil?
|
|
301
|
+
opts[:page_size] = page_size unless page_size.nil?
|
|
302
|
+
response = ApiSupport::ErrorMapping.call { @api.list_context_types(opts) }
|
|
303
|
+
(response.data || []).map { |r| from_resource(ApiSupport::ResourceShim.from_model(r)) }
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Fetch a single context type by id.
|
|
307
|
+
#
|
|
308
|
+
# @param id [String] Identifier of the context type to fetch.
|
|
309
|
+
# @return [ContextType] The matching context type.
|
|
310
|
+
# @raise [Smplkit::NotFoundError] If no context type with that id exists.
|
|
311
|
+
def get(id)
|
|
312
|
+
response = ApiSupport::ErrorMapping.call { @api.get_context_type(id) }
|
|
313
|
+
from_resource(ApiSupport::ResourceShim.from_model(response.data))
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Delete a context type by id.
|
|
317
|
+
#
|
|
318
|
+
# @param id [String] Identifier of the context type to delete.
|
|
319
|
+
# @return [void]
|
|
320
|
+
def delete(id)
|
|
321
|
+
ApiSupport::ErrorMapping.call { @api.delete_context_type(id) }
|
|
322
|
+
nil
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# @api private
|
|
326
|
+
def _create(ct)
|
|
327
|
+
response = ApiSupport::ErrorMapping.call { @api.create_context_type(body_for(ct)) }
|
|
328
|
+
from_resource(ApiSupport::ResourceShim.from_model(response.data))
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# @api private
|
|
332
|
+
def _update(ct)
|
|
333
|
+
raise "cannot update a ContextType with no id" if ct.id.nil?
|
|
334
|
+
|
|
335
|
+
response = ApiSupport::ErrorMapping.call { @api.update_context_type(ct.id, body_for(ct)) }
|
|
336
|
+
from_resource(ApiSupport::ResourceShim.from_model(response.data))
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
private
|
|
340
|
+
|
|
341
|
+
def body_for(ct)
|
|
342
|
+
SmplkitGeneratedClient::App::ContextTypeRequest.new(
|
|
343
|
+
data: SmplkitGeneratedClient::App::ContextTypeResource.new(
|
|
344
|
+
type: "context_type",
|
|
345
|
+
id: ct.id,
|
|
346
|
+
attributes: SmplkitGeneratedClient::App::ContextType.new(name: ct.name, attributes: ct.attributes)
|
|
347
|
+
)
|
|
348
|
+
)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def from_resource(resource)
|
|
352
|
+
attrs = resource["attributes"] || {}
|
|
353
|
+
raw_meta = attrs["attributes"]
|
|
354
|
+
attribute_metadata = raw_meta.is_a?(Hash) ? raw_meta : {}
|
|
355
|
+
ContextType.new(
|
|
356
|
+
self,
|
|
357
|
+
id: resource["id"], name: attrs["name"], attributes: attribute_metadata,
|
|
358
|
+
created_at: attrs["created_at"], updated_at: attrs["updated_at"]
|
|
359
|
+
)
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# -----------------------------------------------------------------------
|
|
364
|
+
# Contexts
|
|
365
|
+
# -----------------------------------------------------------------------
|
|
366
|
+
|
|
367
|
+
# Sync context registration + read/delete (+client.platform.contexts+).
|
|
368
|
+
class ContextsClient
|
|
369
|
+
def initialize(app_http, buffer)
|
|
370
|
+
@api = SmplkitGeneratedClient::App::ContextsApi.new(app_http)
|
|
371
|
+
@buffer = buffer
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# Buffer one or more contexts for registration.
|
|
375
|
+
#
|
|
376
|
+
# Buffered contexts are sent in batches: a background flush kicks in once
|
|
377
|
+
# enough have accumulated, and any remainder is sent on the next explicit
|
|
378
|
+
# flush. Pass +flush: true+ to send everything buffered right away.
|
|
379
|
+
#
|
|
380
|
+
# @param items [Context, Array<Context>] A single context or a list of
|
|
381
|
+
# contexts to register.
|
|
382
|
+
# @param flush [Boolean] When +true+, send all buffered contexts
|
|
383
|
+
# immediately rather than waiting for the batch threshold. Defaults to
|
|
384
|
+
# +false+.
|
|
385
|
+
# @return [void]
|
|
386
|
+
def register(items, flush: false)
|
|
387
|
+
batch = items.is_a?(Array) ? items : [items]
|
|
388
|
+
@buffer.observe(batch)
|
|
389
|
+
if flush
|
|
390
|
+
self.flush
|
|
391
|
+
return
|
|
392
|
+
end
|
|
393
|
+
return unless @buffer.pending_count >= CONTEXT_BATCH_FLUSH_SIZE
|
|
394
|
+
|
|
395
|
+
Thread.new { threshold_flush }
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# Send any pending observations to the server.
|
|
399
|
+
#
|
|
400
|
+
# @return [void]
|
|
401
|
+
def flush
|
|
402
|
+
batch = @buffer.drain
|
|
403
|
+
return if batch.empty?
|
|
404
|
+
|
|
405
|
+
body = build_bulk_register_body(batch)
|
|
406
|
+
ApiSupport::ErrorMapping.call { @api.bulk_register_contexts(body) }
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Number of observations queued and awaiting flush.
|
|
410
|
+
#
|
|
411
|
+
# @return [Integer] The count of observations pending flush.
|
|
412
|
+
def pending_count
|
|
413
|
+
@buffer.pending_count
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# List all contexts of a given type.
|
|
417
|
+
#
|
|
418
|
+
# @param type [String] Context type to list (for example +"user"+).
|
|
419
|
+
# @param page_number [Integer, nil] 1-based page to fetch. Defaults to
|
|
420
|
+
# the first page.
|
|
421
|
+
# @param page_size [Integer, nil] Maximum number of contexts per page.
|
|
422
|
+
# Defaults to the server's page size.
|
|
423
|
+
# @return [Array<Context>] The contexts of the given type on the
|
|
424
|
+
# requested page.
|
|
425
|
+
def list(type, page_number: nil, page_size: nil)
|
|
426
|
+
opts = { filter_context_type: type }
|
|
427
|
+
opts[:page_number] = page_number unless page_number.nil?
|
|
428
|
+
opts[:page_size] = page_size unless page_size.nil?
|
|
429
|
+
response = ApiSupport::ErrorMapping.call { @api.list_contexts(opts) }
|
|
430
|
+
(response.data || []).map { |r| context_from_resource(ApiSupport::ResourceShim.from_model(r)) }
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
# Fetch a single context, identified by composite id or by type and key.
|
|
434
|
+
#
|
|
435
|
+
# @param id_or_type [String] Either the composite context id +"type:key"+
|
|
436
|
+
# (when +key+ is omitted) or just the context type (when +key+ is
|
|
437
|
+
# supplied).
|
|
438
|
+
# @param key [String, nil] The context key. Provide it to use the
|
|
439
|
+
# two-argument form; omit it when +id_or_type+ already carries the
|
|
440
|
+
# composite id.
|
|
441
|
+
# @return [Context] The matching context.
|
|
442
|
+
# @raise [Smplkit::NotFoundError] If no context with that id exists.
|
|
443
|
+
def get(id_or_type, key = nil)
|
|
444
|
+
ctx_type, ctx_key = Platform.split_context_id(id_or_type, key)
|
|
445
|
+
response = ApiSupport::ErrorMapping.call { @api.get_context("#{ctx_type}:#{ctx_key}") }
|
|
446
|
+
context_from_resource(ApiSupport::ResourceShim.from_model(response.data))
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# Delete a single context, identified by composite id or by type and key.
|
|
450
|
+
#
|
|
451
|
+
# @param id_or_type [String] Either the composite context id +"type:key"+
|
|
452
|
+
# (when +key+ is omitted) or just the context type (when +key+ is
|
|
453
|
+
# supplied).
|
|
454
|
+
# @param key [String, nil] The context key. Provide it to use the
|
|
455
|
+
# two-argument form; omit it when +id_or_type+ already carries the
|
|
456
|
+
# composite id.
|
|
457
|
+
# @return [void]
|
|
458
|
+
def delete(id_or_type, key = nil)
|
|
459
|
+
ctx_type, ctx_key = Platform.split_context_id(id_or_type, key)
|
|
460
|
+
ApiSupport::ErrorMapping.call { @api.delete_context("#{ctx_type}:#{ctx_key}") }
|
|
461
|
+
nil
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
# @api private
|
|
465
|
+
def _save_context(ctx)
|
|
466
|
+
body = ctx_to_resource(ctx)
|
|
467
|
+
response = ApiSupport::ErrorMapping.call { @api.update_context(ctx.id, body) }
|
|
468
|
+
context_from_resource(ApiSupport::ResourceShim.from_model(response.data))
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
private
|
|
472
|
+
|
|
473
|
+
def threshold_flush
|
|
474
|
+
flush
|
|
475
|
+
rescue StandardError => e
|
|
476
|
+
Smplkit.debug("registration", "context registration flush failed: #{e.class}: #{e.message}")
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def build_bulk_register_body(items)
|
|
480
|
+
bulk = items.map do |item|
|
|
481
|
+
SmplkitGeneratedClient::App::ContextBulkItem.new(
|
|
482
|
+
type: item["type"], key: item["key"], attributes: item["attributes"] || {}
|
|
483
|
+
)
|
|
484
|
+
end
|
|
485
|
+
SmplkitGeneratedClient::App::ContextBulkRegister.new(contexts: bulk)
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def ctx_to_resource(ctx)
|
|
489
|
+
SmplkitGeneratedClient::App::ContextResponse.new(
|
|
490
|
+
data: SmplkitGeneratedClient::App::ContextResource.new(
|
|
491
|
+
type: "context",
|
|
492
|
+
id: ctx.id,
|
|
493
|
+
attributes: SmplkitGeneratedClient::App::Context.new(
|
|
494
|
+
name: ctx.name, context_type: ctx.type, attributes: ctx.attributes
|
|
495
|
+
)
|
|
496
|
+
)
|
|
497
|
+
)
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
def context_from_resource(resource)
|
|
501
|
+
attrs = resource["attributes"] || {}
|
|
502
|
+
Smplkit::Context.new(
|
|
503
|
+
attrs["context_type"] || attrs["type"] || resource["id"].to_s.split(":").first,
|
|
504
|
+
attrs["key"] || resource["id"].to_s.split(":", 2).last,
|
|
505
|
+
attrs["attributes"] || {},
|
|
506
|
+
name: attrs["name"],
|
|
507
|
+
created_at: attrs["created_at"],
|
|
508
|
+
updated_at: attrs["updated_at"]
|
|
509
|
+
)._bind_client(self)
|
|
510
|
+
end
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
# -----------------------------------------------------------------------
|
|
514
|
+
# PlatformClient (client.platform)
|
|
515
|
+
# -----------------------------------------------------------------------
|
|
516
|
+
|
|
517
|
+
# The Smpl Platform client (sync).
|
|
518
|
+
#
|
|
519
|
+
# Groups the account-wide CRUD resources that aren't owned by a single
|
|
520
|
+
# product, reachable as +client.platform+ (+Smplkit::Client+) or
|
|
521
|
+
# constructed directly:
|
|
522
|
+
#
|
|
523
|
+
# platform = Smplkit::PlatformClient.new(api_key: "sk_...")
|
|
524
|
+
# prod = platform.environments.new("production", name: "Production")
|
|
525
|
+
# prod.save
|
|
526
|
+
# platform.services.list.each { |svc| ... }
|
|
527
|
+
#
|
|
528
|
+
# Sub-clients: +environments+, +services+, +contexts+, +context_types+.
|
|
529
|
+
# Pure CRUD — no +install+ required.
|
|
530
|
+
#
|
|
531
|
+
# @param api_key [String, nil] API key. When omitted, resolved from
|
|
532
|
+
# +SMPLKIT_API_KEY+ or +~/.smplkit+.
|
|
533
|
+
# @param base_url [String, nil] Full app-service base URL. Usually resolved
|
|
534
|
+
# from +base_domain+/+scheme+; supplied directly by the top-level clients
|
|
535
|
+
# which have already computed it.
|
|
536
|
+
# @param profile [String, nil] Named +~/.smplkit+ profile section.
|
|
537
|
+
# @param base_domain [String, nil] Base domain for API requests (default
|
|
538
|
+
# +"smplkit.com"+).
|
|
539
|
+
# @param scheme [String, nil] URL scheme (default +"https"+).
|
|
540
|
+
# @param debug [Boolean, nil] Enable SDK debug logging.
|
|
541
|
+
# @param extra_headers [Hash, nil] Extra headers attached to every request.
|
|
542
|
+
# @param app_transport Internal — a pre-built app transport supplied by a
|
|
543
|
+
# top-level client so the platform surface shares one connection pool.
|
|
544
|
+
# Not for direct use.
|
|
545
|
+
# @param context_buffer Internal — the shared context-registration buffer.
|
|
546
|
+
# Not for direct use.
|
|
547
|
+
class PlatformClient
|
|
548
|
+
attr_reader :environments, :services, :contexts, :context_types
|
|
549
|
+
|
|
550
|
+
def initialize(api_key: nil, base_url: nil, profile: nil, base_domain: nil,
|
|
551
|
+
scheme: nil, debug: nil, extra_headers: nil,
|
|
552
|
+
app_transport: nil, context_buffer: nil)
|
|
553
|
+
if app_transport.nil?
|
|
554
|
+
@app_http = Platform.app_transport(
|
|
555
|
+
api_key: api_key, base_url: base_url, profile: profile,
|
|
556
|
+
base_domain: base_domain, scheme: scheme, debug: debug, extra_headers: extra_headers
|
|
557
|
+
)
|
|
558
|
+
@owns_transport = true
|
|
559
|
+
else
|
|
560
|
+
@app_http = app_transport
|
|
561
|
+
@owns_transport = false
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
buffer = context_buffer || ContextRegistrationBuffer.new
|
|
565
|
+
@context_buffer = buffer
|
|
566
|
+
|
|
567
|
+
@environments = EnvironmentsClient.new(@app_http)
|
|
568
|
+
@services = ServicesClient.new(@app_http)
|
|
569
|
+
@contexts = ContextsClient.new(@app_http, buffer)
|
|
570
|
+
@context_types = ContextTypesClient.new(@app_http)
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
# Close the app transport — only when this client owns it.
|
|
574
|
+
#
|
|
575
|
+
# A wired client borrows the parent's app transport and closes nothing.
|
|
576
|
+
def close
|
|
577
|
+
return unless @owns_transport
|
|
578
|
+
|
|
579
|
+
# The generated ApiClient owns Faraday connections that release on GC;
|
|
580
|
+
# there is no explicit shutdown to call.
|
|
581
|
+
nil
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
# Construct, yield to the block, and close on exit.
|
|
585
|
+
def self.open(**kwargs)
|
|
586
|
+
client = new(**kwargs)
|
|
587
|
+
begin
|
|
588
|
+
yield client
|
|
589
|
+
ensure
|
|
590
|
+
client.close
|
|
591
|
+
end
|
|
592
|
+
end
|
|
593
|
+
end
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
PlatformClient = Platform::PlatformClient
|
|
597
|
+
end
|