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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -2
  3. data/lib/ldclient-rb/config.rb +203 -43
  4. data/lib/ldclient-rb/context.rb +487 -0
  5. data/lib/ldclient-rb/evaluation_detail.rb +85 -26
  6. data/lib/ldclient-rb/events.rb +185 -146
  7. data/lib/ldclient-rb/flags_state.rb +25 -14
  8. data/lib/ldclient-rb/impl/big_segments.rb +117 -0
  9. data/lib/ldclient-rb/impl/context.rb +96 -0
  10. data/lib/ldclient-rb/impl/context_filter.rb +145 -0
  11. data/lib/ldclient-rb/impl/diagnostic_events.rb +9 -10
  12. data/lib/ldclient-rb/impl/evaluator.rb +428 -132
  13. data/lib/ldclient-rb/impl/evaluator_bucketing.rb +40 -41
  14. data/lib/ldclient-rb/impl/evaluator_helpers.rb +50 -0
  15. data/lib/ldclient-rb/impl/evaluator_operators.rb +26 -55
  16. data/lib/ldclient-rb/impl/event_sender.rb +6 -6
  17. data/lib/ldclient-rb/impl/event_summarizer.rb +68 -0
  18. data/lib/ldclient-rb/impl/event_types.rb +78 -0
  19. data/lib/ldclient-rb/impl/integrations/consul_impl.rb +7 -7
  20. data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +92 -28
  21. data/lib/ldclient-rb/impl/integrations/file_data_source.rb +212 -0
  22. data/lib/ldclient-rb/impl/integrations/redis_impl.rb +165 -32
  23. data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +40 -0
  24. data/lib/ldclient-rb/impl/model/clause.rb +39 -0
  25. data/lib/ldclient-rb/impl/model/feature_flag.rb +213 -0
  26. data/lib/ldclient-rb/impl/model/preprocessed_data.rb +64 -0
  27. data/lib/ldclient-rb/impl/model/segment.rb +126 -0
  28. data/lib/ldclient-rb/impl/model/serialization.rb +54 -44
  29. data/lib/ldclient-rb/impl/repeating_task.rb +47 -0
  30. data/lib/ldclient-rb/impl/store_data_set_sorter.rb +2 -2
  31. data/lib/ldclient-rb/impl/unbounded_pool.rb +1 -1
  32. data/lib/ldclient-rb/impl/util.rb +62 -1
  33. data/lib/ldclient-rb/in_memory_store.rb +2 -2
  34. data/lib/ldclient-rb/integrations/consul.rb +9 -2
  35. data/lib/ldclient-rb/integrations/dynamodb.rb +47 -2
  36. data/lib/ldclient-rb/integrations/file_data.rb +108 -0
  37. data/lib/ldclient-rb/integrations/redis.rb +43 -3
  38. data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +594 -0
  39. data/lib/ldclient-rb/integrations/test_data.rb +213 -0
  40. data/lib/ldclient-rb/integrations/util/store_wrapper.rb +14 -9
  41. data/lib/ldclient-rb/integrations.rb +2 -51
  42. data/lib/ldclient-rb/interfaces.rb +151 -1
  43. data/lib/ldclient-rb/ldclient.rb +175 -133
  44. data/lib/ldclient-rb/memoized_value.rb +1 -1
  45. data/lib/ldclient-rb/non_blocking_thread_pool.rb +1 -1
  46. data/lib/ldclient-rb/polling.rb +22 -41
  47. data/lib/ldclient-rb/reference.rb +274 -0
  48. data/lib/ldclient-rb/requestor.rb +7 -7
  49. data/lib/ldclient-rb/stream.rb +9 -9
  50. data/lib/ldclient-rb/util.rb +11 -17
  51. data/lib/ldclient-rb/version.rb +1 -1
  52. data/lib/ldclient-rb.rb +2 -4
  53. metadata +49 -23
  54. data/lib/ldclient-rb/event_summarizer.rb +0 -55
  55. data/lib/ldclient-rb/file_data_source.rb +0 -314
  56. data/lib/ldclient-rb/impl/event_factory.rb +0 -126
  57. data/lib/ldclient-rb/newrelic.rb +0 -17
  58. data/lib/ldclient-rb/redis_store.rb +0 -88
  59. data/lib/ldclient-rb/user_filter.rb +0 -52
