launchdarkly-server-sdk 8.11.2 → 8.11.3
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/ldclient-rb/config.rb +66 -3
- data/lib/ldclient-rb/context.rb +1 -1
- data/lib/ldclient-rb/data_system.rb +243 -0
- data/lib/ldclient-rb/events.rb +34 -19
- data/lib/ldclient-rb/flags_state.rb +1 -1
- data/lib/ldclient-rb/impl/big_segments.rb +4 -4
- data/lib/ldclient-rb/impl/cache_store.rb +44 -0
- data/lib/ldclient-rb/impl/data_source/polling.rb +108 -0
- data/lib/ldclient-rb/impl/data_source/requestor.rb +106 -0
- data/lib/ldclient-rb/impl/data_source/status_provider.rb +78 -0
- data/lib/ldclient-rb/impl/data_source/stream.rb +198 -0
- data/lib/ldclient-rb/impl/data_source.rb +3 -3
- data/lib/ldclient-rb/impl/data_store/data_kind.rb +108 -0
- data/lib/ldclient-rb/impl/data_store/feature_store_client_wrapper.rb +187 -0
- data/lib/ldclient-rb/impl/data_store/in_memory_feature_store.rb +130 -0
- data/lib/ldclient-rb/impl/data_store/status_provider.rb +82 -0
- data/lib/ldclient-rb/impl/data_store/store.rb +371 -0
- data/lib/ldclient-rb/impl/data_store.rb +11 -97
- data/lib/ldclient-rb/impl/data_system/fdv1.rb +20 -7
- data/lib/ldclient-rb/impl/data_system/fdv2.rb +471 -0
- data/lib/ldclient-rb/impl/data_system/polling.rb +601 -0
- data/lib/ldclient-rb/impl/data_system/protocolv2.rb +264 -0
- data/lib/ldclient-rb/impl/dependency_tracker.rb +21 -9
- data/lib/ldclient-rb/impl/evaluator.rb +3 -2
- data/lib/ldclient-rb/impl/event_sender.rb +4 -3
- data/lib/ldclient-rb/impl/expiring_cache.rb +79 -0
- data/lib/ldclient-rb/impl/integrations/file_data_source.rb +8 -8
- data/lib/ldclient-rb/impl/integrations/test_data/test_data_source_v2.rb +288 -0
- data/lib/ldclient-rb/impl/memoized_value.rb +34 -0
- data/lib/ldclient-rb/impl/migrations/migrator.rb +2 -1
- data/lib/ldclient-rb/impl/migrations/tracker.rb +2 -1
- data/lib/ldclient-rb/impl/model/serialization.rb +6 -6
- data/lib/ldclient-rb/impl/non_blocking_thread_pool.rb +48 -0
- data/lib/ldclient-rb/impl/repeating_task.rb +2 -2
- data/lib/ldclient-rb/impl/simple_lru_cache.rb +27 -0
- data/lib/ldclient-rb/impl/util.rb +65 -0
- data/lib/ldclient-rb/impl.rb +1 -2
- data/lib/ldclient-rb/in_memory_store.rb +1 -18
- data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +9 -9
- data/lib/ldclient-rb/integrations/test_data.rb +11 -11
- data/lib/ldclient-rb/integrations/test_data_v2/flag_builder_v2.rb +582 -0
- data/lib/ldclient-rb/integrations/test_data_v2.rb +248 -0
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +3 -2
- data/lib/ldclient-rb/interfaces/data_system.rb +755 -0
- data/lib/ldclient-rb/interfaces/feature_store.rb +3 -0
- data/lib/ldclient-rb/ldclient.rb +55 -131
- data/lib/ldclient-rb/util.rb +11 -70
- data/lib/ldclient-rb/version.rb +1 -1
- data/lib/ldclient-rb.rb +8 -17
- metadata +35 -17
- data/lib/ldclient-rb/cache_store.rb +0 -45
- data/lib/ldclient-rb/expiring_cache.rb +0 -77
- data/lib/ldclient-rb/memoized_value.rb +0 -32
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +0 -46
- data/lib/ldclient-rb/polling.rb +0 -102
- data/lib/ldclient-rb/requestor.rb +0 -102
- data/lib/ldclient-rb/simple_lru_cache.rb +0 -25
- data/lib/ldclient-rb/stream.rb +0 -197
|
@@ -0,0 +1,755 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LaunchDarkly
|
|
4
|
+
module Interfaces
|
|
5
|
+
module DataSystem
|
|
6
|
+
#
|
|
7
|
+
# EventName represents the name of an event that can be sent by the server for FDv2.
|
|
8
|
+
#
|
|
9
|
+
# This type is not stable, and not subject to any backwards
|
|
10
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
11
|
+
#
|
|
12
|
+
# Do not use it.
|
|
13
|
+
# You have been warned.
|
|
14
|
+
#
|
|
15
|
+
module EventName
|
|
16
|
+
# Specifies that an object should be added to the data set with upsert semantics.
|
|
17
|
+
PUT_OBJECT = "put-object"
|
|
18
|
+
|
|
19
|
+
# Specifies that an object should be removed from the data set.
|
|
20
|
+
DELETE_OBJECT = "delete-object"
|
|
21
|
+
|
|
22
|
+
# Specifies the server's intent.
|
|
23
|
+
SERVER_INTENT = "server-intent"
|
|
24
|
+
|
|
25
|
+
# Specifies that all data required to bring the existing data set to a new version has been transferred.
|
|
26
|
+
PAYLOAD_TRANSFERRED = "payload-transferred"
|
|
27
|
+
|
|
28
|
+
# Keeps the connection alive.
|
|
29
|
+
HEARTBEAT = "heart-beat"
|
|
30
|
+
|
|
31
|
+
# Specifies that the server is about to close the connection.
|
|
32
|
+
GOODBYE = "goodbye"
|
|
33
|
+
|
|
34
|
+
# Specifies that an error occurred while serving the connection.
|
|
35
|
+
ERROR = "error"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#
|
|
39
|
+
# ObjectKind represents the kind of object.
|
|
40
|
+
#
|
|
41
|
+
# This type is not stable, and not subject to any backwards
|
|
42
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
43
|
+
#
|
|
44
|
+
# Do not use it.
|
|
45
|
+
# You have been warned.
|
|
46
|
+
#
|
|
47
|
+
module ObjectKind
|
|
48
|
+
# Represents a feature flag.
|
|
49
|
+
FLAG = "flag"
|
|
50
|
+
|
|
51
|
+
# Represents a segment.
|
|
52
|
+
SEGMENT = "segment"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
#
|
|
56
|
+
# ChangeType specifies if an object is being upserted or deleted.
|
|
57
|
+
#
|
|
58
|
+
# This type is not stable, and not subject to any backwards
|
|
59
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
60
|
+
#
|
|
61
|
+
# Do not use it.
|
|
62
|
+
# You have been warned.
|
|
63
|
+
#
|
|
64
|
+
module ChangeType
|
|
65
|
+
# Represents an object being upserted.
|
|
66
|
+
PUT = "put"
|
|
67
|
+
|
|
68
|
+
# Represents an object being deleted.
|
|
69
|
+
DELETE = "delete"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
#
|
|
73
|
+
# IntentCode represents the various intents that can be sent by the server.
|
|
74
|
+
#
|
|
75
|
+
# This type is not stable, and not subject to any backwards
|
|
76
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
77
|
+
#
|
|
78
|
+
# Do not use it.
|
|
79
|
+
# You have been warned.
|
|
80
|
+
#
|
|
81
|
+
module IntentCode
|
|
82
|
+
# The server intends to send a full data set.
|
|
83
|
+
TRANSFER_FULL = "xfer-full"
|
|
84
|
+
|
|
85
|
+
# The server intends to send only the necessary changes to bring an existing data set up-to-date.
|
|
86
|
+
TRANSFER_CHANGES = "xfer-changes"
|
|
87
|
+
|
|
88
|
+
# The server intends to send no data (payload is up to date).
|
|
89
|
+
TRANSFER_NONE = "none"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
#
|
|
93
|
+
# DataStoreMode represents the mode of operation of a Data Store in FDV2 mode.
|
|
94
|
+
#
|
|
95
|
+
# This type is not stable, and not subject to any backwards
|
|
96
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
97
|
+
#
|
|
98
|
+
# Do not use it.
|
|
99
|
+
# You have been warned.
|
|
100
|
+
#
|
|
101
|
+
module DataStoreMode
|
|
102
|
+
# Indicates that the data store is read-only. Data will never be written back to the store by the SDK.
|
|
103
|
+
READ_ONLY = :read_only
|
|
104
|
+
|
|
105
|
+
# Indicates that the data store is read-write. Data from initializers/synchronizers may be written
|
|
106
|
+
# to the store as necessary.
|
|
107
|
+
READ_WRITE = :read_write
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
#
|
|
111
|
+
# Selector represents a particular snapshot of data.
|
|
112
|
+
#
|
|
113
|
+
# This type is not stable, and not subject to any backwards
|
|
114
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
115
|
+
#
|
|
116
|
+
# Do not use it.
|
|
117
|
+
# You have been warned.
|
|
118
|
+
#
|
|
119
|
+
class Selector
|
|
120
|
+
# @return [String] The state
|
|
121
|
+
attr_reader :state
|
|
122
|
+
|
|
123
|
+
# @return [Integer] The version
|
|
124
|
+
attr_reader :version
|
|
125
|
+
|
|
126
|
+
#
|
|
127
|
+
# @param state [String] The state
|
|
128
|
+
# @param version [Integer] The version
|
|
129
|
+
#
|
|
130
|
+
def initialize(state: "", version: 0)
|
|
131
|
+
@state = state
|
|
132
|
+
@version = version
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
#
|
|
136
|
+
# Returns an empty Selector.
|
|
137
|
+
#
|
|
138
|
+
# @return [Selector]
|
|
139
|
+
#
|
|
140
|
+
def self.no_selector
|
|
141
|
+
Selector.new
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
#
|
|
145
|
+
# Returns true if the Selector has a value.
|
|
146
|
+
#
|
|
147
|
+
# @return [Boolean]
|
|
148
|
+
#
|
|
149
|
+
def defined?
|
|
150
|
+
self != Selector.no_selector
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
#
|
|
154
|
+
# Returns the event name for payload transfer.
|
|
155
|
+
#
|
|
156
|
+
# @return [String]
|
|
157
|
+
#
|
|
158
|
+
def name
|
|
159
|
+
EventName::PAYLOAD_TRANSFERRED
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
#
|
|
163
|
+
# Creates a new Selector from a state string and version.
|
|
164
|
+
#
|
|
165
|
+
# @param state [String] The state
|
|
166
|
+
# @param version [Integer] The version
|
|
167
|
+
# @return [Selector]
|
|
168
|
+
#
|
|
169
|
+
def self.new_selector(state, version)
|
|
170
|
+
Selector.new(state: state, version: version)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
#
|
|
174
|
+
# Serializes the Selector to a Hash.
|
|
175
|
+
#
|
|
176
|
+
# @return [Hash]
|
|
177
|
+
#
|
|
178
|
+
def to_h
|
|
179
|
+
{
|
|
180
|
+
state: @state,
|
|
181
|
+
version: @version,
|
|
182
|
+
}
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
#
|
|
186
|
+
# Deserializes a Selector from a Hash.
|
|
187
|
+
#
|
|
188
|
+
# @param data [Hash] The hash representation
|
|
189
|
+
# @return [Selector]
|
|
190
|
+
# @raise [ArgumentError] if required fields are missing
|
|
191
|
+
#
|
|
192
|
+
def self.from_h(data)
|
|
193
|
+
state = data['state'] || data[:state]
|
|
194
|
+
version = data['version'] || data[:version]
|
|
195
|
+
|
|
196
|
+
raise ArgumentError, "Missing required fields in Selector" if state.nil? || version.nil?
|
|
197
|
+
|
|
198
|
+
Selector.new(state: state, version: version)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def ==(other)
|
|
202
|
+
other.is_a?(Selector) && @state == other.state && @version == other.version
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def eql?(other)
|
|
206
|
+
self == other
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def hash
|
|
210
|
+
[@state, @version].hash
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
#
|
|
215
|
+
# Change represents a change to a piece of data, such as an update or deletion.
|
|
216
|
+
#
|
|
217
|
+
# This type is not stable, and not subject to any backwards
|
|
218
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
219
|
+
#
|
|
220
|
+
# Do not use it.
|
|
221
|
+
# You have been warned.
|
|
222
|
+
#
|
|
223
|
+
class Change
|
|
224
|
+
# @return [String] The action ({ChangeType})
|
|
225
|
+
attr_reader :action
|
|
226
|
+
|
|
227
|
+
# @return [String] The kind ({ObjectKind})
|
|
228
|
+
attr_reader :kind
|
|
229
|
+
|
|
230
|
+
# @return [String] The key
|
|
231
|
+
attr_reader :key
|
|
232
|
+
|
|
233
|
+
# @return [Integer] The version
|
|
234
|
+
attr_reader :version
|
|
235
|
+
|
|
236
|
+
# @return [Hash, nil] The object data (for PUT actions)
|
|
237
|
+
attr_reader :object
|
|
238
|
+
|
|
239
|
+
#
|
|
240
|
+
# @param action [String] The action type ({ChangeType})
|
|
241
|
+
# @param kind [String] The object kind ({ObjectKind})
|
|
242
|
+
# @param key [String] The key
|
|
243
|
+
# @param version [Integer] The version
|
|
244
|
+
# @param object [Hash, nil] The object data
|
|
245
|
+
#
|
|
246
|
+
def initialize(action:, kind:, key:, version:, object: nil)
|
|
247
|
+
@action = action
|
|
248
|
+
@kind = kind
|
|
249
|
+
@key = key
|
|
250
|
+
@version = version
|
|
251
|
+
@object = object
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
#
|
|
256
|
+
# ChangeSet represents a list of changes to be applied.
|
|
257
|
+
#
|
|
258
|
+
# This type is not stable, and not subject to any backwards
|
|
259
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
260
|
+
#
|
|
261
|
+
# Do not use it.
|
|
262
|
+
# You have been warned.
|
|
263
|
+
#
|
|
264
|
+
class ChangeSet
|
|
265
|
+
# @return [String] The intent code ({IntentCode})
|
|
266
|
+
attr_reader :intent_code
|
|
267
|
+
|
|
268
|
+
# @return [Array<Change>] The changes
|
|
269
|
+
attr_reader :changes
|
|
270
|
+
|
|
271
|
+
# @return [Selector, nil] The selector
|
|
272
|
+
attr_reader :selector
|
|
273
|
+
|
|
274
|
+
#
|
|
275
|
+
# @param intent_code [String] The intent code ({IntentCode})
|
|
276
|
+
# @param changes [Array<Change>] The changes
|
|
277
|
+
# @param selector [Selector, nil] The selector
|
|
278
|
+
#
|
|
279
|
+
def initialize(intent_code:, changes:, selector:)
|
|
280
|
+
@intent_code = intent_code
|
|
281
|
+
@changes = changes
|
|
282
|
+
@selector = selector
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
#
|
|
287
|
+
# Basis represents the initial payload of data that a data source can provide.
|
|
288
|
+
#
|
|
289
|
+
# This type is not stable, and not subject to any backwards
|
|
290
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
291
|
+
#
|
|
292
|
+
# Do not use it.
|
|
293
|
+
# You have been warned.
|
|
294
|
+
#
|
|
295
|
+
class Basis
|
|
296
|
+
# @return [ChangeSet] The change set
|
|
297
|
+
attr_reader :change_set
|
|
298
|
+
|
|
299
|
+
# @return [Boolean] Whether to persist
|
|
300
|
+
attr_reader :persist
|
|
301
|
+
|
|
302
|
+
# @return [String, nil] The environment ID
|
|
303
|
+
attr_reader :environment_id
|
|
304
|
+
|
|
305
|
+
#
|
|
306
|
+
# @param change_set [ChangeSet] The change set
|
|
307
|
+
# @param persist [Boolean] Whether to persist
|
|
308
|
+
# @param environment_id [String, nil] The environment ID
|
|
309
|
+
#
|
|
310
|
+
def initialize(change_set:, persist:, environment_id: nil)
|
|
311
|
+
@change_set = change_set
|
|
312
|
+
@persist = persist
|
|
313
|
+
@environment_id = environment_id
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
#
|
|
318
|
+
# Payload represents a payload delivered in a streaming response.
|
|
319
|
+
#
|
|
320
|
+
# This type is not stable, and not subject to any backwards
|
|
321
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
322
|
+
#
|
|
323
|
+
# Do not use it.
|
|
324
|
+
# You have been warned.
|
|
325
|
+
#
|
|
326
|
+
class Payload
|
|
327
|
+
# @return [String] The payload ID
|
|
328
|
+
attr_reader :id
|
|
329
|
+
|
|
330
|
+
# @return [Integer] The target
|
|
331
|
+
attr_reader :target
|
|
332
|
+
|
|
333
|
+
# @return [String] The intent code ({IntentCode})
|
|
334
|
+
attr_reader :code
|
|
335
|
+
|
|
336
|
+
# @return [String] The reason
|
|
337
|
+
attr_reader :reason
|
|
338
|
+
|
|
339
|
+
#
|
|
340
|
+
# @param id [String] The payload ID
|
|
341
|
+
# @param target [Integer] The target
|
|
342
|
+
# @param code [String] The intent code ({IntentCode})
|
|
343
|
+
# @param reason [String] The reason
|
|
344
|
+
#
|
|
345
|
+
def initialize(id:, target:, code:, reason:)
|
|
346
|
+
@id = id
|
|
347
|
+
@target = target
|
|
348
|
+
@code = code
|
|
349
|
+
@reason = reason
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
#
|
|
353
|
+
# Serializes the Payload to a Hash.
|
|
354
|
+
#
|
|
355
|
+
# @return [Hash]
|
|
356
|
+
#
|
|
357
|
+
def to_h
|
|
358
|
+
{
|
|
359
|
+
id: @id,
|
|
360
|
+
target: @target,
|
|
361
|
+
intentCode: @code,
|
|
362
|
+
reason: @reason,
|
|
363
|
+
}
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
#
|
|
367
|
+
# Deserializes a Payload from a Hash.
|
|
368
|
+
#
|
|
369
|
+
# @param data [Hash] The hash representation
|
|
370
|
+
# @return [Payload]
|
|
371
|
+
# @raise [ArgumentError] if required fields are missing or invalid
|
|
372
|
+
#
|
|
373
|
+
def self.from_h(data)
|
|
374
|
+
intent_code = data['intentCode'] || data[:intentCode]
|
|
375
|
+
|
|
376
|
+
raise ArgumentError, "Invalid data for Payload: 'intentCode' key is missing or not a string" if intent_code.nil? || !intent_code.is_a?(String)
|
|
377
|
+
|
|
378
|
+
Payload.new(
|
|
379
|
+
id: data['id'] || data[:id] || "",
|
|
380
|
+
target: data['target'] || data[:target] || 0,
|
|
381
|
+
code: intent_code,
|
|
382
|
+
reason: data['reason'] || data[:reason] || ""
|
|
383
|
+
)
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
#
|
|
388
|
+
# ServerIntent represents the type of change associated with the payload.
|
|
389
|
+
#
|
|
390
|
+
# This type is not stable, and not subject to any backwards
|
|
391
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
392
|
+
#
|
|
393
|
+
# Do not use it.
|
|
394
|
+
# You have been warned.
|
|
395
|
+
#
|
|
396
|
+
class ServerIntent
|
|
397
|
+
# @return [Payload] The payload
|
|
398
|
+
attr_reader :payload
|
|
399
|
+
|
|
400
|
+
#
|
|
401
|
+
# @param payload [Payload] The payload
|
|
402
|
+
#
|
|
403
|
+
def initialize(payload:)
|
|
404
|
+
@payload = payload
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
#
|
|
408
|
+
# Serializes the ServerIntent to a Hash.
|
|
409
|
+
#
|
|
410
|
+
# @return [Hash]
|
|
411
|
+
#
|
|
412
|
+
def to_h
|
|
413
|
+
{
|
|
414
|
+
payloads: [@payload.to_h],
|
|
415
|
+
}
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
#
|
|
419
|
+
# Deserializes a ServerIntent from a Hash.
|
|
420
|
+
#
|
|
421
|
+
# @param data [Hash] The hash representation
|
|
422
|
+
# @return [ServerIntent]
|
|
423
|
+
# @raise [ArgumentError] if required fields are missing or invalid
|
|
424
|
+
#
|
|
425
|
+
def self.from_h(data)
|
|
426
|
+
payloads = data['payloads'] || data[:payloads]
|
|
427
|
+
|
|
428
|
+
raise ArgumentError, "Invalid data for ServerIntent: 'payloads' key is missing or not an array" unless payloads.is_a?(Array)
|
|
429
|
+
raise ArgumentError, "Invalid data for ServerIntent: expected exactly one payload" unless payloads.length == 1
|
|
430
|
+
|
|
431
|
+
payload = payloads[0]
|
|
432
|
+
raise ArgumentError, "Invalid payload in ServerIntent: expected a hash" unless payload.is_a?(Hash)
|
|
433
|
+
|
|
434
|
+
ServerIntent.new(payload: Payload.from_h(payload))
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
#
|
|
439
|
+
# ChangeSetBuilder is a helper for constructing a ChangeSet.
|
|
440
|
+
#
|
|
441
|
+
# This type is not stable, and not subject to any backwards
|
|
442
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
443
|
+
#
|
|
444
|
+
# Do not use it.
|
|
445
|
+
# You have been warned.
|
|
446
|
+
#
|
|
447
|
+
class ChangeSetBuilder
|
|
448
|
+
# @return [String, nil] The current intent ({IntentCode})
|
|
449
|
+
attr_accessor :intent
|
|
450
|
+
|
|
451
|
+
# @return [Array<Change>] The changes
|
|
452
|
+
attr_accessor :changes
|
|
453
|
+
|
|
454
|
+
def initialize
|
|
455
|
+
@intent = nil
|
|
456
|
+
@changes = []
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
#
|
|
460
|
+
# Represents an intent that the current data is up-to-date and doesn't require changes.
|
|
461
|
+
#
|
|
462
|
+
# @return [ChangeSet]
|
|
463
|
+
#
|
|
464
|
+
def self.no_changes
|
|
465
|
+
ChangeSet.new(
|
|
466
|
+
intent_code: IntentCode::TRANSFER_NONE,
|
|
467
|
+
selector: Selector.no_selector,
|
|
468
|
+
changes: []
|
|
469
|
+
)
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
#
|
|
473
|
+
# Returns an empty ChangeSet, useful for initializing without data.
|
|
474
|
+
#
|
|
475
|
+
# @param selector [Selector] The selector
|
|
476
|
+
# @return [ChangeSet]
|
|
477
|
+
#
|
|
478
|
+
def self.empty(selector)
|
|
479
|
+
ChangeSet.new(
|
|
480
|
+
intent_code: IntentCode::TRANSFER_FULL,
|
|
481
|
+
selector: selector,
|
|
482
|
+
changes: []
|
|
483
|
+
)
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
#
|
|
487
|
+
# Begins a new change set with a given intent.
|
|
488
|
+
#
|
|
489
|
+
# @param intent [String] The intent code ({IntentCode})
|
|
490
|
+
# @return [void]
|
|
491
|
+
#
|
|
492
|
+
def start(intent)
|
|
493
|
+
@intent = intent
|
|
494
|
+
@changes = []
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
#
|
|
498
|
+
# Ensures that the current ChangeSetBuilder is prepared to handle changes.
|
|
499
|
+
#
|
|
500
|
+
# @return [void]
|
|
501
|
+
# @raise [RuntimeError] if no server-intent has been set
|
|
502
|
+
#
|
|
503
|
+
def expect_changes
|
|
504
|
+
raise "changeset: cannot expect changes without a server-intent" if @intent.nil?
|
|
505
|
+
|
|
506
|
+
return unless @intent == IntentCode::TRANSFER_NONE
|
|
507
|
+
|
|
508
|
+
@intent = IntentCode::TRANSFER_CHANGES
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
#
|
|
512
|
+
# Clears any existing changes while preserving the current intent.
|
|
513
|
+
#
|
|
514
|
+
# @return [void]
|
|
515
|
+
#
|
|
516
|
+
def reset
|
|
517
|
+
@changes = []
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
#
|
|
521
|
+
# Identifies a changeset with a selector and returns the completed changeset.
|
|
522
|
+
#
|
|
523
|
+
# @param selector [Selector] The selector
|
|
524
|
+
# @return [ChangeSet]
|
|
525
|
+
# @raise [RuntimeError] if no server-intent has been set
|
|
526
|
+
#
|
|
527
|
+
def finish(selector)
|
|
528
|
+
raise "changeset: cannot complete without a server-intent" if @intent.nil?
|
|
529
|
+
|
|
530
|
+
changeset = ChangeSet.new(
|
|
531
|
+
intent_code: @intent,
|
|
532
|
+
selector: selector,
|
|
533
|
+
changes: @changes
|
|
534
|
+
)
|
|
535
|
+
@changes = []
|
|
536
|
+
|
|
537
|
+
# Once a full transfer has been processed, all future changes should be
|
|
538
|
+
# assumed to be changes. Flag delivery can override this behavior by
|
|
539
|
+
# sending a new server intent to any connected stream.
|
|
540
|
+
@intent = IntentCode::TRANSFER_CHANGES if @intent == IntentCode::TRANSFER_FULL
|
|
541
|
+
|
|
542
|
+
changeset
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
#
|
|
546
|
+
# Adds a new object to the changeset.
|
|
547
|
+
#
|
|
548
|
+
# @param kind [String] The object kind ({ObjectKind})
|
|
549
|
+
# @param key [String] The key
|
|
550
|
+
# @param version [Integer] The version
|
|
551
|
+
# @param obj [Hash] The object data
|
|
552
|
+
# @return [void]
|
|
553
|
+
#
|
|
554
|
+
def add_put(kind, key, version, obj)
|
|
555
|
+
@changes << Change.new(
|
|
556
|
+
action: ChangeType::PUT,
|
|
557
|
+
kind: kind,
|
|
558
|
+
key: key,
|
|
559
|
+
version: version,
|
|
560
|
+
object: obj
|
|
561
|
+
)
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
#
|
|
565
|
+
# Adds a deletion to the changeset.
|
|
566
|
+
#
|
|
567
|
+
# @param kind [String] The object kind ({ObjectKind})
|
|
568
|
+
# @param key [String] The key
|
|
569
|
+
# @param version [Integer] The version
|
|
570
|
+
# @return [void]
|
|
571
|
+
#
|
|
572
|
+
def add_delete(kind, key, version)
|
|
573
|
+
@changes << Change.new(
|
|
574
|
+
action: ChangeType::DELETE,
|
|
575
|
+
kind: kind,
|
|
576
|
+
key: key,
|
|
577
|
+
version: version
|
|
578
|
+
)
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
#
|
|
583
|
+
# Update represents the results of a synchronizer's ongoing sync method.
|
|
584
|
+
#
|
|
585
|
+
# This type is not stable, and not subject to any backwards
|
|
586
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
587
|
+
#
|
|
588
|
+
# Do not use it.
|
|
589
|
+
# You have been warned.
|
|
590
|
+
#
|
|
591
|
+
class Update
|
|
592
|
+
# @return [Symbol] The data source state ({LaunchDarkly::Interfaces::DataSource::Status})
|
|
593
|
+
attr_reader :state
|
|
594
|
+
|
|
595
|
+
# @return [ChangeSet, nil] The change set
|
|
596
|
+
attr_reader :change_set
|
|
597
|
+
|
|
598
|
+
# @return [LaunchDarkly::Interfaces::DataSource::ErrorInfo, nil] Error information
|
|
599
|
+
attr_reader :error
|
|
600
|
+
|
|
601
|
+
# @return [Boolean] Whether to revert to FDv1
|
|
602
|
+
attr_reader :revert_to_fdv1
|
|
603
|
+
|
|
604
|
+
# @return [String, nil] The environment ID
|
|
605
|
+
attr_reader :environment_id
|
|
606
|
+
|
|
607
|
+
#
|
|
608
|
+
# @param state [Symbol] The data source state ({LaunchDarkly::Interfaces::DataSource::Status})
|
|
609
|
+
# @param change_set [ChangeSet, nil] The change set
|
|
610
|
+
# @param error [LaunchDarkly::Interfaces::DataSource::ErrorInfo, nil] Error information
|
|
611
|
+
# @param revert_to_fdv1 [Boolean] Whether to revert to FDv1
|
|
612
|
+
# @param environment_id [String, nil] The environment ID
|
|
613
|
+
#
|
|
614
|
+
def initialize(state:, change_set: nil, error: nil, revert_to_fdv1: false, environment_id: nil)
|
|
615
|
+
@state = state
|
|
616
|
+
@change_set = change_set
|
|
617
|
+
@error = error
|
|
618
|
+
@revert_to_fdv1 = revert_to_fdv1
|
|
619
|
+
@environment_id = environment_id
|
|
620
|
+
end
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
#
|
|
624
|
+
# SelectorStore represents a component capable of providing Selectors for data retrieval.
|
|
625
|
+
#
|
|
626
|
+
# This type is not stable, and not subject to any backwards
|
|
627
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
628
|
+
#
|
|
629
|
+
# Do not use it.
|
|
630
|
+
# You have been warned.
|
|
631
|
+
#
|
|
632
|
+
module SelectorStore
|
|
633
|
+
#
|
|
634
|
+
# Returns a Selector object that defines the criteria for data retrieval.
|
|
635
|
+
#
|
|
636
|
+
# @return [Selector]
|
|
637
|
+
#
|
|
638
|
+
def selector
|
|
639
|
+
raise NotImplementedError, "#{self.class} must implement #selector"
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
#
|
|
644
|
+
# ReadOnlyStore represents a read-only store interface for retrieving data.
|
|
645
|
+
#
|
|
646
|
+
# This type is not stable, and not subject to any backwards
|
|
647
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
648
|
+
#
|
|
649
|
+
# Do not use it.
|
|
650
|
+
# You have been warned.
|
|
651
|
+
#
|
|
652
|
+
module ReadOnlyStore
|
|
653
|
+
#
|
|
654
|
+
# Retrieves an item by kind and key.
|
|
655
|
+
#
|
|
656
|
+
# @param kind [LaunchDarkly::Impl::DataStore::DataKind] The data kind (e.g., LaunchDarkly::Impl::DataStore::FEATURES, LaunchDarkly::Impl::DataStore::SEGMENTS)
|
|
657
|
+
# @param key [String] The item key
|
|
658
|
+
# @return [Hash, nil] The item, or nil if not found or deleted
|
|
659
|
+
#
|
|
660
|
+
def get(kind, key)
|
|
661
|
+
raise NotImplementedError, "#{self.class} must implement #get"
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
#
|
|
665
|
+
# Retrieves all items of a given kind.
|
|
666
|
+
#
|
|
667
|
+
# @param kind [LaunchDarkly::Impl::DataStore::DataKind] The data kind (e.g., LaunchDarkly::Impl::DataStore::FEATURES, LaunchDarkly::Impl::DataStore::SEGMENTS)
|
|
668
|
+
# @return [Hash] Hash of keys to items (excluding deleted items)
|
|
669
|
+
#
|
|
670
|
+
def all(kind)
|
|
671
|
+
raise NotImplementedError, "#{self.class} must implement #all"
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
#
|
|
675
|
+
# Returns whether the store has been initialized.
|
|
676
|
+
#
|
|
677
|
+
# @return [Boolean]
|
|
678
|
+
#
|
|
679
|
+
def initialized?
|
|
680
|
+
raise NotImplementedError, "#{self.class} must implement #initialized?"
|
|
681
|
+
end
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
#
|
|
685
|
+
# Initializer represents a component capable of retrieving a single data result.
|
|
686
|
+
#
|
|
687
|
+
# This type is not stable, and not subject to any backwards
|
|
688
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
689
|
+
#
|
|
690
|
+
# Do not use it.
|
|
691
|
+
# You have been warned.
|
|
692
|
+
#
|
|
693
|
+
module Initializer
|
|
694
|
+
#
|
|
695
|
+
# Returns the name of the initializer.
|
|
696
|
+
#
|
|
697
|
+
# @return [String]
|
|
698
|
+
#
|
|
699
|
+
def name
|
|
700
|
+
raise NotImplementedError, "#{self.class} must implement #name"
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
#
|
|
704
|
+
# Retrieves the initial data set for the data source.
|
|
705
|
+
#
|
|
706
|
+
# @param selector_store [SelectorStore] Provides the Selector
|
|
707
|
+
# @return [LaunchDarkly::Result<Basis, String>]
|
|
708
|
+
#
|
|
709
|
+
def fetch(selector_store)
|
|
710
|
+
raise NotImplementedError, "#{self.class} must implement #fetch"
|
|
711
|
+
end
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
#
|
|
715
|
+
# Synchronizer represents a component capable of synchronizing data from an external source.
|
|
716
|
+
#
|
|
717
|
+
# This type is not stable, and not subject to any backwards
|
|
718
|
+
# compatibility guarantees or semantic versioning. It is not suitable for production usage.
|
|
719
|
+
#
|
|
720
|
+
# Do not use it.
|
|
721
|
+
# You have been warned.
|
|
722
|
+
#
|
|
723
|
+
module Synchronizer
|
|
724
|
+
#
|
|
725
|
+
# Returns the name of the synchronizer.
|
|
726
|
+
#
|
|
727
|
+
# @return [String]
|
|
728
|
+
#
|
|
729
|
+
def name
|
|
730
|
+
raise NotImplementedError, "#{self.class} must implement #name"
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
#
|
|
734
|
+
# Begins the synchronization process, yielding Update objects.
|
|
735
|
+
#
|
|
736
|
+
# @param selector_store [SelectorStore] Provides the Selector
|
|
737
|
+
# @yieldparam update [Update] The update
|
|
738
|
+
# @return [void]
|
|
739
|
+
#
|
|
740
|
+
def sync(selector_store, &block)
|
|
741
|
+
raise NotImplementedError, "#{self.class} must implement #sync"
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
#
|
|
745
|
+
# Halts the synchronization process.
|
|
746
|
+
#
|
|
747
|
+
# @return [void]
|
|
748
|
+
#
|
|
749
|
+
def stop
|
|
750
|
+
raise NotImplementedError, "#{self.class} must implement #stop"
|
|
751
|
+
end
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
end
|
|
755
|
+
end
|