launchdarkly-server-sdk 6.2.5 → 7.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|