@@ -1,4 +1,3 @@
1
-
2
1
  module LaunchDarkly
3
2
  module Impl
4
3
  # Encapsulates the logic for percentage rollouts.
@@ -6,64 +5,64 @@ module LaunchDarkly
6
5
  # Applies either a fixed variation or a rollout for a rule (or the fallthrough rule).
7
6
  #
8
7
  # @param flag [Object] the feature flag
9
- # @param rule [Object] the rule
10
- # @param user [Object] the user properties
11
- # @return [Number] the variation index, or nil if there is an error
12
- def self.variation_index_for_user(flag, rule, user)
13
-
14
- variation = rule[:variation]
15
- return variation, false if !variation.nil? # fixed variation
16
- rollout = rule[:rollout]
8
+ # @param vr [LaunchDarkly::Impl::Model::VariationOrRollout] the variation/rollout properties
9
+ # @param context [LaunchDarkly::LDContext] the context properties
10
+ # @return [Array<[Number, nil], Boolean>] the variation index, or nil if there is an error
11
+ # @raise [InvalidReferenceException]
12
+ def self.variation_index_for_context(flag, vr, context)
13
+ variation = vr.variation
14
+ return variation, false unless variation.nil? # fixed variation
15
+ rollout = vr.rollout
17
16
  return nil, false if rollout.nil?
18
- variations = rollout[:variations]
17
+ variations = rollout.variations
19
18
  if !variations.nil? && variations.length > 0 # percentage rollout
20
- bucket_by = rollout[:bucketBy].nil? ? "key" : rollout[:bucketBy]
19
+ rollout_is_experiment = rollout.is_experiment
20
+ bucket_by = rollout_is_experiment ? nil : rollout.bucket_by
21
+ bucket_by = 'key' if bucket_by.nil?
21
22
 
22
- seed = rollout[:seed]
23
- bucket = bucket_user(user, flag[:key], bucket_by, flag[:salt], seed) # may not be present
24
- sum = 0;
23
+ seed = rollout.seed
24
+ bucket = bucket_context(context, rollout.context_kind, flag.key, bucket_by, flag.salt, seed) # may not be present
25
+ in_experiment = rollout_is_experiment && !bucket.nil?
26
+ sum = 0
25
27
  variations.each do |variate|
26
- if rollout[:kind] == "experiment" && !variate[:untracked]
27
- in_experiment = true
28
- end
29
-
30
- sum += variate[:weight].to_f / 100000.0
31
- if bucket < sum
32
- return variate[:variation], !!in_experiment
28
+ sum += variate.weight.to_f / 100000.0
29
+ if bucket.nil? || bucket < sum
30
+ return variate.variation, in_experiment && !variate.untracked
33
31
  end
34
32
  end
35
- # The user's bucket value was greater than or equal to the end of the last bucket. This could happen due
33
+ # The context's bucket value was greater than or equal to the end of the last bucket. This could happen due
36
34
  # to a rounding error, or due to the fact that we are scaling to 100000 rather than 99999, or the flag
37
35
  # data could contain buckets that don't actually add up to 100000. Rather than returning an error in
38
- # this case (or changing the scaling, which would potentially change the results for *all* users), we
39
- # will simply put the user in the last bucket.
36
+ # this case (or changing the scaling, which would potentially change the results for *all* contexts), we
37
+ # will simply put the context in the last bucket.
40
38
  last_variation = variations[-1]
41
- in_experiment = rollout[:kind] == "experiment" && !last_variation[:untracked]
42
-
43
- [last_variation[:variation], in_experiment]
39
+ [last_variation.variation, in_experiment && !last_variation.untracked]
44
40
  else # the rule isn't well-formed
45
41
  [nil, false]
46
42
  end
47
43
  end
48
44
 
49
- # Returns a user's bucket value as a floating-point value in `[0, 1)`.
45
+ # Returns a context's bucket value as a floating-point value in `[0, 1)`.
50
46
  #
51
- # @param user [Object] the user properties
47
+ # @param context [LDContext] the context properties
48
+ # @param context_kind [String, nil] the context kind to match against
52
49
  # @param key [String] the feature flag key (or segment key, if this is for a segment rule)
53
- # @param bucket_by [String|Symbol] the name of the user attribute to be used for bucketing
50
+ # @param bucket_by [String|Symbol] the name of the context attribute to be used for bucketing
54
51
  # @param salt [String] the feature flag's or segment's salt value
