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
@@ -12,7 +12,7 @@ module LaunchDarkly
12
12
  # @raise [ArgumentError] if `variation_index` or `reason` is not of the correct type
13
13
  def initialize(value, variation_index, reason)
14
14
  raise ArgumentError.new("variation_index must be a number") if !variation_index.nil? && !(variation_index.is_a? Numeric)
15
- raise ArgumentError.new("reason must be an EvaluationReason") if !(reason.is_a? EvaluationReason)
15
+ raise ArgumentError.new("reason must be an EvaluationReason") unless reason.is_a? EvaluationReason
16
16
  @value = value
17
17
  @variation_index = variation_index
18
18
  @reason = reason
@@ -70,20 +70,20 @@ module LaunchDarkly
70
70
  class EvaluationReason
71
71
  # Value for {#kind} indicating that the flag was off and therefore returned its configured off value.
72
72
  OFF = :OFF
73
-
74
- # Value for {#kind} indicating that the flag was on but the user did not match any targets or rules.
73
+
74
+ # Value for {#kind} indicating that the flag was on but the context did not match any targets or rules.
75
75
  FALLTHROUGH = :FALLTHROUGH
76
-
77
- # Value for {#kind} indicating that the user key was specifically targeted for this flag.
76
+
77
+ # Value for {#kind} indicating that the context key was specifically targeted for this flag.
78
78
  TARGET_MATCH = :TARGET_MATCH
79
-
80
- # Value for {#kind} indicating that the user matched one of the flag's rules.
79
+
80
+ # Value for {#kind} indicating that the context matched one of the flag's rules.
81
81
  RULE_MATCH = :RULE_MATCH
82
-
82
+
83
83
  # Value for {#kind} indicating that the flag was considered off because it had at least one
84
84
  # prerequisite flag that either was off or did not return the desired variation.
85
85
  PREREQUISITE_FAILED = :PREREQUISITE_FAILED
86
-
86
+
87
87
  # Value for {#kind} indicating that the flag could not be evaluated, e.g. because it does not exist
88
88
  # or due to an unexpected error. In this case the result value will be the application default value
89
89
  # that the caller passed to the client. Check {#error_kind} for more details on the problem.
@@ -100,8 +100,8 @@ module LaunchDarkly
100
100
  # a rule specified a nonexistent variation. An error message will always be logged in this case.
101
101
  ERROR_MALFORMED_FLAG = :MALFORMED_FLAG
102
102
 
103
- # Value for {#error_kind} indicating that the caller passed `nil` for the user parameter, or the
104
- # user lacked a key.
103
+ # Value for {#error_kind} indicating that the caller passed `nil` for the context parameter, or the
104
+ # context was invalid.
105
105
  ERROR_USER_NOT_SPECIFIED = :USER_NOT_SPECIFIED
106
106
 
107
107
  # Value for {#error_kind} indicating that an unexpected exception stopped flag evaluation. An error
@@ -110,27 +110,42 @@ module LaunchDarkly
110
110
 
111
111
  # Indicates the general category of the reason. Will always be one of the class constants such
112
112
  # as {#OFF}.
113
+ # @return [Symbol]
113
114
  attr_reader :kind
114
115
 
115
116
  # The index of the rule that was matched (0 for the first rule in the feature flag). If
116
117
  # {#kind} is not {#RULE_MATCH}, this will be `nil`.
118
+ # @return [Integer|nil]
117
119
  attr_reader :rule_index
118
120
 
119
121
  # A unique string identifier for the matched rule, which will not change if other rules are added
120
122
  # or deleted. If {#kind} is not {#RULE_MATCH}, this will be `nil`.
123
+ # @return [String]
121
124
  attr_reader :rule_id
122
125
 
123
126
  # A boolean or nil value representing if the rule or fallthrough has an experiment rollout.
127
+ # @return [Boolean|nil]
124
128
  attr_reader :in_experiment
125
129
 
126
130
  # The key of the prerequisite flag that did not return the desired variation. If {#kind} is not
127
131
  # {#PREREQUISITE_FAILED}, this will be `nil`.
132
+ # @return [String]
128
133
  attr_reader :prerequisite_key
129
134
 
130
135
  # A value indicating the general category of error. This should be one of the class constants such
