launchdarkly-server-sdk 6.3.3 → 6.3.4
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/events.rb +201 -107
- data/lib/ldclient-rb/impl/evaluator.rb +18 -13
- data/lib/ldclient-rb/impl/event_summarizer.rb +63 -0
- data/lib/ldclient-rb/impl/event_types.rb +90 -0
- data/lib/ldclient-rb/ldclient.rb +79 -21
- data/lib/ldclient-rb/version.rb +1 -1
- data/lib/ldclient-rb.rb +0 -1
- metadata +5 -5
- data/lib/ldclient-rb/event_summarizer.rb +0 -55
- data/lib/ldclient-rb/impl/event_factory.rb +0 -123
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50934709e1ee8c075c56c149f75753f87498eb9e1f06e39597d2cd4514ea7196
|
4
|
+
data.tar.gz: e7f153366b7b50c3951f844bf9ce1a0b81093fc733818dbb02e8f0a888d9ac26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2469e536ca482489eb46f3ff650aeb573eb691ab6c6c2b8ed669b7f8ba799bf65d68d40f6cabc51ec4b051dd56e309430f94e5dc4fbbd70193efa5156d1d1082
|
7
|
+
data.tar.gz: 454219746c7b296fd8c92ee5e266f96765f72527706b7d6f7360445eb1d245e2acb37e4cb722ed48bf73dce8e307d2121771270ea9bedb71e91d67f9a79b8ea0
|
data/lib/ldclient-rb/events.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require "ldclient-rb/impl/diagnostic_events"
|
2
2
|
require "ldclient-rb/impl/event_sender"
|
3
|
+
require "ldclient-rb/impl/event_summarizer"
|
4
|
+
require "ldclient-rb/impl/event_types"
|
3
5
|
require "ldclient-rb/impl/util"
|
4
6
|
|
5
7
|
require "concurrent"
|
@@ -26,16 +28,33 @@ require "time"
|
|
26
28
|
#
|
27
29
|
|
28
30
|
module LaunchDarkly
|
29
|
-
|
30
|
-
|
31
|
-
|
31
|
+
module EventProcessorMethods
|
32
|
+
def record_eval_event(
|
33
|
+
user,
|
34
|
+
key,
|
35
|
+
version = nil,
|
36
|
+
variation = nil,
|
37
|
+
value = nil,
|
38
|
+
reason = nil,
|
39
|
+
default = nil,
|
40
|
+
track_events = false,
|
41
|
+
debug_until = nil,
|
42
|
+
prereq_of = nil
|
43
|
+
)
|
44
|
+
end
|
32
45
|
|
33
|
-
|
34
|
-
|
46
|
+
def record_identify_event(user)
|
47
|
+
end
|
35
48
|
|
36
|
-
|
37
|
-
|
38
|
-
|
49
|
+
def record_custom_event(
|
50
|
+
user,
|
51
|
+
key,
|
52
|
+
data = nil,
|
53
|
+
metric_value = nil
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def record_alias_event(user, previous_user)
|
39
58
|
end
|
40
59
|
|
41
60
|
def flush
|
@@ -45,12 +64,16 @@ module LaunchDarkly
|
|
45
64
|
end
|
46
65
|
end
|
47
66
|
|
67
|
+
MAX_FLUSH_WORKERS = 5
|
68
|
+
USER_ATTRS_TO_STRINGIFY_FOR_EVENTS = [ :key, :secondary, :ip, :country, :email, :firstName, :lastName,
|
69
|
+
:avatar, :name ]
|
70
|
+
|
71
|
+
private_constant :MAX_FLUSH_WORKERS
|
72
|
+
private_constant :USER_ATTRS_TO_STRINGIFY_FOR_EVENTS
|
73
|
+
|
48
74
|
# @private
|
49
|
-
class
|
50
|
-
|
51
|
-
@event = event
|
52
|
-
end
|
53
|
-
attr_reader :event
|
75
|
+
class NullEventProcessor
|
76
|
+
include EventProcessorMethods
|
54
77
|
end
|
55
78
|
|
56
79
|
# @private
|
@@ -90,6 +113,8 @@ module LaunchDarkly
|
|
90
113
|
|
91
114
|
# @private
|
92
115
|
class EventProcessor
|
116
|
+
include EventProcessorMethods
|
117
|
+
|
93
118
|
def initialize(sdk_key, config, client = nil, diagnostic_accumulator = nil, test_properties = nil)
|
94
119
|
raise ArgumentError, "sdk_key must not be nil" if sdk_key.nil? # see LDClient constructor comment on sdk_key
|
95
120
|
@logger = config.logger
|
@@ -116,16 +141,46 @@ module LaunchDarkly
|
|
116
141
|
@stopped = Concurrent::AtomicBoolean.new(false)
|
117
142
|
@inbox_full = Concurrent::AtomicBoolean.new(false)
|
118
143
|
|
119
|
-
event_sender = test_properties
|
120
|
-
test_properties[:event_sender] :
|
144
|
+
event_sender = (test_properties || {})[:event_sender] ||
|
121
145
|
Impl::EventSender.new(sdk_key, config, client ? client : Util.new_http_client(config.events_uri, config))
|
122
146
|
|
147
|
+
@timestamp_fn = (test_properties || {})[:timestamp_fn] || proc { Impl::Util.current_time_millis }
|
148
|
+
|
123
149
|
EventDispatcher.new(@inbox, sdk_key, config, diagnostic_accumulator, event_sender)
|
124
150
|
end
|
125
151
|
|
126
|
-
def
|
127
|
-
|
128
|
-
|
152
|
+
def record_eval_event(
|
153
|
+
user,
|
154
|
+
key,
|
155
|
+
version = nil,
|
156
|
+
variation = nil,
|
157
|
+
value = nil,
|
158
|
+
reason = nil,
|
159
|
+
default = nil,
|
160
|
+
track_events = false,
|
161
|
+
debug_until = nil,
|
162
|
+
prereq_of = nil
|
163
|
+
)
|
164
|
+
post_to_inbox(LaunchDarkly::Impl::EvalEvent.new(timestamp, user, key, version, variation, value, reason,
|
165
|
+
default, track_events, debug_until, prereq_of))
|
166
|
+
end
|
167
|
+
|
168
|
+
def record_identify_event(user)
|
169
|
+
post_to_inbox(LaunchDarkly::Impl::IdentifyEvent.new(timestamp, user))
|
170
|
+
end
|
171
|
+
|
172
|
+
def record_custom_event(user, key, data = nil, metric_value = nil)
|
173
|
+
post_to_inbox(LaunchDarkly::Impl::CustomEvent.new(timestamp, user, key, data, metric_value))
|
174
|
+
end
|
175
|
+
|
176
|
+
def record_alias_event(user, previous_user)
|
177
|
+
post_to_inbox(LaunchDarkly::Impl::AliasEvent.new(
|
178
|
+
timestamp,
|
179
|
+
user.nil? ? nil : user[:key],
|
180
|
+
user_to_context_kind(user),
|
181
|
+
previous_user.nil? ? nil : previous_user[:key],
|
182
|
+
user_to_context_kind(previous_user)
|
183
|
+
))
|
129
184
|
end
|
130
185
|
|
131
186
|
def flush
|
@@ -155,9 +210,11 @@ module LaunchDarkly
|
|
155
210
|
sync_msg.wait_for_completion
|
156
211
|
end
|
157
212
|
|
158
|
-
private
|
213
|
+
private def timestamp
|
214
|
+
@timestamp_fn.call()
|
215
|
+
end
|
159
216
|
|
160
|
-
def post_to_inbox(message)
|
217
|
+
private def post_to_inbox(message)
|
161
218
|
begin
|
162
219
|
@inbox.push(message, non_block=true)
|
163
220
|
rescue ThreadError
|
@@ -170,6 +227,10 @@ module LaunchDarkly
|
|
170
227
|
end
|
171
228
|
end
|
172
229
|
end
|
230
|
+
|
231
|
+
private def user_to_context_kind(user)
|
232
|
+
(user.nil? || !user[:anonymous]) ? 'user' : 'anonymousUser'
|
233
|
+
end
|
173
234
|
end
|
174
235
|
|
175
236
|
# @private
|
@@ -209,8 +270,6 @@ module LaunchDarkly
|
|
209
270
|
begin
|
210
271
|
message = inbox.pop
|
211
272
|
case message
|
212
|
-
when EventMessage
|
213
|
-
dispatch_event(message.event, outbox)
|
214
273
|
when FlushMessage
|
215
274
|
trigger_flush(outbox, flush_workers)
|
216
275
|
when FlushUsersMessage
|
@@ -224,6 +283,8 @@ module LaunchDarkly
|
|
224
283
|
do_shutdown(flush_workers, diagnostic_event_workers)
|
225
284
|
running = false
|
226
285
|
message.completed
|
286
|
+
else
|
287
|
+
dispatch_event(message, outbox)
|
227
288
|
end
|
228
289
|
rescue => e
|
229
290
|
Util.log_exception(@config.logger, "Unexpected error in event processor", e)
|
@@ -257,11 +318,10 @@ module LaunchDarkly
|
|
257
318
|
# the event (if tracked) and once for debugging.
|
258
319
|
will_add_full_event = false
|
259
320
|
debug_event = nil
|
260
|
-
if event
|
261
|
-
will_add_full_event = event
|
321
|
+
if event.is_a?(LaunchDarkly::Impl::EvalEvent)
|
322
|
+
will_add_full_event = event.track_events
|
262
323
|
if should_debug_event(event)
|
263
|
-
debug_event = event
|
264
|
-
debug_event[:debug] = true
|
324
|
+
debug_event = LaunchDarkly::Impl::DebugEvent.new(event)
|
265
325
|
end
|
266
326
|
else
|
267
327
|
will_add_full_event = true
|
@@ -270,12 +330,8 @@ module LaunchDarkly
|
|
270
330
|
# For each user we haven't seen before, we add an index event - unless this is already
|
271
331
|
# an identify event for that user.
|
272
332
|
if !(will_add_full_event && @config.inline_users_in_events)
|
273
|
-
if event.
|
274
|
-
outbox.add_event(
|
275
|
-
kind: "index",
|
276
|
-
creationDate: event[:creationDate],
|
277
|
-
user: event[:user]
|
278
|
-
})
|
333
|
+
if !event.user.nil? && !notice_user(event.user) && !event.is_a?(LaunchDarkly::Impl::IdentifyEvent)
|
334
|
+
outbox.add_event(LaunchDarkly::Impl::IndexEvent.new(event.timestamp, event.user))
|
279
335
|
end
|
280
336
|
end
|
281
337
|
|
@@ -295,7 +351,7 @@ module LaunchDarkly
|
|
295
351
|
end
|
296
352
|
|
297
353
|
def should_debug_event(event)
|
298
|
-
debug_until = event
|
354
|
+
debug_until = event.debug_until
|
299
355
|
if !debug_until.nil?
|
300
356
|
last_past = @last_known_past_time.value
|
301
357
|
debug_until > last_past && debug_until > Impl::Util.current_time_millis
|
@@ -365,12 +421,11 @@ module LaunchDarkly
|
|
365
421
|
@capacity_exceeded = false
|
366
422
|
@dropped_events = 0
|
367
423
|
@events = []
|
368
|
-
@summarizer = EventSummarizer.new
|
424
|
+
@summarizer = LaunchDarkly::Impl::EventSummarizer.new
|
369
425
|
end
|
370
426
|
|
371
427
|
def add_event(event)
|
372
428
|
if @events.length < @capacity
|
373
|
-
@logger.debug { "[LDClient] Enqueueing event: #{event.to_json}" }
|
374
429
|
@events.push(event)
|
375
430
|
@capacity_exceeded = false
|
376
431
|
else
|
@@ -404,6 +459,15 @@ module LaunchDarkly
|
|
404
459
|
|
405
460
|
# @private
|
406
461
|
class EventOutputFormatter
|
462
|
+
FEATURE_KIND = 'feature'
|
463
|
+
IDENTIFY_KIND = 'identify'
|
464
|
+
CUSTOM_KIND = 'custom'
|
465
|
+
ALIAS_KIND = 'alias'
|
466
|
+
INDEX_KIND = 'index'
|
467
|
+
DEBUG_KIND = 'debug'
|
468
|
+
SUMMARY_KIND = 'summary'
|
469
|
+
ANONYMOUS_USER_CONTEXT_KIND = 'anonymousUser'
|
470
|
+
|
407
471
|
def initialize(config)
|
408
472
|
@inline_users = config.inline_users_in_events
|
409
473
|
@user_filter = UserFilter.new(config)
|
@@ -418,100 +482,130 @@ module LaunchDarkly
|
|
418
482
|
events_out
|
419
483
|
end
|
420
484
|
|
421
|
-
private
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
Util.stringify_attrs(filtered, USER_ATTRS_TO_STRINGIFY_FOR_EVENTS)
|
426
|
-
end
|
427
|
-
|
428
|
-
def make_output_event(event)
|
429
|
-
case event[:kind]
|
430
|
-
when "feature"
|
431
|
-
is_debug = event[:debug]
|
485
|
+
private def make_output_event(event)
|
486
|
+
case event
|
487
|
+
|
488
|
+
when LaunchDarkly::Impl::EvalEvent
|
432
489
|
out = {
|
433
|
-
kind:
|
434
|
-
creationDate: event
|
435
|
-
key: event
|
436
|
-
value: event
|
490
|
+
kind: FEATURE_KIND,
|
491
|
+
creationDate: event.timestamp,
|
492
|
+
key: event.key,
|
493
|
+
value: event.value
|
437
494
|
}
|
438
|
-
out[:default] = event
|
439
|
-
out[:variation] = event
|
440
|
-
out[:version] = event
|
441
|
-
out[:prereqOf] = event
|
442
|
-
out
|
443
|
-
|
444
|
-
|
445
|
-
else
|
446
|
-
out[:userKey] = event[:user][:key]
|
447
|
-
end
|
448
|
-
out[:reason] = event[:reason] if !event[:reason].nil?
|
495
|
+
out[:default] = event.default if !event.default.nil?
|
496
|
+
out[:variation] = event.variation if !event.variation.nil?
|
497
|
+
out[:version] = event.version if !event.version.nil?
|
498
|
+
out[:prereqOf] = event.prereq_of if !event.prereq_of.nil?
|
499
|
+
set_opt_context_kind(out, event.user)
|
500
|
+
set_user_or_user_key(out, event.user)
|
501
|
+
out[:reason] = event.reason if !event.reason.nil?
|
449
502
|
out
|
450
|
-
|
503
|
+
|
504
|
+
when LaunchDarkly::Impl::IdentifyEvent
|
451
505
|
{
|
452
|
-
kind:
|
453
|
-
creationDate: event
|
454
|
-
key: event
|
455
|
-
user: process_user(event)
|
506
|
+
kind: IDENTIFY_KIND,
|
507
|
+
creationDate: event.timestamp,
|
508
|
+
key: event.user[:key].to_s,
|
509
|
+
user: process_user(event.user)
|
456
510
|
}
|
457
|
-
|
511
|
+
|
512
|
+
when LaunchDarkly::Impl::CustomEvent
|
458
513
|
out = {
|
459
|
-
kind:
|
460
|
-
creationDate: event
|
461
|
-
key: event
|
514
|
+
kind: CUSTOM_KIND,
|
515
|
+
creationDate: event.timestamp,
|
516
|
+
key: event.key
|
462
517
|
}
|
463
|
-
out[:data] = event
|
464
|
-
|
465
|
-
|
466
|
-
|
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)
|
518
|
+
out[:data] = event.data if !event.data.nil?
|
519
|
+
set_user_or_user_key(out, event.user)
|
520
|
+
out[:metricValue] = event.metric_value if !event.metric_value.nil?
|
521
|
+
set_opt_context_kind(out, event.user)
|
471
522
|
out
|
472
|
-
|
523
|
+
|
524
|
+
when LaunchDarkly::Impl::AliasEvent
|
525
|
+
{
|
526
|
+
kind: ALIAS_KIND,
|
527
|
+
creationDate: event.timestamp,
|
528
|
+
key: event.key,
|
529
|
+
contextKind: event.context_kind,
|
530
|
+
previousKey: event.previous_key,
|
531
|
+
previousContextKind: event.previous_context_kind
|
532
|
+
}
|
533
|
+
|
534
|
+
when LaunchDarkly::Impl::IndexEvent
|
473
535
|
{
|
474
|
-
kind:
|
475
|
-
creationDate: event
|
476
|
-
user: process_user(event)
|
536
|
+
kind: INDEX_KIND,
|
537
|
+
creationDate: event.timestamp,
|
538
|
+
user: process_user(event.user)
|
477
539
|
}
|
540
|
+
|
541
|
+
when LaunchDarkly::Impl::DebugEvent
|
542
|
+
original = event.eval_event
|
543
|
+
out = {
|
544
|
+
kind: DEBUG_KIND,
|
545
|
+
creationDate: original.timestamp,
|
546
|
+
key: original.key,
|
547
|
+
user: process_user(original.user),
|
548
|
+
value: original.value
|
549
|
+
}
|
550
|
+
out[:default] = original.default if !original.default.nil?
|
551
|
+
out[:variation] = original.variation if !original.variation.nil?
|
552
|
+
out[:version] = original.version if !original.version.nil?
|
553
|
+
out[:prereqOf] = original.prereq_of if !original.prereq_of.nil?
|
554
|
+
set_opt_context_kind(out, original.user)
|
555
|
+
out[:reason] = original.reason if !original.reason.nil?
|
556
|
+
out
|
557
|
+
|
478
558
|
else
|
479
|
-
|
559
|
+
nil
|
480
560
|
end
|
481
561
|
end
|
482
562
|
|
483
563
|
# Transforms the summary data into the format used for event sending.
|
484
|
-
def make_summary_event(summary)
|
564
|
+
private def make_summary_event(summary)
|
485
565
|
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]
|
566
|
+
summary.counters.each do |flagKey, flagInfo|
|
567
|
+
counters = []
|
568
|
+
flagInfo.versions.each do |version, variations|
|
569
|
+
variations.each do |variation, counter|
|
570
|
+
c = {
|
571
|
+
value: counter.value,
|
572
|
+
count: counter.count
|
573
|
+
}
|
574
|
+
c[:variation] = variation if !variation.nil?
|
575
|
+
if version.nil?
|
576
|
+
c[:unknown] = true
|
577
|
+
else
|
578
|
+
c[:version] = version
|
579
|
+
end
|
580
|
+
counters.push(c)
|
581
|
+
end
|
506
582
|
end
|
507
|
-
|
508
|
-
|
583
|
+
flags[flagKey] = { default: flagInfo.default, counters: counters }
|
584
|
+
end
|
509
585
|
{
|
510
|
-
kind:
|
586
|
+
kind: SUMMARY_KIND,
|
511
587
|
startDate: summary[:start_date],
|
512
588
|
endDate: summary[:end_date],
|
513
589
|
features: flags
|
514
590
|
}
|
515
591
|
end
|
592
|
+
|
593
|
+
private def set_opt_context_kind(out, user)
|
594
|
+
out[:contextKind] = ANONYMOUS_USER_CONTEXT_KIND if !user.nil? && user[:anonymous]
|
595
|
+
end
|
596
|
+
|
597
|
+
private def set_user_or_user_key(out, user)
|
598
|
+
if @inline_users
|
599
|
+
out[:user] = process_user(user)
|
600
|
+
else
|
601
|
+
key = user[:key]
|
602
|
+
out[:userKey] = key.is_a?(String) ? key : key.to_s
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
private def process_user(user)
|
607
|
+
filtered = @user_filter.transform_user_props(user)
|
608
|
+
Util.stringify_attrs(filtered, USER_ATTRS_TO_STRINGIFY_FOR_EVENTS)
|
609
|
+
end
|
516
610
|
end
|
517
611
|
end
|
@@ -4,6 +4,13 @@ require "ldclient-rb/impl/evaluator_operators"
|
|
4
4
|
|
5
5
|
module LaunchDarkly
|
6
6
|
module Impl
|
7
|
+
# Used internally to record that we evaluated a prerequisite flag.
|
8
|
+
PrerequisiteEvalRecord = Struct.new(
|
9
|
+
:prereq_flag, # the prerequisite flag that we evaluated
|
10
|
+
:prereq_of_flag, # the flag that it was a prerequisite of
|
11
|
+
:detail # the EvaluationDetail representing the evaluation result
|
12
|
+
)
|
13
|
+
|
7
14
|
# Encapsulates the feature flag evaluation logic. The Evaluator has no knowledge of the rest of the SDK environment;
|
8
15
|
# if it needs to retrieve flags or segments that are referenced by a flag, it does so through a simple function that
|
9
16
|
# is provided in the constructor. It also produces feature requests as appropriate for any referenced prerequisite
|
@@ -22,7 +29,7 @@ module LaunchDarkly
|
|
22
29
|
@get_big_segments_membership = get_big_segments_membership
|
23
30
|
@logger = logger
|
24
31
|
end
|
25
|
-
|
32
|
+
|
26
33
|
# Used internally to hold an evaluation result and additional state that may be accumulated during an
|
27
34
|
# evaluation. It's simpler and a bit more efficient to represent these as mutable properties rather than
|
28
35
|
# trying to use a pure functional approach, and since we're not exposing this object to any application code
|
@@ -34,7 +41,7 @@ module LaunchDarkly
|
|
34
41
|
# evaluation.
|
35
42
|
EvalResult = Struct.new(
|
36
43
|
:detail, # the EvaluationDetail representing the evaluation result
|
37
|
-
:
|
44
|
+
:prereq_evals, # an array of PrerequisiteEvalRecord instances, or nil
|
38
45
|
:big_segments_status,
|
39
46
|
:big_segments_membership
|
40
47
|
)
|
@@ -50,17 +57,15 @@ module LaunchDarkly
|
|
50
57
|
#
|
51
58
|
# @param flag [Object] the flag
|
52
59
|
# @param user [Object] the user properties
|
53
|
-
# @param event_factory [EventFactory] called to construct a feature request event when a prerequisite flag is
|
54
|
-
# evaluated; the caller is responsible for constructing the feature event for the top-level evaluation
|
55
60
|
# @return [EvalResult] the evaluation result
|
56
|
-
def evaluate(flag, user
|
61
|
+
def evaluate(flag, user)
|
57
62
|
result = EvalResult.new
|
58
63
|
if user.nil? || user[:key].nil?
|
59
64
|
result.detail = Evaluator.error_result(EvaluationReason::ERROR_USER_NOT_SPECIFIED)
|
60
65
|
return result
|
61
66
|
end
|
62
67
|
|
63
|
-
detail = eval_internal(flag, user, result
|
68
|
+
detail = eval_internal(flag, user, result)
|
64
69
|
if !result.big_segments_status.nil?
|
65
70
|
# If big_segments_status is non-nil at the end of the evaluation, it means a query was done at
|
66
71
|
# some point and we will want to include the status in the evaluation reason.
|
@@ -80,12 +85,12 @@ module LaunchDarkly
|
|
80
85
|
|
81
86
|
private
|
82
87
|
|
83
|
-
def eval_internal(flag, user, state
|
88
|
+
def eval_internal(flag, user, state)
|
84
89
|
if !flag[:on]
|
85
90
|
return get_off_value(flag, EvaluationReason::off)
|
86
91
|
end
|
87
92
|
|
88
|
-
prereq_failure_reason = check_prerequisites(flag, user, state
|
93
|
+
prereq_failure_reason = check_prerequisites(flag, user, state)
|
89
94
|
if !prereq_failure_reason.nil?
|
90
95
|
return get_off_value(flag, prereq_failure_reason)
|
91
96
|
end
|
@@ -118,7 +123,7 @@ module LaunchDarkly
|
|
118
123
|
return EvaluationDetail.new(nil, nil, EvaluationReason::fallthrough)
|
119
124
|
end
|
120
125
|
|
121
|
-
def check_prerequisites(flag, user, state
|
126
|
+
def check_prerequisites(flag, user, state)
|
122
127
|
(flag[:prerequisites] || []).each do |prerequisite|
|
123
128
|
prereq_ok = true
|
124
129
|
prereq_key = prerequisite[:key]
|
@@ -129,15 +134,15 @@ module LaunchDarkly
|
|
129
134
|
prereq_ok = false
|
130
135
|
else
|
131
136
|
begin
|
132
|
-
prereq_res = eval_internal(prereq_flag, user, state
|
137
|
+
prereq_res = eval_internal(prereq_flag, user, state)
|
133
138
|
# Note that if the prerequisite flag is off, we don't consider it a match no matter what its
|
134
139
|
# off variation was. But we still need to evaluate it in order to generate an event.
|
135
140
|
if !prereq_flag[:on] || prereq_res.variation_index != prerequisite[:variation]
|
136
141
|
prereq_ok = false
|
137
142
|
end
|
138
|
-
|
139
|
-
state.
|
140
|
-
state.
|
143
|
+
prereq_eval = PrerequisiteEvalRecord.new(prereq_flag, flag, prereq_res)
|
144
|
+
state.prereq_evals = [] if state.prereq_evals.nil?
|
145
|
+
state.prereq_evals.push(prereq_eval)
|
141
146
|
rescue => exn
|
142
147
|
Util.log_exception(@logger, "Error evaluating prerequisite flag \"#{prereq_key}\" for flag \"#{flag[:key]}\"", exn)
|
143
148
|
prereq_ok = false
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "ldclient-rb/impl/event_types"
|
2
|
+
|
3
|
+
module LaunchDarkly
|
4
|
+
module Impl
|
5
|
+
EventSummary = Struct.new(:start_date, :end_date, :counters)
|
6
|
+
|
7
|
+
EventSummaryFlagInfo = Struct.new(:default, :versions)
|
8
|
+
|
9
|
+
EventSummaryFlagVariationCounter = Struct.new(:value, :count)
|
10
|
+
|
11
|
+
# Manages the state of summarizable information for the EventProcessor, including the
|
12
|
+
# event counters and user deduplication. Note that the methods of this class are
|
13
|
+
# deliberately not thread-safe; the EventProcessor is responsible for enforcing
|
14
|
+
# synchronization across both the summarizer and the event queue.
|
15
|
+
class EventSummarizer
|
16
|
+
class Counter
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
clear
|
21
|
+
end
|
22
|
+
|
23
|
+
# Adds this event to our counters, if it is a type of event we need to count.
|
24
|
+
def summarize_event(event)
|
25
|
+
return if !event.is_a?(LaunchDarkly::Impl::EvalEvent)
|
26
|
+
|
27
|
+
counters_for_flag = @counters[event.key]
|
28
|
+
if counters_for_flag.nil?
|
29
|
+
counters_for_flag = EventSummaryFlagInfo.new(event.default, Hash.new)
|
30
|
+
@counters[event.key] = counters_for_flag
|
31
|
+
end
|
32
|
+
counters_for_flag_version = counters_for_flag.versions[event.version]
|
33
|
+
if counters_for_flag_version.nil?
|
34
|
+
counters_for_flag_version = Hash.new
|
35
|
+
counters_for_flag.versions[event.version] = counters_for_flag_version
|
36
|
+
end
|
37
|
+
variation_counter = counters_for_flag_version[event.variation]
|
38
|
+
if variation_counter.nil?
|
39
|
+
counters_for_flag_version[event.variation] = EventSummaryFlagVariationCounter.new(event.value, 1)
|
40
|
+
else
|
41
|
+
variation_counter.count = variation_counter.count + 1
|
42
|
+
end
|
43
|
+
time = event.timestamp
|
44
|
+
if !time.nil?
|
45
|
+
@start_date = time if @start_date == 0 || time < @start_date
|
46
|
+
@end_date = time if time > @end_date
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns a snapshot of the current summarized event data, and resets this state.
|
51
|
+
def snapshot
|
52
|
+
ret = EventSummary.new(@start_date, @end_date, @counters)
|
53
|
+
ret
|
54
|
+
end
|
55
|
+
|
56
|
+
def clear
|
57
|
+
@start_date = 0
|
58
|
+
@end_date = 0
|
59
|
+
@counters = {}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module LaunchDarkly
|
2
|
+
module Impl
|
3
|
+
class Event
|
4
|
+
def initialize(timestamp, user)
|
5
|
+
@timestamp = timestamp
|
6
|
+
@user = user
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :timestamp
|
10
|
+
attr_reader :kind
|
11
|
+
attr_reader :user
|
12
|
+
end
|
13
|
+
|
14
|
+
class EvalEvent < Event
|
15
|
+
def initialize(timestamp, user, key, version = nil, variation = nil, value = nil, reason = nil, default = nil,
|
16
|
+
track_events = false, debug_until = nil, prereq_of = nil)
|
17
|
+
super(timestamp, user)
|
18
|
+
@key = key
|
19
|
+
@version = version
|
20
|
+
@variation = variation
|
21
|
+
@value = value
|
22
|
+
@reason = reason
|
23
|
+
@default = default
|
24
|
+
# avoid setting rarely-used attributes if they have no value - this saves a little space per instance
|
25
|
+
@track_events = track_events if track_events
|
26
|
+
@debug_until = debug_until if debug_until
|
27
|
+
@prereq_of = prereq_of if prereq_of
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :key
|
31
|
+
attr_reader :version
|
32
|
+
attr_reader :variation
|
33
|
+
attr_reader :value
|
34
|
+
attr_reader :reason
|
35
|
+
attr_reader :default
|
36
|
+
attr_reader :track_events
|
37
|
+
attr_reader :debug_until
|
38
|
+
attr_reader :prereq_of
|
39
|
+
end
|
40
|
+
|
41
|
+
class IdentifyEvent < Event
|
42
|
+
def initialize(timestamp, user)
|
43
|
+
super(timestamp, user)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class CustomEvent < Event
|
48
|
+
def initialize(timestamp, user, key, data = nil, metric_value = nil)
|
49
|
+
super(timestamp, user)
|
50
|
+
@key = key
|
51
|
+
@data = data if !data.nil?
|
52
|
+
@metric_value = metric_value if !metric_value.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
attr_reader :key
|
56
|
+
attr_reader :data
|
57
|
+
attr_reader :metric_value
|
58
|
+
end
|
59
|
+
|
60
|
+
class AliasEvent < Event
|
61
|
+
def initialize(timestamp, key, context_kind, previous_key, previous_context_kind)
|
62
|
+
super(timestamp, nil)
|
63
|
+
@key = key
|
64
|
+
@context_kind = context_kind
|
65
|
+
@previous_key = previous_key
|
66
|
+
@previous_context_kind = previous_context_kind
|
67
|
+
end
|
68
|
+
|
69
|
+
attr_reader :key
|
70
|
+
attr_reader :context_kind
|
71
|
+
attr_reader :previous_key
|
72
|
+
attr_reader :previous_context_kind
|
73
|
+
end
|
74
|
+
|
75
|
+
class IndexEvent < Event
|
76
|
+
def initialize(timestamp, user)
|
77
|
+
super(timestamp, user)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class DebugEvent < Event
|
82
|
+
def initialize(eval_event)
|
83
|
+
super(eval_event.timestamp, eval_event.user)
|
84
|
+
@eval_event = eval_event
|
85
|
+
end
|
86
|
+
|
87
|
+
attr_reader :eval_event
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/ldclient-rb/ldclient.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require "ldclient-rb/impl/big_segments"
|
2
2
|
require "ldclient-rb/impl/diagnostic_events"
|
3
3
|
require "ldclient-rb/impl/evaluator"
|
4
|
-
require "ldclient-rb/impl/event_factory"
|
5
4
|
require "ldclient-rb/impl/store_client_wrapper"
|
6
5
|
require "concurrent/atomics"
|
7
6
|
require "digest/sha1"
|
@@ -46,9 +45,6 @@ module LaunchDarkly
|
|
46
45
|
|
47
46
|
@sdk_key = sdk_key
|
48
47
|
|
49
|
-
@event_factory_default = EventFactory.new(false)
|
50
|
-
@event_factory_with_reasons = EventFactory.new(true)
|
51
|
-
|
52
48
|
# We need to wrap the feature store object with a FeatureStoreClientWrapper in order to add
|
53
49
|
# some necessary logic around updates. Unfortunately, we have code elsewhere that accesses
|
54
50
|
# the feature store through the Config object, so we need to make a new Config that uses
|
@@ -202,7 +198,7 @@ module LaunchDarkly
|
|
202
198
|
# @return the variation to show the user, or the default value if there's an an error
|
203
199
|
#
|
204
200
|
def variation(key, user, default)
|
205
|
-
evaluate_internal(key, user, default,
|
201
|
+
evaluate_internal(key, user, default, false).value
|
206
202
|
end
|
207
203
|
|
208
204
|
#
|
@@ -229,7 +225,7 @@ module LaunchDarkly
|
|
229
225
|
# @return [EvaluationDetail] an object describing the result
|
230
226
|
#
|
231
227
|
def variation_detail(key, user, default)
|
232
|
-
evaluate_internal(key, user, default,
|
228
|
+
evaluate_internal(key, user, default, true)
|
233
229
|
end
|
234
230
|
|
235
231
|
#
|
@@ -253,7 +249,7 @@ module LaunchDarkly
|
|
253
249
|
return
|
254
250
|
end
|
255
251
|
sanitize_user(user)
|
256
|
-
@event_processor.
|
252
|
+
@event_processor.record_identify_event(user)
|
257
253
|
end
|
258
254
|
|
259
255
|
#
|
@@ -284,7 +280,7 @@ module LaunchDarkly
|
|
284
280
|
return
|
285
281
|
end
|
286
282
|
sanitize_user(user)
|
287
|
-
@event_processor.
|
283
|
+
@event_processor.record_custom_event(user, event_name, data, metric_value)
|
288
284
|
end
|
289
285
|
|
290
286
|
#
|
@@ -301,7 +297,7 @@ module LaunchDarkly
|
|
301
297
|
end
|
302
298
|
sanitize_user(current_context)
|
303
299
|
sanitize_user(previous_context)
|
304
|
-
@event_processor.
|
300
|
+
@event_processor.record_alias_event(current_context, previous_context)
|
305
301
|
end
|
306
302
|
|
307
303
|
#
|
@@ -368,13 +364,13 @@ module LaunchDarkly
|
|
368
364
|
next
|
369
365
|
end
|
370
366
|
begin
|
371
|
-
detail = @evaluator.evaluate(f, user
|
367
|
+
detail = @evaluator.evaluate(f, user).detail
|
372
368
|
rescue => exn
|
373
369
|
detail = EvaluationDetail.new(nil, nil, EvaluationReason::error(EvaluationReason::ERROR_EXCEPTION))
|
374
370
|
Util.log_exception(@config.logger, "Error evaluating flag \"#{k}\" in all_flags_state", exn)
|
375
371
|
end
|
376
372
|
|
377
|
-
requires_experiment_data =
|
373
|
+
requires_experiment_data = is_experiment(f, detail.reason)
|
378
374
|
flag_state = {
|
379
375
|
key: f[:key],
|
380
376
|
value: detail.value,
|
@@ -430,7 +426,7 @@ module LaunchDarkly
|
|
430
426
|
end
|
431
427
|
|
432
428
|
# @return [EvaluationDetail]
|
433
|
-
def evaluate_internal(key, user, default,
|
429
|
+
def evaluate_internal(key, user, default, with_reasons)
|
434
430
|
if @config.offline?
|
435
431
|
return Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default)
|
436
432
|
end
|
@@ -453,7 +449,7 @@ module LaunchDarkly
|
|
453
449
|
else
|
454
450
|
@config.logger.error { "[LDClient] Client has not finished initializing; feature store unavailable, returning default value" }
|
455
451
|
detail = Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default)
|
456
|
-
|
452
|
+
record_unknown_flag_eval(key, user, default, detail.reason, with_reasons)
|
457
453
|
return detail
|
458
454
|
end
|
459
455
|
end
|
@@ -463,32 +459,94 @@ module LaunchDarkly
|
|
463
459
|
if feature.nil?
|
464
460
|
@config.logger.info { "[LDClient] Unknown feature flag \"#{key}\". Returning default value" }
|
465
461
|
detail = Evaluator.error_result(EvaluationReason::ERROR_FLAG_NOT_FOUND, default)
|
466
|
-
|
462
|
+
record_unknown_flag_eval(key, user, default, detail.reason, with_reasons)
|
467
463
|
return detail
|
468
464
|
end
|
469
465
|
|
470
466
|
begin
|
471
|
-
res = @evaluator.evaluate(feature, user
|
472
|
-
if !res.
|
473
|
-
res.
|
474
|
-
|
467
|
+
res = @evaluator.evaluate(feature, user)
|
468
|
+
if !res.prereq_evals.nil?
|
469
|
+
res.prereq_evals.each do |prereq_eval|
|
470
|
+
record_prereq_flag_eval(prereq_eval.prereq_flag, prereq_eval.prereq_of_flag, user, prereq_eval.detail, with_reasons)
|
475
471
|
end
|
476
472
|
end
|
477
473
|
detail = res.detail
|
478
474
|
if detail.default_value?
|
479
475
|
detail = EvaluationDetail.new(default, nil, detail.reason)
|
480
476
|
end
|
481
|
-
|
477
|
+
record_flag_eval(feature, user, detail, default, with_reasons)
|
482
478
|
return detail
|
483
479
|
rescue => exn
|
484
480
|
Util.log_exception(@config.logger, "Error evaluating feature flag \"#{key}\"", exn)
|
485
481
|
detail = Evaluator.error_result(EvaluationReason::ERROR_EXCEPTION, default)
|
486
|
-
|
482
|
+
record_flag_eval_error(feature, user, default, detail.reason, with_reasons)
|
487
483
|
return detail
|
488
484
|
end
|
489
485
|
end
|
490
486
|
|
491
|
-
def
|
487
|
+
private def record_flag_eval(flag, user, detail, default, with_reasons)
|
488
|
+
add_experiment_data = is_experiment(flag, detail.reason)
|
489
|
+
@event_processor.record_eval_event(
|
490
|
+
user,
|
491
|
+
flag[:key],
|
492
|
+
flag[:version],
|
493
|
+
detail.variation_index,
|
494
|
+
detail.value,
|
495
|
+
(add_experiment_data || with_reasons) ? detail.reason : nil,
|
496
|
+
default,
|
497
|
+
add_experiment_data || flag[:trackEvents] || false,
|
498
|
+
flag[:debugEventsUntilDate],
|
499
|
+
nil
|
500
|
+
)
|
501
|
+
end
|
502
|
+
|
503
|
+
private def record_prereq_flag_eval(prereq_flag, prereq_of_flag, user, detail, with_reasons)
|
504
|
+
add_experiment_data = is_experiment(prereq_flag, detail.reason)
|
505
|
+
@event_processor.record_eval_event(
|
506
|
+
user,
|
507
|
+
prereq_flag[:key],
|
508
|
+
prereq_flag[:version],
|
509
|
+
detail.variation_index,
|
510
|
+
detail.value,
|
511
|
+
(add_experiment_data || with_reasons) ? detail.reason : nil,
|
512
|
+
nil,
|
513
|
+
add_experiment_data || prereq_flag[:trackEvents] || false,
|
514
|
+
prereq_flag[:debugEventsUntilDate],
|
515
|
+
prereq_of_flag[:key]
|
516
|
+
)
|
517
|
+
end
|
518
|
+
|
519
|
+
private def record_flag_eval_error(flag, user, default, reason, with_reasons)
|
520
|
+
@event_processor.record_eval_event(user, flag[:key], flag[:version], nil, default, with_reasons ? reason : nil, default,
|
521
|
+
flag[:trackEvents], flag[:debugEventsUntilDate], nil)
|
522
|
+
end
|
523
|
+
|
524
|
+
private def record_unknown_flag_eval(flag_key, user, default, reason, with_reasons)
|
525
|
+
@event_processor.record_eval_event(user, flag_key, nil, nil, default, with_reasons ? reason : nil, default,
|
526
|
+
false, nil, nil)
|
527
|
+
end
|
528
|
+
|
529
|
+
private def is_experiment(flag, reason)
|
530
|
+
return false if !reason
|
531
|
+
|
532
|
+
if reason.in_experiment
|
533
|
+
return true
|
534
|
+
end
|
535
|
+
|
536
|
+
case reason[:kind]
|
537
|
+
when 'RULE_MATCH'
|
538
|
+
index = reason[:ruleIndex]
|
539
|
+
if !index.nil?
|
540
|
+
rules = flag[:rules] || []
|
541
|
+
return index >= 0 && index < rules.length && rules[index][:trackEvents]
|
542
|
+
end
|
543
|
+
when 'FALLTHROUGH'
|
544
|
+
return !!flag[:trackEventsFallthrough]
|
545
|
+
end
|
546
|
+
false
|
547
|
+
end
|
548
|
+
|
549
|
+
private def sanitize_user(user)
|
492
550
|
if user[:key]
|
493
551
|
user[:key] = user[:key].to_s
|
494
552
|
end
|
data/lib/ldclient-rb/version.rb
CHANGED
data/lib/ldclient-rb.rb
CHANGED
@@ -21,7 +21,6 @@ require "ldclient-rb/polling"
|
|
21
21
|
require "ldclient-rb/user_filter"
|
22
22
|
require "ldclient-rb/simple_lru_cache"
|
23
23
|
require "ldclient-rb/non_blocking_thread_pool"
|
24
|
-
require "ldclient-rb/event_summarizer"
|
25
24
|
require "ldclient-rb/events"
|
26
25
|
require "ldclient-rb/requestor"
|
27
26
|
require "ldclient-rb/file_data_source"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: launchdarkly-server-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.3.
|
4
|
+
version: 6.3.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- LaunchDarkly
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-06-
|
11
|
+
date: 2022-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-dynamodb
|
@@ -254,7 +254,6 @@ files:
|
|
254
254
|
- lib/ldclient-rb/cache_store.rb
|
255
255
|
- lib/ldclient-rb/config.rb
|
256
256
|
- lib/ldclient-rb/evaluation_detail.rb
|
257
|
-
- lib/ldclient-rb/event_summarizer.rb
|
258
257
|
- lib/ldclient-rb/events.rb
|
259
258
|
- lib/ldclient-rb/expiring_cache.rb
|
260
259
|
- lib/ldclient-rb/file_data_source.rb
|
@@ -265,8 +264,9 @@ files:
|
|
265
264
|
- lib/ldclient-rb/impl/evaluator.rb
|
266
265
|
- lib/ldclient-rb/impl/evaluator_bucketing.rb
|
267
266
|
- lib/ldclient-rb/impl/evaluator_operators.rb
|
268
|
-
- lib/ldclient-rb/impl/event_factory.rb
|
269
267
|
- lib/ldclient-rb/impl/event_sender.rb
|
268
|
+
- lib/ldclient-rb/impl/event_summarizer.rb
|
269
|
+
- lib/ldclient-rb/impl/event_types.rb
|
270
270
|
- lib/ldclient-rb/impl/integrations/consul_impl.rb
|
271
271
|
- lib/ldclient-rb/impl/integrations/dynamodb_impl.rb
|
272
272
|
- lib/ldclient-rb/impl/integrations/file_data_source.rb
|
@@ -319,7 +319,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
319
319
|
- !ruby/object:Gem::Version
|
320
320
|
version: '0'
|
321
321
|
requirements: []
|
322
|
-
rubygems_version: 3.3.
|
322
|
+
rubygems_version: 3.3.17
|
323
323
|
signing_key:
|
324
324
|
specification_version: 4
|
325
325
|
summary: LaunchDarkly SDK for Ruby
|
@@ -1,55 +0,0 @@
|
|
1
|
-
|
2
|
-
module LaunchDarkly
|
3
|
-
# @private
|
4
|
-
EventSummary = Struct.new(:start_date, :end_date, :counters)
|
5
|
-
|
6
|
-
# Manages the state of summarizable information for the EventProcessor, including the
|
7
|
-
# event counters and user deduplication. Note that the methods of this class are
|
8
|
-
# deliberately not thread-safe; the EventProcessor is responsible for enforcing
|
9
|
-
# synchronization across both the summarizer and the event queue.
|
10
|
-
#
|
11
|
-
# @private
|
12
|
-
class EventSummarizer
|
13
|
-
def initialize
|
14
|
-
clear
|
15
|
-
end
|
16
|
-
|
17
|
-
# Adds this event to our counters, if it is a type of event we need to count.
|
18
|
-
def summarize_event(event)
|
19
|
-
if event[:kind] == "feature"
|
20
|
-
counter_key = {
|
21
|
-
key: event[:key],
|
22
|
-
version: event[:version],
|
23
|
-
variation: event[:variation]
|
24
|
-
}
|
25
|
-
c = @counters[counter_key]
|
26
|
-
if c.nil?
|
27
|
-
@counters[counter_key] = {
|
28
|
-
value: event[:value],
|
29
|
-
default: event[:default],
|
30
|
-
count: 1
|
31
|
-
}
|
32
|
-
else
|
33
|
-
c[:count] = c[:count] + 1
|
34
|
-
end
|
35
|
-
time = event[:creationDate]
|
36
|
-
if !time.nil?
|
37
|
-
@start_date = time if @start_date == 0 || time < @start_date
|
38
|
-
@end_date = time if time > @end_date
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Returns a snapshot of the current summarized event data, and resets this state.
|
44
|
-
def snapshot
|
45
|
-
ret = EventSummary.new(@start_date, @end_date, @counters)
|
46
|
-
ret
|
47
|
-
end
|
48
|
-
|
49
|
-
def clear
|
50
|
-
@start_date = 0
|
51
|
-
@end_date = 0
|
52
|
-
@counters = {}
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
@@ -1,123 +0,0 @@
|
|
1
|
-
|
2
|
-
module LaunchDarkly
|
3
|
-
module Impl
|
4
|
-
# Event constructors are centralized here to avoid mistakes and repetitive logic.
|
5
|
-
# The LDClient owns two instances of EventFactory: one that always embeds evaluation reasons
|
6
|
-
# in the events (for when variation_detail is called) and one that doesn't.
|
7
|
-
#
|
8
|
-
# Note that these methods do not set the "creationDate" property, because in the Ruby client,
|
9
|
-
# that is done by EventProcessor.add_event().
|
10
|
-
class EventFactory
|
11
|
-
def initialize(with_reasons)
|
12
|
-
@with_reasons = with_reasons
|
13
|
-
end
|
14
|
-
|
15
|
-
def new_eval_event(flag, user, detail, default_value, prereq_of_flag = nil)
|
16
|
-
add_experiment_data = self.class.is_experiment(flag, detail.reason)
|
17
|
-
e = {
|
18
|
-
kind: 'feature',
|
19
|
-
key: flag[:key],
|
20
|
-
user: user,
|
21
|
-
variation: detail.variation_index,
|
22
|
-
value: detail.value,
|
23
|
-
default: default_value,
|
24
|
-
version: flag[:version]
|
25
|
-
}
|
26
|
-
# the following properties are handled separately so we don't waste bandwidth on unused keys
|
27
|
-
e[:trackEvents] = true if add_experiment_data || flag[:trackEvents]
|
28
|
-
e[:debugEventsUntilDate] = flag[:debugEventsUntilDate] if flag[:debugEventsUntilDate]
|
29
|
-
e[:prereqOf] = prereq_of_flag[:key] if !prereq_of_flag.nil?
|
30
|
-
e[:reason] = detail.reason if add_experiment_data || @with_reasons
|
31
|
-
e[:contextKind] = context_to_context_kind(user) if !user.nil? && user[:anonymous]
|
32
|
-
e
|
33
|
-
end
|
34
|
-
|
35
|
-
def new_default_event(flag, user, default_value, reason)
|
36
|
-
e = {
|
37
|
-
kind: 'feature',
|
38
|
-
key: flag[:key],
|
39
|
-
user: user,
|
40
|
-
value: default_value,
|
41
|
-
default: default_value,
|
42
|
-
version: flag[:version]
|
43
|
-
}
|
44
|
-
e[:trackEvents] = true if flag[:trackEvents]
|
45
|
-
e[:debugEventsUntilDate] = flag[:debugEventsUntilDate] if flag[:debugEventsUntilDate]
|
46
|
-
e[:reason] = reason if @with_reasons
|
47
|
-
e[:contextKind] = context_to_context_kind(user) if !user.nil? && user[:anonymous]
|
48
|
-
e
|
49
|
-
end
|
50
|
-
|
51
|
-
def new_unknown_flag_event(key, user, default_value, reason)
|
52
|
-
e = {
|
53
|
-
kind: 'feature',
|
54
|
-
key: key,
|
55
|
-
user: user,
|
56
|
-
value: default_value,
|
57
|
-
default: default_value
|
58
|
-
}
|
59
|
-
e[:reason] = reason if @with_reasons
|
60
|
-
e[:contextKind] = context_to_context_kind(user) if !user.nil? && user[:anonymous]
|
61
|
-
e
|
62
|
-
end
|
63
|
-
|
64
|
-
def new_identify_event(user)
|
65
|
-
{
|
66
|
-
kind: 'identify',
|
67
|
-
key: user[:key],
|
68
|
-
user: user
|
69
|
-
}
|
70
|
-
end
|
71
|
-
|
72
|
-
def new_alias_event(current_context, previous_context)
|
73
|
-
{
|
74
|
-
kind: 'alias',
|
75
|
-
key: current_context[:key],
|
76
|
-
contextKind: context_to_context_kind(current_context),
|
77
|
-
previousKey: previous_context[:key],
|
78
|
-
previousContextKind: context_to_context_kind(previous_context)
|
79
|
-
}
|
80
|
-
end
|
81
|
-
|
82
|
-
def new_custom_event(event_name, user, data, metric_value)
|
83
|
-
e = {
|
84
|
-
kind: 'custom',
|
85
|
-
key: event_name,
|
86
|
-
user: user
|
87
|
-
}
|
88
|
-
e[:data] = data if !data.nil?
|
89
|
-
e[:metricValue] = metric_value if !metric_value.nil?
|
90
|
-
e[:contextKind] = context_to_context_kind(user) if !user.nil? && user[:anonymous]
|
91
|
-
e
|
92
|
-
end
|
93
|
-
|
94
|
-
def self.is_experiment(flag, reason)
|
95
|
-
return false if !reason
|
96
|
-
|
97
|
-
if reason.in_experiment
|
98
|
-
return true
|
99
|
-
end
|
100
|
-
|
101
|
-
case reason[:kind]
|
102
|
-
when 'RULE_MATCH'
|
103
|
-
index = reason[:ruleIndex]
|
104
|
-
if !index.nil?
|
105
|
-
rules = flag[:rules] || []
|
106
|
-
return index >= 0 && index < rules.length && rules[index][:trackEvents]
|
107
|
-
end
|
108
|
-
when 'FALLTHROUGH'
|
109
|
-
return !!flag[:trackEventsFallthrough]
|
110
|
-
end
|
111
|
-
false
|
112
|
-
end
|
113
|
-
|
114
|
-
private def context_to_context_kind(user)
|
115
|
-
if !user.nil? && user[:anonymous]
|
116
|
-
return "anonymousUser"
|
117
|
-
else
|
118
|
-
return "user"
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|