launchdarkly-server-sdk 6.4.0 → 7.0.1
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 +4 -4
- data/lib/ldclient-rb/config.rb +102 -56
- 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 +379 -131
- data/lib/ldclient-rb/impl/evaluator_bucketing.rb +40 -41
- data/lib/ldclient-rb/impl/evaluator_helpers.rb +31 -34
- 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 +45 -0
- data/lib/ldclient-rb/impl/model/feature_flag.rb +232 -0
- data/lib/ldclient-rb/impl/model/preprocessed_data.rb +8 -121
- data/lib/ldclient-rb/impl/model/segment.rb +132 -0
- data/lib/ldclient-rb/impl/model/serialization.rb +52 -12
- 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 +2 -2
- 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 +5 -5
- data/lib/ldclient-rb/stream.rb +7 -8
- data/lib/ldclient-rb/util.rb +4 -19
- data/lib/ldclient-rb/version.rb +1 -1
- data/lib/ldclient-rb.rb +2 -3
- metadata +34 -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
|