launchdarkly-server-sdk 6.3.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d21f900f036d19f8b33271d825f3c00cda01d0ac44caf875774a196950ce2479
4
- data.tar.gz: f409fa9e445a0387ef96256d6a8cc57320ebe354cc07cc9f1f78897eff5fc82f
3
+ metadata.gz: 50934709e1ee8c075c56c149f75753f87498eb9e1f06e39597d2cd4514ea7196
4
+ data.tar.gz: e7f153366b7b50c3951f844bf9ce1a0b81093fc733818dbb02e8f0a888d9ac26
5
5
  SHA512:
6
- metadata.gz: 21b358fa934cbe2d5c06e9f941ebbfeb785a3d93b2bbe562859fc59af3137b0b5673de8794d1d4a32c33f2e12d212c27451242ef85c9341ac2c0c59b80d5c5d5
7
- data.tar.gz: fef2084a06846e16047d96a784b1d00351d45489baa657a48970a28190ebab67fdcc39a1716138c5b2c7f6d210be97d8289863d6f04c6a36a8bec2b5a8d49389
6
+ metadata.gz: 2469e536ca482489eb46f3ff650aeb573eb691ab6c6c2b8ed669b7f8ba799bf65d68d40f6cabc51ec4b051dd56e309430f94e5dc4fbbd70193efa5156d1d1082
7
+ data.tar.gz: 454219746c7b296fd8c92ee5e266f96765f72527706b7d6f7360445eb1d245e2acb37e4cb722ed48bf73dce8e307d2121771270ea9bedb71e91d67f9a79b8ea0
data/README.md CHANGED
@@ -3,8 +3,7 @@ LaunchDarkly Server-side SDK for Ruby
3
3
 
