launchdarkly-server-sdk 6.3.4 → 7.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ldclient-rb/config.rb +121 -55
- data/lib/ldclient-rb/context.rb +487 -0
- data/lib/ldclient-rb/evaluation_detail.rb +20 -20
- data/lib/ldclient-rb/events.rb +77 -132
- data/lib/ldclient-rb/flags_state.rb +4 -4
- data/lib/ldclient-rb/impl/big_segments.rb +17 -17
- data/lib/ldclient-rb/impl/context.rb +96 -0
- data/lib/ldclient-rb/impl/context_filter.rb +145 -0
- data/lib/ldclient-rb/impl/diagnostic_events.rb +9 -10
- data/lib/ldclient-rb/impl/evaluator.rb +378 -139
- data/lib/ldclient-rb/impl/evaluator_bucketing.rb +40 -41
- data/lib/ldclient-rb/impl/evaluator_helpers.rb +50 -0
- data/lib/ldclient-rb/impl/evaluator_operators.rb +26 -55
- data/lib/ldclient-rb/impl/event_sender.rb +6 -6
- data/lib/ldclient-rb/impl/event_summarizer.rb +12 -7
- data/lib/ldclient-rb/impl/event_types.rb +18 -30
- data/lib/ldclient-rb/impl/integrations/consul_impl.rb +7 -7
- data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +29 -29
- data/lib/ldclient-rb/impl/integrations/file_data_source.rb +8 -8
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +92 -12
- data/lib/ldclient-rb/impl/model/clause.rb +39 -0
- data/lib/ldclient-rb/impl/model/feature_flag.rb +213 -0
- data/lib/ldclient-rb/impl/model/preprocessed_data.rb +64 -0
- data/lib/ldclient-rb/impl/model/segment.rb +126 -0
- data/lib/ldclient-rb/impl/model/serialization.rb +54 -44
- data/lib/ldclient-rb/impl/repeating_task.rb +1 -1
- data/lib/ldclient-rb/impl/store_data_set_sorter.rb +2 -2
- data/lib/ldclient-rb/impl/unbounded_pool.rb +1 -1
- data/lib/ldclient-rb/impl/util.rb +59 -1
- data/lib/ldclient-rb/in_memory_store.rb +2 -2
- data/lib/ldclient-rb/integrations/consul.rb +1 -1
- data/lib/ldclient-rb/integrations/dynamodb.rb +1 -1
- data/lib/ldclient-rb/integrations/file_data.rb +3 -3
- data/lib/ldclient-rb/integrations/redis.rb +4 -4
- data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +218 -62
- data/lib/ldclient-rb/integrations/test_data.rb +16 -12
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +9 -9
- data/lib/ldclient-rb/interfaces.rb +14 -14
- data/lib/ldclient-rb/ldclient.rb +94 -144
- data/lib/ldclient-rb/memoized_value.rb +1 -1
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +1 -1
- data/lib/ldclient-rb/polling.rb +2 -2
- data/lib/ldclient-rb/reference.rb +274 -0
- data/lib/ldclient-rb/requestor.rb +7 -7
- data/lib/ldclient-rb/stream.rb +8 -9
- data/lib/ldclient-rb/util.rb +4 -19
- data/lib/ldclient-rb/version.rb +1 -1
- data/lib/ldclient-rb.rb +2 -3
- metadata +36 -17
- data/lib/ldclient-rb/file_data_source.rb +0 -23
- data/lib/ldclient-rb/newrelic.rb +0 -17
- data/lib/ldclient-rb/redis_store.rb +0 -88
- data/lib/ldclient-rb/user_filter.rb +0 -52
@@ -163,30 +163,30 @@ module LaunchDarkly
|
|
163
163
|
end
|
164
164
|
|
165
165
|
#
|
166
|
-
# Queries the store for a snapshot of the current segment state for a specific
|
166
|
+
# Queries the store for a snapshot of the current segment state for a specific context.
|
167
167
|
#
|
168
|
-
# The
|
168
|
+
# The context_hash is a base64-encoded string produced by hashing the context key as defined by
|
169
169
|
# the Big Segments specification; the store implementation does not need to know the details
|
170
170
|
# of how this is done, because it deals only with already-hashed keys, but the string can be
|
171
171
|
# assumed to only contain characters that are valid in base64.
|
172
172
|
#
|
173
|
-
# The return value should be either a Hash, or nil if the
|
173
|
+
# The return value should be either a Hash, or nil if the context is not referenced in any big
|
174
174
|
# segments. Each key in the Hash is a "segment reference", which is how segments are
|
175
175
|
# identified in Big Segment data. This string is not identical to the segment key-- the SDK
|
176
176
|
# will add other information. The store implementation should not be concerned with the
|
177
|
-
# format of the string. Each value in the Hash is true if the
|
178
|
-
# the segment, false if the
|
177
|
+
# format of the string. Each value in the Hash is true if the context is explicitly included in
|
178
|
+
# the segment, false if the context is explicitly excluded from the segment-- and is not also
|
179
179
|
# explicitly included (that is, if both an include and an exclude existed in the data, the
|
180
|
-
# include would take precedence). If the
|
180
|
+
# include would take precedence). If the context's status in a particular segment is undefined,
|
181
181
|
# there should be no key or value for that segment.
|
182
182
|
#
|
183
183
|
# This Hash may be cached by the SDK, so it should not be modified after it is created. It
|
184
184
|
# is a snapshot of the segment membership state at one point in time.
|
185
185
|
#
|
186
|
-
# @param
|
187
|
-
# @return [Hash] true/false values for Big Segments that reference this
|
186
|
+
# @param context_hash [String]
|
187
|
+
# @return [Hash] true/false values for Big Segments that reference this context
|
188
188
|
#
|
189
|
-
def get_membership(
|
189
|
+
def get_membership(context_hash)
|
190
190
|
end
|
191
191
|
|
192
192
|
#
|
@@ -216,7 +216,7 @@ module LaunchDarkly
|
|
216
216
|
#
|
217
217
|
# Information about the status of a Big Segment store, provided by {BigSegmentStoreStatusProvider}.
|
218
218
|
#
|
219
|
-
# Big Segments are a specific type of
|
219
|
+
# Big Segments are a specific type of segments. For more information, read the LaunchDarkly
|
220
220
|
# documentation: https://docs.launchdarkly.com/home/users/big-segments
|
221
221
|
#
|
222
222
|
class BigSegmentStoreStatus
|
@@ -226,11 +226,11 @@ module LaunchDarkly
|
|
226
226
|
end
|
227
227
|
|
228
228
|
# True if the Big Segment store is able to respond to queries, so that the SDK can evaluate
|
229
|
-
# whether a
|
229
|
+
# whether a context is in a segment or not.
|
230
230
|
#
|
231
231
|
# If this property is false, the store is not able to make queries (for instance, it may not have
|
232
232
|
# a valid database connection). In this case, the SDK will treat any reference to a Big Segment
|
233
|
-
# as if no
|
233
|
+
# as if no contexts are included in that segment. Also, the {EvaluationReason} associated with
|
234
234
|
# with any flag evaluation that references a Big Segment when the store is not available will
|
235
235
|
# have a `big_segments_status` of `STORE_ERROR`.
|
236
236
|
#
|
@@ -259,14 +259,14 @@ module LaunchDarkly
|
|
259
259
|
#
|
260
260
|
# The Big Segment store is the component that receives information about Big Segments, normally
|
261
261
|
# from a database populated by the LaunchDarkly Relay Proxy. Big Segments are a specific type
|
262
|
-
# of
|
262
|
+
# of segments. For more information, read the LaunchDarkly documentation:
|
263
263
|
# https://docs.launchdarkly.com/home/users/big-segments
|
264
264
|
#
|
265
265
|
# An implementation of this interface is returned by {LDClient#big_segment_store_status_provider}.
|
266
266
|
# Application code never needs to implement this interface.
|
267
267
|
#
|
268
268
|
# There are two ways to interact with the status. One is to simply get the current status; if its
|
269
|
-
# `available` property is true, then the SDK is able to evaluate
|
269
|
+
# `available` property is true, then the SDK is able to evaluate context membership in Big Segments,
|
270
270
|
# and the `stale`` property indicates whether the data might be out of date.
|
271
271
|
#
|
272
272
|
# The other way is to subscribe to status change notifications. Applications may wish to know if
|
data/lib/ldclient-rb/ldclient.rb
CHANGED
@@ -59,7 +59,7 @@ module LaunchDarkly
|
|
59
59
|
|
60
60
|
get_flag = lambda { |key| @store.get(FEATURES, key) }
|
61
61
|
get_segment = lambda { |key| @store.get(SEGMENTS, key) }
|
62
|
-
get_big_segments_membership = lambda { |key| @big_segment_store_manager.
|
62
|
+
get_big_segments_membership = lambda { |key| @big_segment_store_manager.get_context_membership(key) }
|
63
63
|
@evaluator = LaunchDarkly::Impl::Evaluator.new(get_flag, get_segment, get_big_segments_membership, @config.logger)
|
64
64
|
|
65
65
|
if !@config.offline? && @config.send_events && !@config.diagnostic_opt_out?
|
@@ -120,26 +120,20 @@ module LaunchDarkly
|
|
120
120
|
end
|
121
121
|
|
122
122
|
#
|
123
|
-
#
|
124
|
-
# @param user [Hash] the user properties
|
125
|
-
# @param default [Boolean] (false) the value to use if the flag cannot be evaluated
|
126
|
-
# @return [Boolean] the flag value
|
127
|
-
# @deprecated Use {#variation} instead.
|
128
|
-
#
|
129
|
-
def toggle?(key, user, default = false)
|
130
|
-
@config.logger.warn { "[LDClient] toggle? is deprecated. Use variation instead" }
|
131
|
-
variation(key, user, default)
|
132
|
-
end
|
133
|
-
|
134
|
-
#
|
135
|
-
# Creates a hash string that can be used by the JavaScript SDK to identify a user.
|
123
|
+
# Creates a hash string that can be used by the JavaScript SDK to identify a context.
|
136
124
|
# For more information, see [Secure mode](https://docs.launchdarkly.com/sdk/features/secure-mode#ruby).
|
137
125
|
#
|
138
|
-
# @param
|
139
|
-
# @return [String] a hash string
|
126
|
+
# @param context [Hash, LDContext]
|
127
|
+
# @return [String, nil] a hash string or nil if the provided context was invalid
|
140
128
|
#
|
141
|
-
def secure_mode_hash(
|
142
|
-
|
129
|
+
def secure_mode_hash(context)
|
130
|
+
context = Impl::Context::make_context(context)
|
131
|
+
unless context.valid?
|
132
|
+
@config.logger.warn("secure_mode_hash called with invalid context: #{context.error}")
|
133
|
+
return nil
|
134
|
+
end
|
135
|
+
|
136
|
+
OpenSSL::HMAC.hexdigest("sha256", @sdk_key, context.fully_qualified_key)
|
143
137
|
end
|
144
138
|
|
145
139
|
#
|
@@ -165,44 +159,22 @@ module LaunchDarkly
|
|
165
159
|
end
|
166
160
|
|
167
161
|
#
|
168
|
-
# Determines the variation of a feature flag to present
|
169
|
-
#
|
170
|
-
# At a minimum, the user hash should contain a `:key`, which should be the unique
|
171
|
-
# identifier for your user (or, for an anonymous user, a session identifier or
|
172
|
-
# cookie).
|
173
|
-
#
|
174
|
-
# Other supported user attributes include IP address, country code, and an arbitrary hash of
|
175
|
-
# custom attributes. For more about the supported user properties and how they work in
|
176
|
-
# LaunchDarkly, see [Targeting users](https://docs.launchdarkly.com/home/flags/targeting-users).
|
177
|
-
#
|
178
|
-
# The optional `:privateAttributeNames` user property allows you to specify a list of
|
179
|
-
# attribute names that should not be sent back to LaunchDarkly.
|
180
|
-
# [Private attributes](https://docs.launchdarkly.com/home/users/attributes#creating-private-user-attributes)
|
181
|
-
# can also be configured globally in {Config}.
|
182
|
-
#
|
183
|
-
# @example Basic user hash
|
184
|
-
# {key: "my-user-id"}
|
185
|
-
#
|
186
|
-
# @example More complete user hash
|
187
|
-
# {key: "my-user-id", ip: "127.0.0.1", country: "US", custom: {customer_rank: 1000}}
|
188
|
-
#
|
189
|
-
# @example User with a private attribute
|
190
|
-
# {key: "my-user-id", email: "email@example.com", privateAttributeNames: ["email"]}
|
162
|
+
# Determines the variation of a feature flag to present for a context.
|
191
163
|
#
|
192
164
|
# @param key [String] the unique feature key for the feature flag, as shown
|
193
165
|
# on the LaunchDarkly dashboard
|
194
|
-
# @param
|
166
|
+
# @param context [Hash, LDContext] a hash or LDContext instance describing the context requesting the flag
|
195
167
|
# @param default the default value of the flag; this is used if there is an error
|
196
168
|
# condition making it impossible to find or evaluate the flag
|
197
169
|
#
|
198
|
-
# @return the variation
|
170
|
+
# @return the variation for the provided context, or the default value if there's an an error
|
199
171
|
#
|
200
|
-
def variation(key,
|
201
|
-
evaluate_internal(key,
|
172
|
+
def variation(key, context, default)
|
173
|
+
evaluate_internal(key, context, default, false).value
|
202
174
|
end
|
203
175
|
|
204
176
|
#
|
205
|
-
# Determines the variation of a feature flag for a
|
177
|
+
# Determines the variation of a feature flag for a context, like {#variation}, but also
|
206
178
|
# provides additional information about how this value was calculated.
|
207
179
|
#
|
208
180
|
# The return value of `variation_detail` is an {EvaluationDetail} object, which has
|
@@ -218,43 +190,48 @@ module LaunchDarkly
|
|
218
190
|
#
|
219
191
|
# @param key [String] the unique feature key for the feature flag, as shown
|
220
192
|
# on the LaunchDarkly dashboard
|
221
|
-
# @param
|
193
|
+
# @param context [Hash, LDContext] a hash or object describing the context requesting the flag,
|
222
194
|
# @param default the default value of the flag; this is used if there is an error
|
223
195
|
# condition making it impossible to find or evaluate the flag
|
224
196
|
#
|
225
197
|
# @return [EvaluationDetail] an object describing the result
|
226
198
|
#
|
227
|
-
def variation_detail(key,
|
228
|
-
evaluate_internal(key,
|
199
|
+
def variation_detail(key, context, default)
|
200
|
+
evaluate_internal(key, context, default, true)
|
229
201
|
end
|
230
202
|
|
231
203
|
#
|
232
|
-
# Registers the
|
233
|
-
# properties, so that LaunchDarkly will know about that
|
204
|
+
# Registers the context. This method simply creates an analytics event containing the context
|
205
|
+
# properties, so that LaunchDarkly will know about that context if it does not already.
|
234
206
|
#
|
235
|
-
# Calling {#variation} or {#variation_detail} also sends the
|
207
|
+
# Calling {#variation} or {#variation_detail} also sends the context information to
|
236
208
|
# LaunchDarkly (if events are enabled), so you only need to use {#identify} if you
|
237
|
-
# want to identify the
|
209
|
+
# want to identify the context without evaluating a flag.
|
238
210
|
#
|
239
211
|
# Note that event delivery is asynchronous, so the event may not actually be sent
|
240
212
|
# until later; see {#flush}.
|
241
213
|
#
|
242
|
-
# @param
|
243
|
-
# described in {#variation}
|
214
|
+
# @param context [Hash, LDContext] a hash or object describing the context to register
|
244
215
|
# @return [void]
|
245
216
|
#
|
246
|
-
def identify(
|
247
|
-
|
248
|
-
|
217
|
+
def identify(context)
|
218
|
+
context = LaunchDarkly::Impl::Context.make_context(context)
|
219
|
+
unless context.valid?
|
220
|
+
@config.logger.warn("Identify called with invalid context: #{context.error}")
|
221
|
+
return
|
222
|
+
end
|
223
|
+
|
224
|
+
if context.key == ""
|
225
|
+
@config.logger.warn("Identify called with empty key")
|
249
226
|
return
|
250
227
|
end
|
251
|
-
|
252
|
-
@event_processor.record_identify_event(
|
228
|
+
|
229
|
+
@event_processor.record_identify_event(context)
|
253
230
|
end
|
254
231
|
|
255
232
|
#
|
256
|
-
# Tracks that a
|
257
|
-
# containing the specified event name (key),
|
233
|
+
# Tracks that a context performed an event. This method creates a "custom" analytics event
|
234
|
+
# containing the specified event name (key), context properties, and optional data.
|
258
235
|
#
|
259
236
|
# Note that event delivery is asynchronous, so the event may not actually be sent
|
260
237
|
# until later; see {#flush}.
|
@@ -265,8 +242,7 @@ module LaunchDarkly
|
|
265
242
|
# for the latest status.
|
266
243
|
#
|
267
244
|
# @param event_name [String] The name of the event
|
268
|
-
# @param
|
269
|
-
# described in {#variation}
|
245
|
+
# @param context [Hash, LDContext] a hash or object describing the context to track
|
270
246
|
# @param data [Hash] An optional hash containing any additional data associated with the event
|
271
247
|
# @param metric_value [Number] A numeric value used by the LaunchDarkly experimentation
|
272
248
|
# feature in numeric custom metrics. Can be omitted if this event is used by only
|
@@ -274,52 +250,22 @@ module LaunchDarkly
|
|
274
250
|
# for Data Export.
|
275
251
|
# @return [void]
|
276
252
|
#
|
277
|
-
def track(event_name,
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
end
|
282
|
-
sanitize_user(user)
|
283
|
-
@event_processor.record_custom_event(user, event_name, data, metric_value)
|
284
|
-
end
|
285
|
-
|
286
|
-
#
|
287
|
-
# Associates a new and old user object for analytics purposes via an alias event.
|
288
|
-
#
|
289
|
-
# @param current_context [Hash] The current version of a user.
|
290
|
-
# @param previous_context [Hash] The previous version of a user.
|
291
|
-
# @return [void]
|
292
|
-
#
|
293
|
-
def alias(current_context, previous_context)
|
294
|
-
if !current_context || current_context[:key].nil? || !previous_context || previous_context[:key].nil?
|
295
|
-
@config.logger.warn("Alias called with nil user or nil user key!")
|
253
|
+
def track(event_name, context, data = nil, metric_value = nil)
|
254
|
+
context = LaunchDarkly::Impl::Context.make_context(context)
|
255
|
+
unless context.valid?
|
256
|
+
@config.logger.warn("Track called with invalid context: #{context.error}")
|
296
257
|
return
|
297
258
|
end
|
298
|
-
sanitize_user(current_context)
|
299
|
-
sanitize_user(previous_context)
|
300
|
-
@event_processor.record_alias_event(current_context, previous_context)
|
301
|
-
end
|
302
259
|
|
303
|
-
|
304
|
-
# Returns all feature flag values for the given user.
|
305
|
-
#
|
306
|
-
# @deprecated Please use {#all_flags_state} instead. Current versions of the
|
307
|
-
# client-side SDK will not generate analytics events correctly if you pass the
|
308
|
-
# result of `all_flags`.
|
309
|
-
#
|
310
|
-
# @param user [Hash] The end user requesting the feature flags
|
311
|
-
# @return [Hash] a hash of feature flag keys to values
|
312
|
-
#
|
313
|
-
def all_flags(user)
|
314
|
-
all_flags_state(user).values_map
|
260
|
+
@event_processor.record_custom_event(context, event_name, data, metric_value)
|
315
261
|
end
|
316
262
|
|
317
263
|
#
|
318
|
-
# Returns a {FeatureFlagsState} object that encapsulates the state of all feature flags for a given
|
264
|
+
# Returns a {FeatureFlagsState} object that encapsulates the state of all feature flags for a given context,
|
319
265
|
# including the flag values and also metadata that can be used on the front end. This method does not
|
320
266
|
# send analytics events back to LaunchDarkly.
|
321
267
|
#
|
322
|
-
# @param
|
268
|
+
# @param context [Hash, LDContext] a hash or object describing the context requesting the flags,
|
323
269
|
# @param options [Hash] Optional parameters to control how the state is generated
|
324
270
|
# @option options [Boolean] :client_side_only (false) True if only flags marked for use with the
|
325
271
|
# client-side SDK should be included in the state. By default, all flags are included.
|
@@ -331,10 +277,10 @@ module LaunchDarkly
|
|
331
277
|
# of the JSON data if you are passing the flag state to the front end.
|
332
278
|
# @return [FeatureFlagsState] a {FeatureFlagsState} object which can be serialized to JSON
|
333
279
|
#
|
334
|
-
def all_flags_state(
|
280
|
+
def all_flags_state(context, options={})
|
335
281
|
return FeatureFlagsState.new(false) if @config.offline?
|
336
282
|
|
337
|
-
|
283
|
+
unless initialized?
|
338
284
|
if @store.initialized?
|
339
285
|
@config.logger.warn { "Called all_flags_state before client initialization; using last known values from data store" }
|
340
286
|
else
|
@@ -343,8 +289,9 @@ module LaunchDarkly
|
|
343
289
|
end
|
344
290
|
end
|
345
291
|
|
346
|
-
|
347
|
-
|
292
|
+
context = Impl::Context::make_context(context)
|
293
|
+
unless context.valid?
|
294
|
+
@config.logger.error { "[LDClient] Context was invalid for all_flags_state (#{context.error})" }
|
348
295
|
return FeatureFlagsState.new(false)
|
349
296
|
end
|
350
297
|
|
@@ -364,13 +311,13 @@ module LaunchDarkly
|
|
364
311
|
next
|
365
312
|
end
|
366
313
|
begin
|
367
|
-
detail = @evaluator.evaluate(f,
|
314
|
+
detail = @evaluator.evaluate(f, context).detail
|
368
315
|
rescue => exn
|
369
316
|
detail = EvaluationDetail.new(nil, nil, EvaluationReason::error(EvaluationReason::ERROR_EXCEPTION))
|
370
317
|
Util.log_exception(@config.logger, "Error evaluating flag \"#{k}\" in all_flags_state", exn)
|
371
318
|
end
|
372
319
|
|
373
|
-
requires_experiment_data =
|
320
|
+
requires_experiment_data = experiment?(f, detail.reason)
|
374
321
|
flag_state = {
|
375
322
|
key: f[:key],
|
376
323
|
value: detail.value,
|
@@ -425,32 +372,34 @@ module LaunchDarkly
|
|
425
372
|
end
|
426
373
|
end
|
427
374
|
|
375
|
+
# @param context [Hash, LDContext]
|
428
376
|
# @return [EvaluationDetail]
|
429
|
-
def evaluate_internal(key,
|
377
|
+
def evaluate_internal(key, context, default, with_reasons)
|
430
378
|
if @config.offline?
|
431
379
|
return Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default)
|
432
380
|
end
|
433
381
|
|
434
|
-
|
435
|
-
@config.logger.error { "[LDClient] Must specify
|
382
|
+
if context.nil?
|
383
|
+
@config.logger.error { "[LDClient] Must specify context" }
|
436
384
|
detail = Evaluator.error_result(EvaluationReason::ERROR_USER_NOT_SPECIFIED, default)
|
437
385
|
return detail
|
438
386
|
end
|
439
387
|
|
440
|
-
|
441
|
-
|
388
|
+
context = Impl::Context::make_context(context)
|
389
|
+
unless context.valid?
|
390
|
+
@config.logger.error { "[LDClient] Context was invalid for flag evaluation (#{context.error}); returning default value" }
|
442
391
|
detail = Evaluator.error_result(EvaluationReason::ERROR_USER_NOT_SPECIFIED, default)
|
443
392
|
return detail
|
444
393
|
end
|
445
394
|
|
446
|
-
|
395
|
+
unless initialized?
|
447
396
|
if @store.initialized?
|
448
397
|
@config.logger.warn { "[LDClient] Client has not finished initializing; using last known values from feature store" }
|
449
398
|
else
|
450
399
|
@config.logger.error { "[LDClient] Client has not finished initializing; feature store unavailable, returning default value" }
|
451
400
|
detail = Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default)
|
452
|
-
record_unknown_flag_eval(key,
|
453
|
-
return
|
401
|
+
record_unknown_flag_eval(key, context, default, detail.reason, with_reasons)
|
402
|
+
return detail
|
454
403
|
end
|
455
404
|
end
|
456
405
|
|
@@ -459,35 +408,35 @@ module LaunchDarkly
|
|
459
408
|
if feature.nil?
|
460
409
|
@config.logger.info { "[LDClient] Unknown feature flag \"#{key}\". Returning default value" }
|
461
410
|
detail = Evaluator.error_result(EvaluationReason::ERROR_FLAG_NOT_FOUND, default)
|
462
|
-
record_unknown_flag_eval(key,
|
411
|
+
record_unknown_flag_eval(key, context, default, detail.reason, with_reasons)
|
463
412
|
return detail
|
464
413
|
end
|
465
414
|
|
466
415
|
begin
|
467
|
-
res = @evaluator.evaluate(feature,
|
468
|
-
|
416
|
+
res = @evaluator.evaluate(feature, context)
|
417
|
+
unless res.prereq_evals.nil?
|
469
418
|
res.prereq_evals.each do |prereq_eval|
|
470
|
-
record_prereq_flag_eval(prereq_eval.prereq_flag, prereq_eval.prereq_of_flag,
|
419
|
+
record_prereq_flag_eval(prereq_eval.prereq_flag, prereq_eval.prereq_of_flag, context, prereq_eval.detail, with_reasons)
|
471
420
|
end
|
472
421
|
end
|
473
422
|
detail = res.detail
|
474
423
|
if detail.default_value?
|
475
424
|
detail = EvaluationDetail.new(default, nil, detail.reason)
|
476
425
|
end
|
477
|
-
record_flag_eval(feature,
|
478
|
-
|
426
|
+
record_flag_eval(feature, context, detail, default, with_reasons)
|
427
|
+
detail
|
479
428
|
rescue => exn
|
480
429
|
Util.log_exception(@config.logger, "Error evaluating feature flag \"#{key}\"", exn)
|
481
430
|
detail = Evaluator.error_result(EvaluationReason::ERROR_EXCEPTION, default)
|
482
|
-
record_flag_eval_error(feature,
|
483
|
-
|
431
|
+
record_flag_eval_error(feature, context, default, detail.reason, with_reasons)
|
432
|
+
detail
|
484
433
|
end
|
485
434
|
end
|
486
435
|
|
487
|
-
private def record_flag_eval(flag,
|
488
|
-
add_experiment_data =
|
436
|
+
private def record_flag_eval(flag, context, detail, default, with_reasons)
|
437
|
+
add_experiment_data = experiment?(flag, detail.reason)
|
489
438
|
@event_processor.record_eval_event(
|
490
|
-
|
439
|
+
context,
|
491
440
|
flag[:key],
|
492
441
|
flag[:version],
|
493
442
|
detail.variation_index,
|
@@ -499,11 +448,11 @@ module LaunchDarkly
|
|
499
448
|
nil
|
500
449
|
)
|
501
450
|
end
|
502
|
-
|
503
|
-
private def record_prereq_flag_eval(prereq_flag, prereq_of_flag,
|
504
|
-
add_experiment_data =
|
451
|
+
|
452
|
+
private def record_prereq_flag_eval(prereq_flag, prereq_of_flag, context, detail, with_reasons)
|
453
|
+
add_experiment_data = experiment?(prereq_flag, detail.reason)
|
505
454
|
@event_processor.record_eval_event(
|
506
|
-
|
455
|
+
context,
|
507
456
|
prereq_flag[:key],
|
508
457
|
prereq_flag[:version],
|
509
458
|
detail.variation_index,
|
@@ -515,19 +464,26 @@ module LaunchDarkly
|
|
515
464
|
prereq_of_flag[:key]
|
516
465
|
)
|
517
466
|
end
|
518
|
-
|
519
|
-
private def record_flag_eval_error(flag,
|
520
|
-
@event_processor.record_eval_event(
|
467
|
+
|
468
|
+
private def record_flag_eval_error(flag, context, default, reason, with_reasons)
|
469
|
+
@event_processor.record_eval_event(context, flag[:key], flag[:version], nil, default, with_reasons ? reason : nil, default,
|
521
470
|
flag[:trackEvents], flag[:debugEventsUntilDate], nil)
|
522
471
|
end
|
523
472
|
|
524
|
-
|
525
|
-
|
473
|
+
#
|
474
|
+
# @param flag_key [String]
|
475
|
+
# @param context [LaunchDarkly::LDContext]
|
476
|
+
# @param default [any]
|
477
|
+
# @param reason [LaunchDarkly::EvaluationReason]
|
478
|
+
# @param with_reasons [Boolean]
|
479
|
+
#
|
480
|
+
private def record_unknown_flag_eval(flag_key, context, default, reason, with_reasons)
|
481
|
+
@event_processor.record_eval_event(context, flag_key, nil, nil, default, with_reasons ? reason : nil, default,
|
526
482
|
false, nil, nil)
|
527
483
|
end
|
528
484
|
|
529
|
-
private def
|
530
|
-
return false
|
485
|
+
private def experiment?(flag, reason)
|
486
|
+
return false unless reason
|
531
487
|
|
532
488
|
if reason.in_experiment
|
533
489
|
return true
|
@@ -536,7 +492,7 @@ module LaunchDarkly
|
|
536
492
|
case reason[:kind]
|
537
493
|
when 'RULE_MATCH'
|
538
494
|
index = reason[:ruleIndex]
|
539
|
-
|
495
|
+
unless index.nil?
|
540
496
|
rules = flag[:rules] || []
|
541
497
|
return index >= 0 && index < rules.length && rules[index][:trackEvents]
|
542
498
|
end
|
@@ -545,12 +501,6 @@ module LaunchDarkly
|
|
545
501
|
end
|
546
502
|
false
|
547
503
|
end
|
548
|
-
|
549
|
-
private def sanitize_user(user)
|
550
|
-
if user[:key]
|
551
|
-
user[:key] = user[:key].to_s
|
552
|
-
end
|
553
|
-
end
|
554
504
|
end
|
555
505
|
|
556
506
|
#
|
@@ -17,7 +17,7 @@ module LaunchDarkly
|
|
17
17
|
# Attempts to submit a job, but only if a worker is available. Unlike the regular post method,
|
18
18
|
# this returns a value: true if the job was submitted, false if all workers are busy.
|
19
19
|
def post
|
20
|
-
|
20
|
+
unless @semaphore.try_acquire(1)
|
21
21
|
return
|
22
22
|
end
|
23
23
|
@pool.post do
|
data/lib/ldclient-rb/polling.rb
CHANGED
@@ -43,8 +43,8 @@ module LaunchDarkly
|
|
43
43
|
end
|
44
44
|
rescue UnexpectedResponseError => e
|
45
45
|
message = Util.http_error_message(e.status, "polling request", "will retry")
|
46
|
-
@config.logger.error { "[LDClient] #{message}" }
|
47
|
-
|
46
|
+
@config.logger.error { "[LDClient] #{message}" }
|
47
|
+
unless Util.http_error_recoverable?(e.status)
|
48
48
|
@ready.set # if client was waiting on us, make it stop waiting - has no effect if already set
|
49
49
|
stop
|
50
50
|
end
|