launchdarkly-server-sdk 6.2.5 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
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