131
136
  # as {#ERROR_FLAG_NOT_FOUND}. If {#kind} is not {#ERROR}, it will be `nil`.
137
+ # @return [Symbol]
132
138
  attr_reader :error_kind
133
139
 
140
+ # Describes the validity of Big Segment information, if and only if the flag evaluation required
141
+ # querying at least one Big Segment. Otherwise it returns `nil`. Possible values are defined by
142
+ # {BigSegmentsStatus}.
143
+ #
144
+ # Big Segments are a specific kind of context segments. For more information, read the LaunchDarkly
145
+ # documentation: https://docs.launchdarkly.com/home/users/big-segments
146
+ # @return [Symbol]
147
+ attr_reader :big_segments_status
148
+
134
149
  # Returns an instance whose {#kind} is {#OFF}.
135
150
  # @return [EvaluationReason]
136
151
  def self.off
@@ -161,9 +176,9 @@ module LaunchDarkly
161
176
  # @return [EvaluationReason]
162
177
  # @raise [ArgumentError] if `rule_index` is not a number or `rule_id` is not a string
163
178
  def self.rule_match(rule_index, rule_id, in_experiment=false)
164
- raise ArgumentError.new("rule_index must be a number") if !(rule_index.is_a? Numeric)
179
+ raise ArgumentError.new("rule_index must be a number") unless rule_index.is_a? Numeric
165
180
  raise ArgumentError.new("rule_id must be a string") if !rule_id.nil? && !(rule_id.is_a? String) # in test data, ID could be nil
166
-
181
+
167
182
  if in_experiment
168
183
  er = new(:RULE_MATCH, rule_index, rule_id, nil, nil, true)
169
184
  else
@@ -178,7 +193,7 @@ module LaunchDarkly
178
193
  # @return [EvaluationReason]
179
194
  # @raise [ArgumentError] if `prerequisite_key` is nil or not a string
180
195
  def self.prerequisite_failed(prerequisite_key)
181
- raise ArgumentError.new("prerequisite_key must be a string") if !(prerequisite_key.is_a? String)
196
+ raise ArgumentError.new("prerequisite_key must be a string") unless prerequisite_key.is_a? String
182
197
  new(:PREREQUISITE_FAILED, nil, nil, prerequisite_key, nil)
183
198
  end
184
199
 
@@ -188,7 +203,7 @@ module LaunchDarkly
188
203
  # @return [EvaluationReason]
189
204
  # @raise [ArgumentError] if `error_kind` is not a symbol
190
205
  def self.error(error_kind)
191
- raise ArgumentError.new("error_kind must be a symbol") if !(error_kind.is_a? Symbol)
206
+ raise ArgumentError.new("error_kind must be a symbol") unless error_kind.is_a? Symbol
192
207
  e = @@error_instances[error_kind]
193
208
  e.nil? ? make_error(error_kind) : e
194
209
  end
@@ -196,11 +211,13 @@ module LaunchDarkly
196
211
  def ==(other)
197
212
  if other.is_a? EvaluationReason
198
213
  @kind == other.kind && @rule_index == other.rule_index && @rule_id == other.rule_id &&
199
- @prerequisite_key == other.prerequisite_key && @error_kind == other.error_kind
214
+ @prerequisite_key == other.prerequisite_key && @error_kind == other.error_kind &&
215
+ @big_segments_status == other.big_segments_status
200
216
  elsif other.is_a? Hash
201
217
  @kind.to_s == other[:kind] && @rule_index == other[:ruleIndex] && @rule_id == other[:ruleId] &&
202
218
  @prerequisite_key == other[:prerequisiteKey] &&
203
- (other[:errorKind] == @error_kind.nil? ? nil : @error_kind.to_s)
219
+ (other[:errorKind] == @error_kind.nil? ? nil : @error_kind.to_s) &&
220
+ (other[:bigSegmentsStatus] == @big_segments_status.nil? ? nil : @big_segments_status.to_s)
204
221
  end
205
222
  end
206
223
 
@@ -242,7 +259,7 @@ module LaunchDarkly
242
259
  # enabled for a flag and the application called variation_detail, or 2. experimentation is
243
260
  # enabled for an evaluation. We can't reuse these hashes because an application could call
244
261
  # as_json and then modify the result.
245
- case @kind
262
+ ret = case @kind
246
263
  when :RULE_MATCH