4
4
  [![Gem Version](https://badge.fury.io/rb/launchdarkly-server-sdk.svg)](http://badge.fury.io/rb/launchdarkly-server-sdk)
5
5
 
6
- [![Circle CI](https://circleci.com/gh/launchdarkly/ruby-server-sdk/tree/master.svg?style=svg)](https://circleci.com/gh/launchdarkly/ruby-server-sdk/tree/master)
7
- [![Security](https://hakiri.io/github/launchdarkly/ruby-server-sdk/master.svg)](https://hakiri.io/github/launchdarkly/ruby-server-sdk/master)
6
+ [![Circle CI](https://circleci.com/gh/launchdarkly/ruby-server-sdk/tree/main.svg?style=svg)](https://circleci.com/gh/launchdarkly/ruby-server-sdk/tree/main)
8
7
  [![RubyDoc](https://img.shields.io/static/v1?label=docs+-+all+versions&message=reference&color=00add8)](https://www.rubydoc.info/gems/launchdarkly-server-sdk)
9
8
  [![GitHub Pages](https://img.shields.io/static/v1?label=docs+-+latest&message=reference&color=00add8)](https://launchdarkly.github.io/ruby-server-sdk)
10
9
 
@@ -21,6 +21,7 @@ module LaunchDarkly
21
21
  # @option opts [Integer] :capacity (10000) See {#capacity}.
22
22
  # @option opts [Float] :flush_interval (30) See {#flush_interval}.
23
23
  # @option opts [Float] :read_timeout (10) See {#read_timeout}.
24
+ # @option opts [Float] :initial_reconnect_delay (1) See {#initial_reconnect_delay}.
24
25
  # @option opts [Float] :connect_timeout (2) See {#connect_timeout}.
25
26
  # @option opts [Object] :cache_store See {#cache_store}.
26
27
  # @option opts [Object] :feature_store See {#feature_store}.
@@ -54,6 +55,7 @@ module LaunchDarkly
54
55
  @flush_interval = opts[:flush_interval] || Config.default_flush_interval
55
56
  @connect_timeout = opts[:connect_timeout] || Config.default_connect_timeout
56
57
  @read_timeout = opts[:read_timeout] || Config.default_read_timeout
58
+ @initial_reconnect_delay = opts[:initial_reconnect_delay] || Config.default_initial_reconnect_delay
57
59
  @feature_store = opts[:feature_store] || Config.default_feature_store
58
60
  @stream = opts.has_key?(:stream) ? opts[:stream] : Config.default_stream
59
61
  @use_ldd = opts.has_key?(:use_ldd) ? opts[:use_ldd] : Config.default_use_ldd
@@ -180,6 +182,13 @@ module LaunchDarkly
180
182
  #
181
183
  attr_reader :read_timeout
182
184
 
185
+ #
186
+ # The initial delay before reconnecting after an error in the SSE client.
187
+ # This only applies to the streaming connection.
188
+ # @return [Float]
189
+ #
190
+ attr_reader :initial_reconnect_delay
191
+
183
192
  #
184
193
  # The connect timeout for network connections in seconds.
185
194
  # @return [Float]
@@ -395,6 +404,14 @@ module LaunchDarkly
395
404
  10
396
405
  end
397
406
 
407
+ #
408
+ # The default value for {#initial_reconnect_delay}.
409
+ # @return [Float] 1
410
+ #
411
+ def self.default_initial_reconnect_delay
412
+ 1
413
+ end
414
+
398
415
  #
399
416
  # The default value for {#connect_timeout}.
400
417
  # @return [Float] 10
@@ -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
- MAX_FLUSH_WORKERS = 5
30
- USER_ATTRS_TO_STRINGIFY_FOR_EVENTS = [ :key, :secondary, :ip, :country, :email, :firstName, :lastName,
31
- :avatar, :name ]
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
- private_constant :MAX_FLUSH_WORKERS
34
- private_constant :USER_ATTRS_TO_STRINGIFY_FOR_EVENTS
46
+ def record_identify_event(user)
47
+ end
35
48
 
36
- # @private
37
- class NullEventProcessor
38
- def add_event(event)
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 EventMessage
50
- def initialize(event)
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 && test_properties.has_key?(:event_sender) ?
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 add_event(event)
127
- event[:creationDate] = Impl::Util.current_time_millis
128
- post_to_inbox(EventMessage.new(event))
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[:kind] == "feature"
261
- will_add_full_event = event[:trackEvents]
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.clone
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.has_key?(:user) && !notice_user(event[:user]) && event[:kind] != "identify"
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[:debugEventsUntilDate]
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
- 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
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: is_debug ? "debug" : "feature",
434
- creationDate: event[:creationDate],
435
- key: event[:key],
436
- value: event[:value]
490
+ kind: FEATURE_KIND,
491
+ creationDate: event.timestamp,
492
+ key: event.key,
493
+ value: event.value
437
494
  }
438
- out[:default] = event[:default] if event.has_key?(:default)
439
- out[:variation] = event[:variation] if event.has_key?(:variation)
440
- out[:version] = event[:version] if event.has_key?(:version)
441
- out[:prereqOf] = event[:prereqOf] if event.has_key?(:prereqOf)
442
- out[:contextKind] = event[:contextKind] if event.has_key?(:contextKind)
443
- if @inline_users || is_debug
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?
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
- when "identify"
503
+
504
+ when LaunchDarkly::Impl::IdentifyEvent
451
505
  {
452
- kind: "identify",
453
- creationDate: event[:creationDate],
454
- key: event[:user][:key].to_s,
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
- when "custom"
511
+
512
+ when LaunchDarkly::Impl::CustomEvent
458
513
  out = {
459
- kind: "custom",
460
- creationDate: event[:creationDate],
461
- key: event[:key]
514
+ kind: CUSTOM_KIND,
515
+ creationDate: event.timestamp,
516
+ key: event.key
462
517
  }
463
- out[:data] = event[:data] if event.has_key?(:data)
464
- if @inline_users
465
- out[:user] = process_user(event)
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)
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
- when "index"
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: "index",
475
- creationDate: event[:creationDate],
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
- event
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[:counters].each { |ckey, cval|
487
- flag = flags[ckey[:key]]
488
- if flag.nil?
489
- flag = {
490
- default: cval[:default],
491
- counters: []
492
- }
493
- flags[ckey[:key]] = flag
494
- end
495
- c = {
496
- value: cval[:value],
497
- count: cval[:count]
498
- }
499
- if !ckey[:variation].nil?
500
- c[:variation] = ckey[:variation]
501
- end
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
- flag[:counters].push(c)
508
- }
583
+ flags[flagKey] = { default: flagInfo.default, counters: counters }
584
+ end
509
585
  {
510
- kind: "summary",
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
@@ -16,21 +16,32 @@ module LaunchDarkly
16
16
 
17
17
  # Used internally to build the state map.
18
18
  # @private
19
- def add_flag(flag, value, variation, reason = nil, details_only_if_tracked = false)
20
- key = flag[: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
- with_details = !details_only_if_tracked || flag[:trackEvents]
24
- if !with_details && flag[:debugEventsUntilDate]
25
- with_details = flag[:debugEventsUntilDate] > Impl::Util::current_time_millis
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
- if with_details
28
- meta[:version] = flag[:version]
29
- meta[:reason] = reason if !reason.nil?
36
+
37
+ if !omit_details
38
+ meta[:version] = flag_state[:version]
30
39
  end
31
- meta[:variation] = variation if !variation.nil?
32
- meta[:trackEvents] = true if flag[:trackEvents]
33
- meta[:debugEventsUntilDate] = flag[:debugEventsUntilDate] if flag[:debugEventsUntilDate]
40
+
41
+ meta[:variation] = flag_state[:variation] if !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
 
@@ -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
- :events, # an array of evaluation events generated by prerequisites, or nil
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, event_factory)
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, event_factory)
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, event_factory)
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, event_factory)
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, event_factory)
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, event_factory)
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
- event = event_factory.new_eval_event(prereq_flag, user, prereq_res, nil, flag)
139
- state.events = [] if state.events.nil?
140
- state.events.push(event)
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
@@ -89,7 +89,7 @@ module LaunchDarkly
89
89
 
90
90
  private
91
91
 
92
- BUILTINS = Set[:key, :ip, :country, :email, :firstName, :lastName, :avatar, :name, :anonymous]
92
+ BUILTINS = Set[:key, :secondary, :ip, :country, :email, :firstName, :lastName, :avatar, :name, :anonymous]
93
93
  NUMERIC_VERSION_COMPONENTS_REGEX = Regexp.new("^[0-9.]*")
94
94
 
95
95
  private_constant :BUILTINS