55
- # @return [Number] the bucket value, from 0 inclusive to 1 exclusive
56
- def self.bucket_user(user, key, bucket_by, salt, seed)
57
- return nil unless user[:key]
52
+ # @return [Float, nil] the bucket value, from 0 inclusive to 1 exclusive
53
+ # @raise [InvalidReferenceException] Raised if the clause.attribute is an invalid reference
54
+ def self.bucket_context(context, context_kind, key, bucket_by, salt, seed)
55
+ matched_context = context.individual_context(context_kind || LaunchDarkly::LDContext::KIND_DEFAULT)
56
+ return nil if matched_context.nil?
58
57
 
59
- id_hash = bucketable_string_value(EvaluatorOperators.user_value(user, bucket_by))
60
- if id_hash.nil?
61
- return 0.0
62
- end
58
+ reference = (context_kind.nil? || context_kind.empty?) ? Reference.create_literal(bucket_by) : Reference.create(bucket_by)
59
+ raise InvalidReferenceException.new(reference.error) unless reference.error.nil?
63
60
 
64
- if user[:secondary]
65
- id_hash += "." + user[:secondary].to_s
66
- end
61
+ context_value = matched_context.get_value_for_reference(reference)
62
+ return 0.0 if context_value.nil?
63
+
64
+ id_hash = bucketable_string_value(context_value)
65
+ return 0.0 if id_hash.nil?
67
66
 
68
67
  if seed
69
68
  hash_key = "%d.%s" % [seed, id_hash]
@@ -71,7 +70,7 @@ module LaunchDarkly
71
70
  hash_key = "%s.%s.%s" % [key, salt, id_hash]
72
71
  end
73
72
 
74
- hash_val = (Digest::SHA1.hexdigest(hash_key))[0..14]
73
+ hash_val = Digest::SHA1.hexdigest(hash_key)[0..14]
75
74
  hash_val.to_i(16) / Float(0xFFFFFFFFFFFFFFF)
76
75
  end
77
76
 
@@ -0,0 +1,50 @@
1
+ require "ldclient-rb/evaluation_detail"
2
+
3
+ # This file contains any pieces of low-level evaluation logic that don't need to be inside the Evaluator
4
+ # class, because they don't depend on any SDK state outside of their input parameters.
5
+
6
+ module LaunchDarkly
7
+ module Impl
8
+ module EvaluatorHelpers
9
+ #
10
+ # @param flag [LaunchDarkly::Impl::Model::FeatureFlag]
11
+ # @param reason [LaunchDarkly::EvaluationReason]
12
+ #
13
+ def self.evaluation_detail_for_off_variation(flag, reason, logger = nil)
14
+ index = flag.off_variation
15
+ index.nil? ? EvaluationDetail.new(nil, nil, reason) : evaluation_detail_for_variation(flag, index, reason, logger)
16
+ end
17
+
18
+ #
19
+ # @param flag [LaunchDarkly::Impl::Model::FeatureFlag]
20
+ # @param index [Integer]
21
+ # @param reason [LaunchDarkly::EvaluationReason]
22
+ #
23
+ def self.evaluation_detail_for_variation(flag, index, reason, logger = nil)
24
+ vars = flag.variations
25
+ if index < 0 || index >= vars.length
26
+ logger.error("[LDClient] Data inconsistency in feature flag \"#{flag.key}\": invalid variation index") unless logger.nil?
27
+ EvaluationDetail.new(nil, nil, EvaluationReason::error(EvaluationReason::ERROR_MALFORMED_FLAG))
28
+ else
29
+ EvaluationDetail.new(vars[index], index, reason)
30
+ end
31
+ end
32
+
33
+ #
34
+ # @param context [LaunchDarkly::LDContext]
35
+ # @param kind [String, nil]
36
+ # @param keys [Enumerable<String>]
37
+ # @return [Boolean]
38
+ #
39
+ def self.context_key_in_target_list(context, kind, keys)
40
+ return false unless keys.is_a? Enumerable
41
+ return false if keys.empty?
42
+
43
+ matched_context = context.individual_context(kind || LaunchDarkly::LDContext::KIND_DEFAULT)
44
+ return false if matched_context.nil?
45
+
46
+ keys.include? matched_context.key
47
+ end
48
+ end
49
+ end
50
+ end
@@ -9,24 +9,24 @@ module LaunchDarkly
9
9
  # Applies an operator to produce a boolean result.
