launchdarkly-server-sdk 6.2.5 → 7.0.0
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/README.md +1 -2
- data/lib/ldclient-rb/config.rb +203 -43
- data/lib/ldclient-rb/context.rb +487 -0
- data/lib/ldclient-rb/evaluation_detail.rb +85 -26
- data/lib/ldclient-rb/events.rb +185 -146
- data/lib/ldclient-rb/flags_state.rb +25 -14
- data/lib/ldclient-rb/impl/big_segments.rb +117 -0
- data/lib/ldclient-rb/impl/context.rb +96 -0
- data/lib/ldclient-rb/impl/context_filter.rb +145 -0
- data/lib/ldclient-rb/impl/diagnostic_events.rb +9 -10
- data/lib/ldclient-rb/impl/evaluator.rb +428 -132
- data/lib/ldclient-rb/impl/evaluator_bucketing.rb +40 -41
- data/lib/ldclient-rb/impl/evaluator_helpers.rb +50 -0
- data/lib/ldclient-rb/impl/evaluator_operators.rb +26 -55
- data/lib/ldclient-rb/impl/event_sender.rb +6 -6
- data/lib/ldclient-rb/impl/event_summarizer.rb +68 -0
- data/lib/ldclient-rb/impl/event_types.rb +78 -0
- data/lib/ldclient-rb/impl/integrations/consul_impl.rb +7 -7
- data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +92 -28
- data/lib/ldclient-rb/impl/integrations/file_data_source.rb +212 -0
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +165 -32
- data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +40 -0
- data/lib/ldclient-rb/impl/model/clause.rb +39 -0
- data/lib/ldclient-rb/impl/model/feature_flag.rb +213 -0
- data/lib/ldclient-rb/impl/model/preprocessed_data.rb +64 -0
- data/lib/ldclient-rb/impl/model/segment.rb +126 -0
- data/lib/ldclient-rb/impl/model/serialization.rb +54 -44
- data/lib/ldclient-rb/impl/repeating_task.rb +47 -0
- data/lib/ldclient-rb/impl/store_data_set_sorter.rb +2 -2
- data/lib/ldclient-rb/impl/unbounded_pool.rb +1 -1
- data/lib/ldclient-rb/impl/util.rb +62 -1
- data/lib/ldclient-rb/in_memory_store.rb +2 -2
- data/lib/ldclient-rb/integrations/consul.rb +9 -2
- data/lib/ldclient-rb/integrations/dynamodb.rb +47 -2
- data/lib/ldclient-rb/integrations/file_data.rb +108 -0
- data/lib/ldclient-rb/integrations/redis.rb +43 -3
- data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +594 -0
- data/lib/ldclient-rb/integrations/test_data.rb +213 -0
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +14 -9
- data/lib/ldclient-rb/integrations.rb +2 -51
- data/lib/ldclient-rb/interfaces.rb +151 -1
- data/lib/ldclient-rb/ldclient.rb +175 -133
- data/lib/ldclient-rb/memoized_value.rb +1 -1
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +1 -1
- data/lib/ldclient-rb/polling.rb +22 -41
- data/lib/ldclient-rb/reference.rb +274 -0
- data/lib/ldclient-rb/requestor.rb +7 -7
- data/lib/ldclient-rb/stream.rb +9 -9
- data/lib/ldclient-rb/util.rb +11 -17
- data/lib/ldclient-rb/version.rb +1 -1
- data/lib/ldclient-rb.rb +2 -4
- metadata +49 -23
- data/lib/ldclient-rb/event_summarizer.rb +0 -55
- data/lib/ldclient-rb/file_data_source.rb +0 -314
- data/lib/ldclient-rb/impl/event_factory.rb +0 -126
- data/lib/ldclient-rb/newrelic.rb +0 -17
- data/lib/ldclient-rb/redis_store.rb +0 -88
- data/lib/ldclient-rb/user_filter.rb +0 -52
data/lib/ldclient-rb/events.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
require "ldclient-rb/impl/context_filter"
|
1
2
|
require "ldclient-rb/impl/diagnostic_events"
|
2
3
|
require "ldclient-rb/impl/event_sender"
|
4
|
+
require "ldclient-rb/impl/event_summarizer"
|
5
|
+
require "ldclient-rb/impl/event_types"
|
3
6
|
require "ldclient-rb/impl/util"
|
4
7
|
|
5
8
|
require "concurrent"
|
@@ -18,7 +21,7 @@ require "time"
|
|
18
21
|
# On a separate worker thread, EventDispatcher consumes events from the inbox. These are considered
|
19
22
|
# "input events" because they may or may not actually be sent to LaunchDarkly; most flag evaluation
|
20
23
|
# events are not sent, but are counted and the counters become part of a single summary event.
|
21
|
-
# EventDispatcher updates those counters, creates "index" events for any
|
24
|
+
# EventDispatcher updates those counters, creates "index" events for any contexts that have not been seen
|
22
25
|
# recently, and places any events that will be sent to LaunchDarkly into the "outbox" queue.
|
23
26
|
#
|
24
27
|
# When it is time to flush events to LaunchDarkly, the contents of the outbox are handed off to
|
@@ -26,16 +29,30 @@ require "time"
|
|
26
29
|
#
|
27
30
|
|
28
31
|
module LaunchDarkly
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
+
module EventProcessorMethods
|
33
|
+
def record_eval_event(
|
34
|
+
context,
|
35
|
+
key,
|
36
|
+
version = nil,
|
37
|
+
variation = nil,
|
38
|
+
value = nil,
|
39
|
+
reason = nil,
|
40
|
+
default = nil,
|
41
|
+
track_events = false,
|
42
|
+
debug_until = nil,
|
43
|
+
prereq_of = nil
|
44
|
+
)
|
45
|
+
end
|
32
46
|
|
33
|
-
|
34
|
-
|
47
|
+
def record_identify_event(context)
|
48
|
+
end
|
35
49
|
|
36
|
-
|
37
|
-
|
38
|
-
|
50
|
+
def record_custom_event(
|
51
|
+
context,
|
52
|
+
key,
|
53
|
+
data = nil,
|
54
|
+
metric_value = nil
|
55
|
+
)
|
39
56
|
end
|
40
57
|
|
41
58
|
def flush
|
@@ -45,12 +62,12 @@ module LaunchDarkly
|
|
45
62
|
end
|
46
63
|
end
|
47
64
|
|
65
|
+
MAX_FLUSH_WORKERS = 5
|
66
|
+
private_constant :MAX_FLUSH_WORKERS
|
67
|
+
|
48
68
|
# @private
|
49
|
-
class
|
50
|
-
|
51
|
-
@event = event
|
52
|
-
end
|
53
|
-
attr_reader :event
|
69
|
+
class NullEventProcessor
|
70
|
+
include EventProcessorMethods
|
54
71
|
end
|
55
72
|
|
56
73
|
# @private
|
@@ -58,7 +75,7 @@ module LaunchDarkly
|
|
58
75
|
end
|
59
76
|
|
60
77
|
# @private
|
61
|
-
class
|
78
|
+
class FlushContextsMessage
|
62
79
|
end
|
63
80
|
|
64
81
|
# @private
|
@@ -70,7 +87,7 @@ module LaunchDarkly
|
|
70
87
|
def initialize
|
71
88
|
@reply = Concurrent::Semaphore.new(0)
|
72
89
|
end
|
73
|
-
|
90
|
+
|
74
91
|
def completed
|
75
92
|
@reply.release
|
76
93
|
end
|
@@ -90,6 +107,8 @@ module LaunchDarkly
|
|
90
107
|
|
91
108
|
# @private
|
92
109
|
class EventProcessor
|
110
|
+
include EventProcessorMethods
|
111
|
+
|
93
112
|
def initialize(sdk_key, config, client = nil, diagnostic_accumulator = nil, test_properties = nil)
|
94
113
|
raise ArgumentError, "sdk_key must not be nil" if sdk_key.nil? # see LDClient constructor comment on sdk_key
|
95
114
|
@logger = config.logger
|
@@ -98,10 +117,10 @@ module LaunchDarkly
|
|
98
117
|
post_to_inbox(FlushMessage.new)
|
99
118
|
end
|
100
119
|
@flush_task.execute
|
101
|
-
@
|
102
|
-
post_to_inbox(
|
120
|
+
@contexts_flush_task = Concurrent::TimerTask.new(execution_interval: config.context_keys_flush_interval) do
|
121
|
+
post_to_inbox(FlushContextsMessage.new)
|
103
122
|
end
|
104
|
-
@
|
123
|
+
@contexts_flush_task.execute
|
105
124
|
if !diagnostic_accumulator.nil?
|
106
125
|
interval = test_properties && test_properties.has_key?(:diagnostic_recording_interval) ?
|
107
126
|
test_properties[:diagnostic_recording_interval] :
|
@@ -116,16 +135,36 @@ module LaunchDarkly
|
|
116
135
|
@stopped = Concurrent::AtomicBoolean.new(false)
|
117
136
|
@inbox_full = Concurrent::AtomicBoolean.new(false)
|
118
137
|
|
119
|
-
event_sender = test_properties
|
120
|
-
|
121
|
-
|
138
|
+
event_sender = (test_properties || {})[:event_sender] ||
|
139
|
+
Impl::EventSender.new(sdk_key, config, client || Util.new_http_client(config.events_uri, config))
|
140
|
+
|
141
|
+
@timestamp_fn = (test_properties || {})[:timestamp_fn] || proc { Impl::Util.current_time_millis }
|
122
142
|
|
123
143
|
EventDispatcher.new(@inbox, sdk_key, config, diagnostic_accumulator, event_sender)
|
124
144
|
end
|
125
145
|
|
126
|
-
def
|
127
|
-
|
128
|
-
|
146
|
+
def record_eval_event(
|
147
|
+
context,
|
148
|
+
key,
|
149
|
+
version = nil,
|
150
|
+
variation = nil,
|
151
|
+
value = nil,
|
152
|
+
reason = nil,
|
153
|
+
default = nil,
|
154
|
+
track_events = false,
|
155
|
+
debug_until = nil,
|
156
|
+
prereq_of = nil
|
157
|
+
)
|
158
|
+
post_to_inbox(LaunchDarkly::Impl::EvalEvent.new(timestamp, context, key, version, variation, value, reason,
|
159
|
+
default, track_events, debug_until, prereq_of))
|
160
|
+
end
|
161
|
+
|
162
|
+
def record_identify_event(context)
|
163
|
+
post_to_inbox(LaunchDarkly::Impl::IdentifyEvent.new(timestamp, context))
|
164
|
+
end
|
165
|
+
|
166
|
+
def record_custom_event(context, key, data = nil, metric_value = nil)
|
167
|
+
post_to_inbox(LaunchDarkly::Impl::CustomEvent.new(timestamp, context, key, data, metric_value))
|
129
168
|
end
|
130
169
|
|
131
170
|
def flush
|
@@ -137,8 +176,8 @@ module LaunchDarkly
|
|
137
176
|
# final shutdown, which includes a final flush, is done synchronously
|
138
177
|
if @stopped.make_true
|
139
178
|
@flush_task.shutdown
|
140
|
-
@
|
141
|
-
@diagnostic_event_task.shutdown
|
179
|
+
@contexts_flush_task.shutdown
|
180
|
+
@diagnostic_event_task.shutdown unless @diagnostic_event_task.nil?
|
142
181
|
# Note that here we are not calling post_to_inbox, because we *do* want to wait if the inbox
|
143
182
|
# is full; an orderly shutdown can't happen unless these messages are received.
|
144
183
|
@inbox << FlushMessage.new
|
@@ -155,9 +194,11 @@ module LaunchDarkly
|
|
155
194
|
sync_msg.wait_for_completion
|
156
195
|
end
|
157
196
|
|
158
|
-
private
|
197
|
+
private def timestamp
|
198
|
+
@timestamp_fn.call()
|
199
|
+
end
|
159
200
|
|
160
|
-
def post_to_inbox(message)
|
201
|
+
private def post_to_inbox(message)
|
161
202
|
begin
|
162
203
|
@inbox.push(message, non_block=true)
|
163
204
|
rescue ThreadError
|
@@ -180,13 +221,13 @@ module LaunchDarkly
|
|
180
221
|
@diagnostic_accumulator = config.diagnostic_opt_out? ? nil : diagnostic_accumulator
|
181
222
|
@event_sender = event_sender
|
182
223
|
|
183
|
-
@
|
224
|
+
@context_keys = SimpleLRUCacheSet.new(config.context_keys_capacity)
|
184
225
|
@formatter = EventOutputFormatter.new(config)
|
185
226
|
@disabled = Concurrent::AtomicBoolean.new(false)
|
186
227
|
@last_known_past_time = Concurrent::AtomicReference.new(0)
|
187
|
-
@
|
228
|
+
@deduplicated_contexts = 0
|
188
229
|
@events_in_last_batch = 0
|
189
|
-
|
230
|
+
|
190
231
|
outbox = EventBuffer.new(config.capacity, config.logger)
|
191
232
|
flush_workers = NonBlockingThreadPool.new(MAX_FLUSH_WORKERS)
|
192
233
|
|
@@ -209,12 +250,10 @@ module LaunchDarkly
|
|
209
250
|
begin
|
210
251
|
message = inbox.pop
|
211
252
|
case message
|
212
|
-
when EventMessage
|
213
|
-
dispatch_event(message.event, outbox)
|
214
253
|
when FlushMessage
|
215
254
|
trigger_flush(outbox, flush_workers)
|
216
|
-
when
|
217
|
-
@
|
255
|
+
when FlushContextsMessage
|
256
|
+
@context_keys.clear
|
218
257
|
when DiagnosticEventMessage
|
219
258
|
send_and_reset_diagnostics(outbox, diagnostic_event_workers)
|
220
259
|
when TestSyncMessage
|
@@ -224,6 +263,8 @@ module LaunchDarkly
|
|
224
263
|
do_shutdown(flush_workers, diagnostic_event_workers)
|
225
264
|
running = false
|
226
265
|
message.completed
|
266
|
+
else
|
267
|
+
dispatch_event(message, outbox)
|
227
268
|
end
|
228
269
|
rescue => e
|
229
270
|
Util.log_exception(@config.logger, "Unexpected error in event processor", e)
|
@@ -234,7 +275,7 @@ module LaunchDarkly
|
|
234
275
|
def do_shutdown(flush_workers, diagnostic_event_workers)
|
235
276
|
flush_workers.shutdown
|
236
277
|
flush_workers.wait_for_termination
|
237
|
-
|
278
|
+
unless diagnostic_event_workers.nil?
|
238
279
|
diagnostic_event_workers.shutdown
|
239
280
|
diagnostic_event_workers.wait_for_termination
|
240
281
|
end
|
@@ -244,7 +285,7 @@ module LaunchDarkly
|
|
244
285
|
def synchronize_for_testing(flush_workers, diagnostic_event_workers)
|
245
286
|
# Used only by unit tests. Wait until all active flush workers have finished.
|
246
287
|
flush_workers.wait_all
|
247
|
-
diagnostic_event_workers.wait_all
|
288
|
+
diagnostic_event_workers.wait_all unless diagnostic_event_workers.nil?
|
248
289
|
end
|
249
290
|
|
250
291
|
def dispatch_event(event, outbox)
|
@@ -257,45 +298,39 @@ module LaunchDarkly
|
|
257
298
|
# the event (if tracked) and once for debugging.
|
258
299
|
will_add_full_event = false
|
259
300
|
debug_event = nil
|
260
|
-
if event
|
261
|
-
will_add_full_event = event
|
301
|
+
if event.is_a?(LaunchDarkly::Impl::EvalEvent)
|
302
|
+
will_add_full_event = event.track_events
|
262
303
|
if should_debug_event(event)
|
263
|
-
debug_event = event
|
264
|
-
debug_event[:debug] = true
|
304
|
+
debug_event = LaunchDarkly::Impl::DebugEvent.new(event)
|
265
305
|
end
|
266
306
|
else
|
267
307
|
will_add_full_event = true
|
268
308
|
end
|
269
309
|
|
270
|
-
# For each
|
271
|
-
# an identify event for that
|
272
|
-
if !(
|
273
|
-
|
274
|
-
outbox.add_event({
|
275
|
-
kind: "index",
|
276
|
-
creationDate: event[:creationDate],
|
277
|
-
user: event[:user]
|
278
|
-
})
|
279
|
-
end
|
310
|
+
# For each context we haven't seen before, we add an index event - unless this is already
|
311
|
+
# an identify event for that context.
|
312
|
+
if !event.context.nil? && !notice_context(event.context) && !event.is_a?(LaunchDarkly::Impl::IdentifyEvent)
|
313
|
+
outbox.add_event(LaunchDarkly::Impl::IndexEvent.new(event.timestamp, event.context))
|
280
314
|
end
|
281
315
|
|
282
316
|
outbox.add_event(event) if will_add_full_event
|
283
|
-
outbox.add_event(debug_event)
|
317
|
+
outbox.add_event(debug_event) unless debug_event.nil?
|
284
318
|
end
|
285
319
|
|
286
|
-
#
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
320
|
+
#
|
321
|
+
# Add to the set of contexts we've noticed, and return true if the context
|
322
|
+
# was already known to us.
|
323
|
+
# @param context [LaunchDarkly::LDContext]
|
324
|
+
# @return [Boolean]
|
325
|
+
#
|
326
|
+
def notice_context(context)
|
327
|
+
known = @context_keys.add(context.fully_qualified_key)
|
328
|
+
@deduplicated_contexts += 1 if known
|
329
|
+
known
|
295
330
|
end
|
296
331
|
|
297
332
|
def should_debug_event(event)
|
298
|
-
debug_until = event
|
333
|
+
debug_until = event.debug_until
|
299
334
|
if !debug_until.nil?
|
300
335
|
last_past = @last_known_past_time.value
|
301
336
|
debug_until > last_past && debug_until > Impl::Util.current_time_millis
|
@@ -309,7 +344,7 @@ module LaunchDarkly
|
|
309
344
|
return
|
310
345
|
end
|
311
346
|
|
312
|
-
payload = outbox.get_payload
|
347
|
+
payload = outbox.get_payload
|
313
348
|
if !payload.events.empty? || !payload.summary.counters.empty?
|
314
349
|
count = payload.events.length + (payload.summary.counters.empty? ? 0 : 1)
|
315
350
|
@events_in_last_batch = count
|
@@ -319,7 +354,7 @@ module LaunchDarkly
|
|
319
354
|
events_out = @formatter.make_output_events(payload.events, payload.summary)
|
320
355
|
result = @event_sender.send_event_data(events_out.to_json, "#{events_out.length} events", false)
|
321
356
|
@disabled.value = true if result.must_shutdown
|
322
|
-
|
357
|
+
unless result.time_from_server.nil?
|
323
358
|
@last_known_past_time.value = (result.time_from_server.to_f * 1000).to_i
|
324
359
|
end
|
325
360
|
rescue => e
|
@@ -335,8 +370,8 @@ module LaunchDarkly
|
|
335
370
|
def send_and_reset_diagnostics(outbox, diagnostic_event_workers)
|
336
371
|
return if @diagnostic_accumulator.nil?
|
337
372
|
dropped_count = outbox.get_and_clear_dropped_count
|
338
|
-
event = @diagnostic_accumulator.create_periodic_event_and_reset(dropped_count, @
|
339
|
-
@
|
373
|
+
event = @diagnostic_accumulator.create_periodic_event_and_reset(dropped_count, @deduplicated_contexts, @events_in_last_batch)
|
374
|
+
@deduplicated_contexts = 0
|
340
375
|
@events_in_last_batch = 0
|
341
376
|
send_diagnostic_event(event, diagnostic_event_workers)
|
342
377
|
end
|
@@ -365,17 +400,16 @@ module LaunchDarkly
|
|
365
400
|
@capacity_exceeded = false
|
366
401
|
@dropped_events = 0
|
367
402
|
@events = []
|
368
|
-
@summarizer = EventSummarizer.new
|
403
|
+
@summarizer = LaunchDarkly::Impl::EventSummarizer.new
|
369
404
|
end
|
370
405
|
|
371
406
|
def add_event(event)
|
372
407
|
if @events.length < @capacity
|
373
|
-
@logger.debug { "[LDClient] Enqueueing event: #{event.to_json}" }
|
374
408
|
@events.push(event)
|
375
409
|
@capacity_exceeded = false
|
376
410
|
else
|
377
411
|
@dropped_events += 1
|
378
|
-
|
412
|
+
unless @capacity_exceeded
|
379
413
|
@capacity_exceeded = true
|
380
414
|
@logger.warn { "[LDClient] Exceeded event queue capacity. Increase capacity to avoid dropping events." }
|
381
415
|
end
|
@@ -387,7 +421,7 @@ module LaunchDarkly
|
|
387
421
|
end
|
388
422
|
|
389
423
|
def get_payload
|
390
|
-
|
424
|
+
FlushPayload.new(@events, @summarizer.snapshot)
|
391
425
|
end
|
392
426
|
|
393
427
|
def get_and_clear_dropped_count
|
@@ -404,113 +438,118 @@ module LaunchDarkly
|
|
404
438
|
|
405
439
|
# @private
|
406
440
|
class EventOutputFormatter
|
441
|
+
FEATURE_KIND = 'feature'
|
442
|
+
IDENTIFY_KIND = 'identify'
|
443
|
+
CUSTOM_KIND = 'custom'
|
444
|
+
INDEX_KIND = 'index'
|
445
|
+
DEBUG_KIND = 'debug'
|
446
|
+
SUMMARY_KIND = 'summary'
|
447
|
+
|
407
448
|
def initialize(config)
|
408
|
-
@
|
409
|
-
@user_filter = UserFilter.new(config)
|
449
|
+
@context_filter = LaunchDarkly::Impl::ContextFilter.new(config.all_attributes_private, config.private_attributes)
|
410
450
|
end
|
411
451
|
|
412
452
|
# Transforms events into the format used for event sending.
|
413
453
|
def make_output_events(events, summary)
|
414
454
|
events_out = events.map { |e| make_output_event(e) }
|
415
|
-
|
455
|
+
unless summary.counters.empty?
|
416
456
|
events_out.push(make_summary_event(summary))
|
417
457
|
end
|
418
458
|
events_out
|
419
459
|
end
|
420
460
|
|
421
|
-
private
|
422
|
-
|
423
|
-
def process_user(event)
|
424
|
-
filtered = @user_filter.transform_user_props(event[:user])
|
425
|
-
Util.stringify_attrs(filtered, USER_ATTRS_TO_STRINGIFY_FOR_EVENTS)
|
426
|
-
end
|
461
|
+
private def make_output_event(event)
|
462
|
+
case event
|
427
463
|
|
428
|
-
|
429
|
-
case event[:kind]
|
430
|
-
when "feature"
|
431
|
-
is_debug = event[:debug]
|
464
|
+
when LaunchDarkly::Impl::EvalEvent
|
432
465
|
out = {
|
433
|
-
kind:
|
434
|
-
creationDate: event
|
435
|
-
key: event
|
436
|
-
value: event
|
466
|
+
kind: FEATURE_KIND,
|
467
|
+
creationDate: event.timestamp,
|
468
|
+
key: event.key,
|
469
|
+
value: event.value,
|
437
470
|
}
|
438
|
-
out[:default] = event
|
439
|
-
out[:variation] = event
|
440
|
-
out[:version] = event
|
441
|
-
out[:prereqOf] = event
|
442
|
-
out[:
|
443
|
-
|
444
|
-
out[:user] = process_user(event)
|
445
|
-
else
|
446
|
-
out[:userKey] = event[:user][:key]
|
447
|
-
end
|
448
|
-
out[:reason] = event[:reason] if !event[:reason].nil?
|
471
|
+
out[:default] = event.default unless event.default.nil?
|
472
|
+
out[:variation] = event.variation unless event.variation.nil?
|
473
|
+
out[:version] = event.version unless event.version.nil?
|
474
|
+
out[:prereqOf] = event.prereq_of unless event.prereq_of.nil?
|
475
|
+
out[:contextKeys] = event.context.keys
|
476
|
+
out[:reason] = event.reason unless event.reason.nil?
|
449
477
|
out
|
450
|
-
|
478
|
+
|
479
|
+
when LaunchDarkly::Impl::IdentifyEvent
|
451
480
|
{
|
452
|
-
kind:
|
453
|
-
creationDate: event
|
454
|
-
key: event
|
455
|
-
|
481
|
+
kind: IDENTIFY_KIND,
|
482
|
+
creationDate: event.timestamp,
|
483
|
+
key: event.context.fully_qualified_key,
|
484
|
+
context: @context_filter.filter(event.context),
|
456
485
|
}
|
457
|
-
|
486
|
+
|
487
|
+
when LaunchDarkly::Impl::CustomEvent
|
458
488
|
out = {
|
459
|
-
kind:
|
460
|
-
creationDate: event
|
461
|
-
key: event
|
489
|
+
kind: CUSTOM_KIND,
|
490
|
+
creationDate: event.timestamp,
|
491
|
+
key: event.key,
|
462
492
|
}
|
463
|
-
out[:data] = event
|
464
|
-
|
465
|
-
|
466
|
-
else
|
467
|
-
out[:userKey] = event[:user][:key]
|
468
|
-
end
|
469
|
-
out[:metricValue] = event[:metricValue] if event.has_key?(:metricValue)
|
470
|
-
out[:contextKind] = event[:contextKind] if event.has_key?(:contextKind)
|
493
|
+
out[:data] = event.data unless event.data.nil?
|
494
|
+
out[:contextKeys] = event.context.keys
|
495
|
+
out[:metricValue] = event.metric_value unless event.metric_value.nil?
|
471
496
|
out
|
472
|
-
|
497
|
+
|
498
|
+
when LaunchDarkly::Impl::IndexEvent
|
473
499
|
{
|
474
|
-
kind:
|
475
|
-
creationDate: event
|
476
|
-
|
500
|
+
kind: INDEX_KIND,
|
501
|
+
creationDate: event.timestamp,
|
502
|
+
context: @context_filter.filter(event.context),
|
477
503
|
}
|
504
|
+
|
505
|
+
when LaunchDarkly::Impl::DebugEvent
|
506
|
+
original = event.eval_event
|
507
|
+
out = {
|
508
|
+
kind: DEBUG_KIND,
|
509
|
+
creationDate: original.timestamp,
|
510
|
+
key: original.key,
|
511
|
+
context: @context_filter.filter(original.context),
|
512
|
+
value: original.value,
|
513
|
+
}
|
514
|
+
out[:default] = original.default unless original.default.nil?
|
515
|
+
out[:variation] = original.variation unless original.variation.nil?
|
516
|
+
out[:version] = original.version unless original.version.nil?
|
517
|
+
out[:prereqOf] = original.prereq_of unless original.prereq_of.nil?
|
518
|
+
out[:reason] = original.reason unless original.reason.nil?
|
519
|
+
out
|
520
|
+
|
478
521
|
else
|
479
|
-
|
522
|
+
nil
|
480
523
|
end
|
481
524
|
end
|
482
525
|
|
483
526
|
# Transforms the summary data into the format used for event sending.
|
484
|
-
def make_summary_event(summary)
|
527
|
+
private def make_summary_event(summary)
|
485
528
|
flags = {}
|
486
|
-
summary
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
if ckey[:version].nil?
|
503
|
-
c[:unknown] = true
|
504
|
-
else
|
505
|
-
c[:version] = ckey[:version]
|
529
|
+
summary.counters.each do |flagKey, flagInfo|
|
530
|
+
counters = []
|
531
|
+
flagInfo.versions.each do |version, variations|
|
532
|
+
variations.each do |variation, counter|
|
533
|
+
c = {
|
534
|
+
value: counter.value,
|
535
|
+
count: counter.count,
|
536
|
+
}
|
537
|
+
c[:variation] = variation unless variation.nil?
|
538
|
+
if version.nil?
|
539
|
+
c[:unknown] = true
|
540
|
+
else
|
541
|
+
c[:version] = version
|
542
|
+
end
|
543
|
+
counters.push(c)
|
544
|
+
end
|
506
545
|
end
|
507
|
-
|
508
|
-
|
546
|
+
flags[flagKey] = { default: flagInfo.default, counters: counters, contextKinds: flagInfo.context_kinds.to_a }
|
547
|
+
end
|
509
548
|
{
|
510
|
-
kind:
|
549
|
+
kind: SUMMARY_KIND,
|
511
550
|
startDate: summary[:start_date],
|
512
551
|
endDate: summary[:end_date],
|
513
|
-
features: flags
|
552
|
+
features: flags,
|
514
553
|
}
|
515
554
|
end
|
516
555
|
end
|
@@ -2,7 +2,7 @@ require 'json'
|
|
2
2
|
|
3
3
|
module LaunchDarkly
|
4
4
|
#
|
5
|
-
# A snapshot of the state of all feature flags with regard to a specific
|
5
|
+
# A snapshot of the state of all feature flags with regard to a specific context, generated by
|
6
6
|
# calling the {LDClient#all_flags_state}. Serializing this object to JSON using
|
7
7
|
# `JSON.generate` (or the `to_json` method) will produce the appropriate data structure for
|
8
8
|
# bootstrapping the LaunchDarkly JavaScript client.
|
@@ -16,26 +16,37 @@ module LaunchDarkly
|
|
16
16
|
|
17
17
|
# Used internally to build the state map.
|
18
18
|
# @private
|
19
|
-
def add_flag(
|
20
|
-
key =
|
21
|
-
@flag_values[key] = value
|
19
|
+
def add_flag(flag_state, with_reasons, details_only_if_tracked)
|
20
|
+
key = flag_state[:key]
|
21
|
+
@flag_values[key] = flag_state[:value]
|
22
22
|
meta = {}
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
|
24
|
+
omit_details = false
|
25
|
+
if details_only_if_tracked
|
26
|
+
if !flag_state[:trackEvents] && !flag_state[:trackReason] && !(flag_state[:debugEventsUntilDate] && flag_state[:debugEventsUntilDate] > Impl::Util::current_time_millis)
|
27
|
+
omit_details = true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
reason = (!with_reasons and !flag_state[:trackReason]) ? nil : flag_state[:reason]
|
32
|
+
|
33
|
+
if !reason.nil? && !omit_details
|
34
|
+
meta[:reason] = reason
|
26
35
|
end
|
27
|
-
|
28
|
-
|
29
|
-
meta[:
|
36
|
+
|
37
|
+
unless omit_details
|
38
|
+
meta[:version] = flag_state[:version]
|
30
39
|
end
|
31
|
-
|
32
|
-
meta[:
|
33
|
-
meta[:
|
40
|
+
|
41
|
+
meta[:variation] = flag_state[:variation] unless flag_state[:variation].nil?
|
42
|
+
meta[:trackEvents] = true if flag_state[:trackEvents]
|
43
|
+
meta[:trackReason] = true if flag_state[:trackReason]
|
44
|
+
meta[:debugEventsUntilDate] = flag_state[:debugEventsUntilDate] if flag_state[:debugEventsUntilDate]
|
34
45
|
@flag_metadata[key] = meta
|
35
46
|
end
|
36
47
|
|
37
48
|
# Returns true if this object contains a valid snapshot of feature flag state, or false if the
|
38
|
-
# state could not be computed (for instance, because the client was offline or there was no
|
49
|
+
# state could not be computed (for instance, because the client was offline or there was no context).
|
39
50
|
def valid?
|
40
51
|
@valid
|
41
52
|
end
|