247
264
  if @in_experiment
248
265
  { kind: @kind, ruleIndex: @rule_index, ruleId: @rule_id, inExperiment: @in_experiment }
@@ -262,6 +279,10 @@ module LaunchDarkly
262
279
  else
263
280
  { kind: @kind }
264
281
  end
282
+ unless @big_segments_status.nil?
283
+ ret[:bigSegmentsStatus] = @big_segments_status
284
+ end
285
+ ret
265
286
  end
266
287
 
267
288
  # Same as {#as_json}, but converts the JSON structure into a string.
@@ -285,27 +306,36 @@ module LaunchDarkly
285
306
  @prerequisite_key
286
307
  when :errorKind
287
308
  @error_kind.nil? ? nil : @error_kind.to_s
309
+ when :bigSegmentsStatus
310
+ @big_segments_status.nil? ? nil : @big_segments_status.to_s
288
311
  else
289
312
  nil
290
313
  end
291
314
  end
292
315
 
293
- private
316
+ def with_big_segments_status(big_segments_status)
317
+ return self if @big_segments_status == big_segments_status
318
+ EvaluationReason.new(@kind, @rule_index, @rule_id, @prerequisite_key, @error_kind, @in_experiment, big_segments_status)
319
+ end
294
320
 
295
- def initialize(kind, rule_index, rule_id, prerequisite_key, error_kind, in_experiment=nil)
321
+ #
322
+ # Constructor that sets all properties. Applications should not normally use this constructor,
323
+ # but should use class methods like {#off} to avoid creating unnecessary instances.
324
+ #
325
+ def initialize(kind, rule_index, rule_id, prerequisite_key, error_kind, in_experiment=nil,
326
+ big_segments_status = nil)
296
327
  @kind = kind.to_sym
297
328
  @rule_index = rule_index
298
329
  @rule_id = rule_id
299
- @rule_id.freeze if !rule_id.nil?
330
+ @rule_id.freeze unless rule_id.nil?
300
331
  @prerequisite_key = prerequisite_key
301
- @prerequisite_key.freeze if !prerequisite_key.nil?
332
+ @prerequisite_key.freeze unless prerequisite_key.nil?
302
333
  @error_kind = error_kind
303
334
  @in_experiment = in_experiment
335
+ @big_segments_status = big_segments_status
304
336
  end
305
337
 
306
- private_class_method :new
307
-
308
- def self.make_error(error_kind)
338
+ private_class_method def self.make_error(error_kind)
309
339
  new(:ERROR, nil, nil, nil, error_kind)
310
340
  end
311
341
 
@@ -318,7 +348,36 @@ module LaunchDarkly
318
348
  ERROR_FLAG_NOT_FOUND => make_error(ERROR_FLAG_NOT_FOUND),
319
349
  ERROR_MALFORMED_FLAG => make_error(ERROR_MALFORMED_FLAG),
320
350
  ERROR_USER_NOT_SPECIFIED => make_error(ERROR_USER_NOT_SPECIFIED),
321
- ERROR_EXCEPTION => make_error(ERROR_EXCEPTION)
351
+ ERROR_EXCEPTION => make_error(ERROR_EXCEPTION),
322
352
  }
323
353
  end
354
+
355
+ #
356
+ # Defines the possible values of {EvaluationReason#big_segments_status}.
357
+ #
358
+ module BigSegmentsStatus
359
+ #
360
+ # Indicates that the Big Segment query involved in the flag evaluation was successful, and
361
+ # that the segment state is considered up to date.
362
+ #
363
+ HEALTHY = :HEALTHY
364
+
365
+ #
366
+ # Indicates that the Big Segment query involved in the flag evaluation was successful, but
367
+ # that the segment state may not be up to date.
368
+ #
369
+ STALE = :STALE
370
+
371
+ #
372
+ # Indicates that Big Segments could not be queried for the flag evaluation because the SDK
373
+ # configuration did not include a Big Segment store.
374
+ #
375
+ NOT_CONFIGURED = :NOT_CONFIGURED
376
+
377
+ #
378
+ # Indicates that the Big Segment query involved in the flag evaluation failed, for instance
379
+ # due to a database error.
380
+ #
381
+ STORE_ERROR = :STORE_ERROR
382
+ end
324
383
  end