10
10
  #
11
11
  # @param op [Symbol] one of the supported LaunchDarkly operators, as a symbol
12
- # @param user_value the value of the user attribute that is referenced in the current clause (left-hand
12
+ # @param context_value the value of the context attribute that is referenced in the current clause (left-hand
13
13
  # side of the expression)
14
- # @param clause_value the constant value that `user_value` is being compared to (right-hand side of the
14
+ # @param clause_value the constant value that `context_value` is being compared to (right-hand side of the
15
15
  # expression)
16
16
  # @return [Boolean] true if the expression should be considered a match; false if it is not a match, or
17
17
  # if the values cannot be compared because they are of the wrong types, or if the operator is unknown
18
- def self.apply(op, user_value, clause_value)
18
+ def self.apply(op, context_value, clause_value)
19
19
  case op
20
20
  when :in
21
- user_value == clause_value
21
+ context_value == clause_value
22
22
  when :startsWith
23
- string_op(user_value, clause_value, lambda { |a, b| a.start_with? b })
23
+ string_op(context_value, clause_value, lambda { |a, b| a.start_with? b })
24
24
  when :endsWith
25
- string_op(user_value, clause_value, lambda { |a, b| a.end_with? b })
25
+ string_op(context_value, clause_value, lambda { |a, b| a.end_with? b })
26
26
  when :contains
27
- string_op(user_value, clause_value, lambda { |a, b| a.include? b })
27
+ string_op(context_value, clause_value, lambda { |a, b| a.include? b })
28
28
  when :matches
29
- string_op(user_value, clause_value, lambda { |a, b|
29
+ string_op(context_value, clause_value, lambda { |a, b|
30
30
  begin
31
31
  re = Regexp.new b
32
32
  !re.match(a).nil?
@@ -35,76 +35,47 @@ module LaunchDarkly
35
35
  end
36
36
  })
37
37
  when :lessThan
38
- numeric_op(user_value, clause_value, lambda { |a, b| a < b })
38
+ numeric_op(context_value, clause_value, lambda { |a, b| a < b })
39
39
  when :lessThanOrEqual
40
- numeric_op(user_value, clause_value, lambda { |a, b| a <= b })
40
+ numeric_op(context_value, clause_value, lambda { |a, b| a <= b })
41
41
  when :greaterThan
42
- numeric_op(user_value, clause_value, lambda { |a, b| a > b })
42
+ numeric_op(context_value, clause_value, lambda { |a, b| a > b })
43
43
  when :greaterThanOrEqual
44
- numeric_op(user_value, clause_value, lambda { |a, b| a >= b })
44
+ numeric_op(context_value, clause_value, lambda { |a, b| a >= b })
45
45
  when :before
46
- date_op(user_value, clause_value, lambda { |a, b| a < b })
46
+ date_op(context_value, clause_value, lambda { |a, b| a < b })
47
47
  when :after
48
- date_op(user_value, clause_value, lambda { |a, b| a > b })
48
+ date_op(context_value, clause_value, lambda { |a, b| a > b })
49
49
  when :semVerEqual
50
- semver_op(user_value, clause_value, lambda { |a, b| a == b })
50
+ semver_op(context_value, clause_value, lambda { |a, b| a == b })
51
51
  when :semVerLessThan
52
- semver_op(user_value, clause_value, lambda { |a, b| a < b })
52
+ semver_op(context_value, clause_value, lambda { |a, b| a < b })
53
53
  when :semVerGreaterThan
54
- semver_op(user_value, clause_value, lambda { |a, b| a > b })
54
+ semver_op(context_value, clause_value, lambda { |a, b| a > b })
55
55
  when :segmentMatch
56
56
  # We should never reach this; it can't be evaluated based on just two parameters, because it requires
57
- # looking up the segment from the data store. Instead, we special-case this operator in clause_match_user.
57
+ # looking up the segment from the data store. Instead, we special-case this operator in clause_match_context.
58
58
  false
59
59
  else
60
60
  false
61
61
  end
62
62
  end
63
63
 
64
- # Retrieves the value of a user attribute by name.
65
- #
66
- # Built-in attributes correspond to top-level properties in the user object. They are treated as strings and
67
- # non-string values are coerced to strings, except for `anonymous` which is meant to be a boolean if present
68
- # and is not currently coerced. This behavior is consistent with earlier versions of the Ruby SDK, but is not
69
- # guaranteed to be consistent with other SDKs, since the evaluator specification is based on the strongly-typed
70
- # SDKs where it is not possible for an attribute to have the wrong type.
71
- #
72
- # Custom attributes correspond to properties within the `custom` property, if any, and can be of any type.
73
- #
74
- # @param user [Object] the user properties
75
- # @param attribute [String|Symbol] the attribute to get, for instance `:key` or `:name` or `:some_custom_attr`
76
- # @return the attribute value, or nil if the attribute is unknown
77
- def self.user_value(user, attribute)
78
- attribute = attribute.to_sym
79
- if BUILTINS.include? attribute
80
- value = user[attribute]
81
- return nil if value.nil?
82
- (attribute == :anonymous) ? value : value.to_s
83
- elsif !user[:custom].nil?
84
- user[:custom][attribute]
85
- else
86
- nil
87
- end
88
- end
89
-
90
64
  private
91
65
 
92
- BUILTINS = Set[:key, :ip, :country, :email, :firstName, :lastName, :avatar, :name, :anonymous]
93
66
  NUMERIC_VERSION_COMPONENTS_REGEX = Regexp.new("^[0-9.]*")
94
-
95
- private_constant :BUILTINS
96
67
  private_constant :NUMERIC_VERSION_COMPONENTS_REGEX
97
68
 
98
- def self.string_op(user_value, clause_value, fn)
99
- (user_value.is_a? String) && (clause_value.is_a? String) && fn.call(user_value, clause_value)
69
+ def self.string_op(context_value, clause_value, fn)
70
+ (context_value.is_a? String) && (clause_value.is_a? String) && fn.call(context_value, clause_value)
100
71
  end
101
72
 
102
- def self.numeric_op(user_value, clause_value, fn)
103
- (user_value.is_a? Numeric) && (clause_value.is_a? Numeric) && fn.call(user_value, clause_value)
73
+ def self.numeric_op(context_value, clause_value, fn)
74
+ (context_value.is_a? Numeric) && (clause_value.is_a? Numeric) && fn.call(context_value, clause_value)
104
75
  end
105
76
 
106
- def self.date_op(user_value, clause_value, fn)
107
- ud = to_date(user_value)
77
+ def self.date_op(context_value, clause_value, fn)
78
+ ud = to_date(context_value)
108
79
  if !ud.nil?
109
80
  cd = to_date(clause_value)
110
81
  !cd.nil? && fn.call(ud, cd)
@@ -113,8 +84,8 @@ module LaunchDarkly
113
84
  end
114
85
  end
115
86
 
116
- def self.semver_op(user_value, clause_value, fn)
117
- uv = to_semver(user_value)
87
+ def self.semver_op(context_value, clause_value, fn)
88
+ uv = to_semver(context_value)
118
89
  if !uv.nil?
119
90
  cv = to_semver(clause_value)
120
91
  !cv.nil? && fn.call(uv, cv)
@@ -8,7 +8,7 @@ module LaunchDarkly
8
8
  EventSenderResult = Struct.new(:success, :must_shutdown, :time_from_server)
9
9
 
10
10
  class EventSender
11
- CURRENT_SCHEMA_VERSION = 3
11
+ CURRENT_SCHEMA_VERSION = 4
12
12
  DEFAULT_RETRY_INTERVAL = 1
13
13
 
14
14
  def initialize(sdk_key, config, http_client = nil, retry_interval = DEFAULT_RETRY_INTERVAL)
@@ -33,7 +33,7 @@ module LaunchDarkly
33
33
  begin
34
34
  http_client = @http_client_pool.acquire()
35
35
  response = nil
36
- (0..1).each do |attempt|
36
+ 2.times do |attempt|
37
37
  if attempt > 0
38
38
  @logger.warn { "[LDClient] Will retry posting events after #{@retry_interval} second" }
39
39
  sleep(@retry_interval)
@@ -43,13 +43,13 @@ module LaunchDarkly
43
43
  headers = {}
44
44
  headers["content-type"] = "application/json"
45
45
  Impl::Util.default_http_headers(@sdk_key, @config).each { |k, v| headers[k] = v }
46
- if !is_diagnostic
46
+ unless is_diagnostic
47
47
  headers["X-LaunchDarkly-Event-Schema"] = CURRENT_SCHEMA_VERSION.to_s
48
48
  headers["X-LaunchDarkly-Payload-ID"] = payload_id
49
49
  end
50
50
  response = http_client.request("POST", uri, {
51
51
  headers: headers,
52
- body: event_data
52
+ body: event_data,
53
53
  })
54
54
  rescue StandardError => exn
55
55
  @logger.warn { "[LDClient] Error sending events: #{exn.inspect}." }
@@ -60,7 +60,7 @@ module LaunchDarkly
60
60
  body = response.to_s
61
61
  if status >= 200 && status < 300
62
62
  res_time = nil
63
- if !response.headers["date"].nil?
63
+ unless response.headers["date"].nil?
64
64
  begin
65
65
  res_time = Time.httpdate(response.headers["date"])
66
66
  rescue ArgumentError
@@ -77,7 +77,7 @@ module LaunchDarkly
77
77
  end
78
78
  end
79
79
  # used up our retries
80
- return EventSenderResult.new(false, false, nil)
80
+ EventSenderResult.new(false, false, nil)
81
81
  ensure
82
82
  @http_client_pool.release(http_client)
83
83
  end
@@ -0,0 +1,68 @@
1
+ require "ldclient-rb/impl/event_types"
2
+ require "set"
3
+
4
+ module LaunchDarkly
5
+ module Impl
6
+ EventSummary = Struct.new(:start_date, :end_date, :counters)
7
+
8
+ EventSummaryFlagInfo = Struct.new(:default, :versions, :context_kinds)
9
+
10
+ EventSummaryFlagVariationCounter = Struct.new(:value, :count)
11
+
12
+ # Manages the state of summarizable information for the EventProcessor, including the
13
+ # event counters and context deduplication. Note that the methods of this class are
14
+ # deliberately not thread-safe; the EventProcessor is responsible for enforcing
15
+ # synchronization across both the summarizer and the event queue.
16
+ class EventSummarizer
17
+ class Counter
18
+ end
19
+
20
+ def initialize
21
+ clear
22
+ end
23
+
24
+ # Adds this event to our counters, if it is a type of event we need to count.
25
+ def summarize_event(event)
26
+ return unless event.is_a?(LaunchDarkly::Impl::EvalEvent)
27
+
28
+ counters_for_flag = @counters[event.key]
29
+ if counters_for_flag.nil?
30
+ counters_for_flag = EventSummaryFlagInfo.new(event.default, Hash.new, Set.new)
31
+ @counters[event.key] = counters_for_flag
32
+ end
33
+
34
+ counters_for_flag_version = counters_for_flag.versions[event.version]
35
+ if counters_for_flag_version.nil?
36
+ counters_for_flag_version = Hash.new
37
+ counters_for_flag.versions[event.version] = counters_for_flag_version
38
+ end
39
+
40
+ counters_for_flag.context_kinds.merge(event.context.kinds)
41
+
42
+ variation_counter = counters_for_flag_version[event.variation]
43
+ if variation_counter.nil?
44
+ counters_for_flag_version[event.variation] = EventSummaryFlagVariationCounter.new(event.value, 1)
45
+ else
46
+ variation_counter.count = variation_counter.count + 1
47
+ end
48
+
49
+ time = event.timestamp
50
+ unless time.nil?
51
+ @start_date = time if @start_date == 0 || time < @start_date
52
+ @end_date = time if time > @end_date
53
+ end
54
+ end
55
+
56
+ # Returns a snapshot of the current summarized event data, and resets this state.
57
+ def snapshot
58
+ EventSummary.new(@start_date, @end_date, @counters)
59
+ end
60
+
61
+ def clear
62
+ @start_date = 0
63
+ @end_date = 0
64
+ @counters = {}
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,78 @@
1
+ module LaunchDarkly
2
+ module Impl
3
+ class Event
4
+ # @param timestamp [Integer]
5
+ # @param context [LaunchDarkly::LDContext]
6
+ def initialize(timestamp, context)
7
+ @timestamp = timestamp
8
+ @context = context
9
+ end
10
+
11
+ # @return [Integer]
12
+ attr_reader :timestamp
13
+ # @return [LaunchDarkly::LDContext]
14
+ attr_reader :context
15
+ end
16
+
17
+ class EvalEvent < Event
18
+ def initialize(timestamp, context, key, version = nil, variation = nil, value = nil, reason = nil, default = nil,
19
+ track_events = false, debug_until = nil, prereq_of = nil)
20
+ super(timestamp, context)
21
+ @key = key
22
+ @version = version
23
+ @variation = variation
24
+ @value = value
25
+ @reason = reason
26
+ @default = default
27
+ # avoid setting rarely-used attributes if they have no value - this saves a little space per instance
28
+ @track_events = track_events if track_events
29
+ @debug_until = debug_until if debug_until
30
+ @prereq_of = prereq_of if prereq_of
31
+ end
32
+
33
+ attr_reader :key
34
+ attr_reader :version
35
+ attr_reader :variation
36
+ attr_reader :value
37
+ attr_reader :reason
38
+ attr_reader :default
39
+ attr_reader :track_events
40
+ attr_reader :debug_until
41
+ attr_reader :prereq_of
42
+ end
43
+
44
+ class IdentifyEvent < Event
45
+ def initialize(timestamp, context)
46
+ super(timestamp, context)
47
+ end
48
+ end
49
+
50
+ class CustomEvent < Event
51
+ def initialize(timestamp, context, key, data = nil, metric_value = nil)
52
+ super(timestamp, context)
53
+ @key = key
54
+ @data = data unless data.nil?
55
+ @metric_value = metric_value unless metric_value.nil?
56
+ end
57
+
58
+ attr_reader :key
59
+ attr_reader :data
60
+ attr_reader :metric_value
61
+ end
62
+
63
+ class IndexEvent < Event
64
+ def initialize(timestamp, context)
65
+ super(timestamp, context)
66
+ end
67
+ end
68
+
69
+ class DebugEvent < Event
70
+ def initialize(eval_event)
71
+ super(eval_event.timestamp, eval_event.context)
72
+ @eval_event = eval_event
73
+ end
74
+
75
+ attr_reader :eval_event
76
+ end
77
+ end
78
+ end
@@ -16,14 +16,14 @@ module LaunchDarkly
16
16
  end
17
17
 
18
18
  def initialize(opts)
19
- if !CONSUL_ENABLED
19
+ unless CONSUL_ENABLED
20
20
  raise RuntimeError.new("can't use Consul feature store without the 'diplomat' gem")
21
21
  end
22
22
 
23
23
  @prefix = (opts[:prefix] || LaunchDarkly::Integrations::Consul.default_prefix) + '/'
24
24
  @logger = opts[:logger] || Config.default_logger
25
- Diplomat.configuration = opts[:consul_config] if !opts[:consul_config].nil?
26
- Diplomat.configuration.url = opts[:url] if !opts[:url].nil?
25
+ Diplomat.configuration = opts[:consul_config] unless opts[:consul_config].nil?
26
+ Diplomat.configuration.url = opts[:url] unless opts[:url].nil?
27
27
  @logger.info("ConsulFeatureStore: using Consul host at #{Diplomat.configuration.url}")
28
28
  end
29
29
 
@@ -51,10 +51,10 @@ module LaunchDarkly
51
51
  unused_old_keys.each do |key|
52
52
  ops.push({ 'KV' => { 'Verb' => 'delete', 'Key' => key } })
53
53
  end
54
-
54
+
55
55
  # Now set the special key that we check in initialized_internal?
56
56
  ops.push({ 'KV' => { 'Verb' => 'set', 'Key' => inited_key, 'Value' => '' } })
57
-
57
+
58
58
  ConsulUtil.batch_operations(ops)
59
59
 
60
60
  @logger.info { "Initialized database with #{num_items} items" }
@@ -70,7 +70,7 @@ module LaunchDarkly
70
70
  results = Diplomat::Kv.get(kind_key(kind), { recurse: true }, :return)
71
71
  (results == "" ? [] : results).each do |result|
72
72
  value = result[:value]
73
- if !value.nil?
73
+ unless value.nil?
74
74
  item = Model.deserialize(kind, value)
75
75
  items_out[item[:key].to_sym] = item
76
76
  end
@@ -132,7 +132,7 @@ module LaunchDarkly
132
132
  def kind_key(kind)
133
133
  @prefix + kind[:namespace] + '/'
134
134
  end
135
-
135
+
136
136
  def inited_key
137
137
  @prefix + '$inited'
138
138
  end