launchdarkly-server-sdk 8.1.0 → 8.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -1
- data/lib/ldclient-rb/config.rb +14 -1
- data/lib/ldclient-rb/context.rb +0 -2
- data/lib/ldclient-rb/events.rb +3 -1
- data/lib/ldclient-rb/impl/context_filter.rb +29 -8
- data/lib/ldclient-rb/impl/evaluation_with_hook_result.rb +34 -0
- data/lib/ldclient-rb/impl/model/feature_flag.rb +2 -3
- data/lib/ldclient-rb/interfaces.rb +85 -0
- data/lib/ldclient-rb/ldclient.rb +154 -16
- data/lib/ldclient-rb/requestor.rb +2 -0
- data/lib/ldclient-rb/version.rb +1 -1
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c04f38ba70b95c1446fc0aaffd2f3020e208501e9ede2313b559848792c1bf81
|
4
|
+
data.tar.gz: 6dd37514e2588d78c995702af1d66d6f00cdc3762ce4908690a2a4e13ab5c46d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40f20870dfa777687da11f7611255ee7f15dde800c7ac9860e5a673643c9d7d19841932162fcabef1d57e6a9ab7710fb3d06ea72567336a5c26ecb0cdd220f74
|
7
|
+
data.tar.gz: bbf303fc081003f0a5e71f2fd38dc48f707958c5c33f6f450982db6c3fecb7076f287460d597414b52c8e4181d95c55babd599fca7877bebe4ff9d82d38e6dd5
|
data/README.md
CHANGED
@@ -39,7 +39,12 @@ Contributing
|
|
39
39
|
------------
|
40
40
|
|
41
41
|
We encourage pull requests and other contributions from the community. Check out our [contributing guidelines](CONTRIBUTING.md) for instructions on how to contribute to this SDK.
|
42
|
-
|
42
|
+
|
43
|
+
Verifying SDK build provenance with the SLSA framework
|
44
|
+
------------
|
45
|
+
|
46
|
+
LaunchDarkly uses the [SLSA framework](https://slsa.dev/spec/v1.0/about) (Supply-chain Levels for Software Artifacts) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published SDK packages. To learn more, see the [provenance guide](PROVENANCE.md).
|
47
|
+
|
43
48
|
About LaunchDarkly
|
44
49
|
-----------
|
45
50
|
|
data/lib/ldclient-rb/config.rb
CHANGED
@@ -43,6 +43,7 @@ module LaunchDarkly
|
|
43
43
|
# @option opts [BigSegmentsConfig] :big_segments See {#big_segments}.
|
44
44
|
# @option opts [Hash] :application See {#application}
|
45
45
|
# @option opts [String] :payload_filter_key See {#payload_filter_key}
|
46
|
+
# @option hooks [Array<Interfaces::Hooks::Hook]
|
46
47
|
#
|
47
48
|
def initialize(opts = {})
|
48
49
|
@base_uri = (opts[:base_uri] || Config.default_base_uri).chomp("/")
|
@@ -75,6 +76,7 @@ module LaunchDarkly
|
|
75
76
|
@big_segments = opts[:big_segments] || BigSegmentsConfig.new(store: nil)
|
76
77
|
@application = LaunchDarkly::Impl::Util.validate_application_info(opts[:application] || {}, @logger)
|
77
78
|
@payload_filter_key = opts[:payload_filter_key]
|
79
|
+
@hooks = (opts[:hooks] || []).keep_if { |hook| hook.is_a? Interfaces::Hooks::Hook }
|
78
80
|
@data_source_update_sink = nil
|
79
81
|
end
|
80
82
|
|
@@ -372,6 +374,17 @@ module LaunchDarkly
|
|
372
374
|
#
|
373
375
|
attr_reader :socket_factory
|
374
376
|
|
377
|
+
#
|
378
|
+
# Initial set of hooks for the client.
|
379
|
+
#
|
380
|
+
# Hooks provide entrypoints which allow for observation of SDK functions.
|
381
|
+
#
|
382
|
+
# LaunchDarkly provides integration packages, and most applications will not
|
383
|
+
# need to implement their own hooks. Refer to the `launchdarkly-server-sdk-otel` gem
|
384
|
+
# for instrumentation.
|
385
|
+
#
|
386
|
+
attr_reader :hooks
|
387
|
+
|
375
388
|
#
|
376
389
|
# The default LaunchDarkly client configuration. This configuration sets
|
377
390
|
# reasonable defaults for most users.
|
@@ -458,7 +471,7 @@ module LaunchDarkly
|
|
458
471
|
# @return [Logger] the Rails logger if in Rails, or a default Logger at WARN level otherwise
|
459
472
|
#
|
460
473
|
def self.default_logger
|
461
|
-
if defined?(Rails) && Rails.respond_to?(:logger)
|
474
|
+
if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
|
462
475
|
Rails.logger
|
463
476
|
else
|
464
477
|
log = ::Logger.new($stdout)
|
data/lib/ldclient-rb/context.rb
CHANGED
@@ -377,8 +377,6 @@ module LaunchDarkly
|
|
377
377
|
# {https://docs.launchdarkly.com/sdk/features/user-config SDK
|
378
378
|
# documentation}.
|
379
379
|
#
|
380
|
-
# @deprecated The old user format will be removed in 8.0.0. Please use the new context specific format.
|
381
|
-
#
|
382
380
|
# @param data [Hash]
|
383
381
|
# @return [LDContext]
|
384
382
|
#
|
data/lib/ldclient-rb/events.rb
CHANGED
@@ -481,12 +481,14 @@ module LaunchDarkly
|
|
481
481
|
key: event.key,
|
482
482
|
value: event.value,
|
483
483
|
}
|
484
|
+
|
484
485
|
out[:default] = event.default unless event.default.nil?
|
485
486
|
out[:variation] = event.variation unless event.variation.nil?
|
486
487
|
out[:version] = event.version unless event.version.nil?
|
487
488
|
out[:prereqOf] = event.prereq_of unless event.prereq_of.nil?
|
488
|
-
out[:
|
489
|
+
out[:context] = @context_filter.filter_redact_anonymous(event.context)
|
489
490
|
out[:reason] = event.reason unless event.reason.nil?
|
491
|
+
|
490
492
|
out
|
491
493
|
|
492
494
|
when LaunchDarkly::Impl::MigrationOpEvent
|
@@ -23,14 +23,32 @@ module LaunchDarkly
|
|
23
23
|
# @return [Hash]
|
24
24
|
#
|
25
25
|
def filter(context)
|
26
|
-
|
26
|
+
internal_filter(context, false)
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Return a hash representation of the provided context with attribute
|
31
|
+
# redaction applied.
|
32
|
+
#
|
33
|
+
# If a context is anonyomous, all attributes will be redacted except
|
34
|
+
# for key, kind, and anonymous.
|
35
|
+
#
|
36
|
+
# @param context [LaunchDarkly::LDContext]
|
37
|
+
# @return [Hash]
|
38
|
+
#
|
39
|
+
def filter_redact_anonymous(context)
|
40
|
+
internal_filter(context, true)
|
41
|
+
end
|
42
|
+
|
43
|
+
private def internal_filter(context, redact_anonymous)
|
44
|
+
return filter_single_context(context, true, redact_anonymous) unless context.multi_kind?
|
27
45
|
|
28
46
|
filtered = {kind: 'multi'}
|
29
47
|
(0...context.individual_context_count).each do |i|
|
30
48
|
c = context.individual_context(i)
|
31
49
|
next if c.nil?
|
32
50
|
|
33
|
-
filtered[c.kind] = filter_single_context(c, false)
|
51
|
+
filtered[c.kind] = filter_single_context(c, false, redact_anonymous)
|
34
52
|
end
|
35
53
|
|
36
54
|
filtered
|
@@ -43,22 +61,24 @@ module LaunchDarkly
|
|
43
61
|
# @param include_kind [Boolean]
|
44
62
|
# @return [Hash]
|
45
63
|
#
|
46
|
-
private def filter_single_context(context, include_kind)
|
64
|
+
private def filter_single_context(context, include_kind, redact_anonymous)
|
47
65
|
filtered = {key: context.key}
|
48
66
|
|
49
67
|
filtered[:kind] = context.kind if include_kind
|
50
|
-
|
68
|
+
|
69
|
+
anonymous = context.get_value(:anonymous)
|
70
|
+
filtered[:anonymous] = true if anonymous
|
51
71
|
|
52
72
|
redacted = []
|
53
73
|
private_attributes = @private_attributes.concat(context.private_attributes)
|
54
74
|
|
55
75
|
name = context.get_value(:name)
|
56
|
-
if !name.nil? && !check_whole_attribute_private(:name, private_attributes, redacted)
|
76
|
+
if !name.nil? && !check_whole_attribute_private(:name, private_attributes, redacted, anonymous && redact_anonymous)
|
57
77
|
filtered[:name] = name
|
58
78
|
end
|
59
79
|
|
60
80
|
context.get_custom_attribute_names.each do |attribute|
|
61
|
-
unless check_whole_attribute_private(attribute, private_attributes, redacted)
|
81
|
+
unless check_whole_attribute_private(attribute, private_attributes, redacted, anonymous && redact_anonymous)
|
62
82
|
value = context.get_value(attribute)
|
63
83
|
filtered[attribute] = redact_json_value(nil, attribute, value, private_attributes, redacted)
|
64
84
|
end
|
@@ -75,10 +95,11 @@ module LaunchDarkly
|
|
75
95
|
# @param attribute [Symbol]
|
76
96
|
# @param private_attributes [Array<Reference>]
|
77
97
|
# @param redacted [Array<Symbol>]
|
98
|
+
# @param redact_all [Boolean]
|
78
99
|
# @return [Boolean]
|
79
100
|
#
|
80
|
-
private def check_whole_attribute_private(attribute, private_attributes, redacted)
|
81
|
-
if @all_attributes_private
|
101
|
+
private def check_whole_attribute_private(attribute, private_attributes, redacted, redact_all)
|
102
|
+
if @all_attributes_private || redact_all
|
82
103
|
redacted << attribute
|
83
104
|
return true
|
84
105
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module LaunchDarkly
|
2
|
+
module Impl
|
3
|
+
#
|
4
|
+
# Simple helper class for returning formatted data.
|
5
|
+
#
|
6
|
+
# The variation methods make use of the new hook support. Those methods all need to return an evaluation detail, and
|
7
|
+
# some other unstructured bit of data.
|
8
|
+
#
|
9
|
+
class EvaluationWithHookResult
|
10
|
+
#
|
11
|
+
# Return the evaluation detail that was generated as part of the evaluation.
|
12
|
+
#
|
13
|
+
# @return [LaunchDarkly::EvaluationDetail]
|
14
|
+
#
|
15
|
+
attr_reader :evaluation_detail
|
16
|
+
|
17
|
+
#
|
18
|
+
# All purpose container for additional return values from the wrapping method
|
19
|
+
#
|
20
|
+
# @return [any]
|
21
|
+
#
|
22
|
+
attr_reader :results
|
23
|
+
|
24
|
+
#
|
25
|
+
# @param evaluation_detail [LaunchDarkly::EvaluationDetail]
|
26
|
+
# @param results [any]
|
27
|
+
#
|
28
|
+
def initialize(evaluation_detail, results = nil)
|
29
|
+
@evaluation_detail = evaluation_detail
|
30
|
+
@results = results
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -37,7 +37,7 @@ module LaunchDarkly
|
|
37
37
|
@off_variation = data[:offVariation]
|
38
38
|
check_variation_range(self, errors, @off_variation, "off variation")
|
39
39
|
@prerequisites = (data[:prerequisites] || []).map do |prereq_data|
|
40
|
-
Prerequisite.new(prereq_data, self
|
40
|
+
Prerequisite.new(prereq_data, self)
|
41
41
|
end
|
42
42
|
@targets = (data[:targets] || []).map do |target_data|
|
43
43
|
Target.new(target_data, self, errors)
|
@@ -118,13 +118,12 @@ module LaunchDarkly
|
|
118
118
|
end
|
119
119
|
|
120
120
|
class Prerequisite
|
121
|
-
def initialize(data, flag
|
121
|
+
def initialize(data, flag)
|
122
122
|
@data = data
|
123
123
|
@key = data[:key]
|
124
124
|
@variation = data[:variation]
|
125
125
|
@failure_result = EvaluatorHelpers.evaluation_detail_for_off_variation(flag,
|
126
126
|
EvaluationReason::prerequisite_failed(@key))
|
127
|
-
check_variation_range(flag, errors_out, @variation, "prerequisite")
|
128
127
|
end
|
129
128
|
|
130
129
|
# @return [Hash]
|
@@ -885,5 +885,90 @@ module LaunchDarkly
|
|
885
885
|
end
|
886
886
|
end
|
887
887
|
end
|
888
|
+
|
889
|
+
module Hooks
|
890
|
+
#
|
891
|
+
# Mixin for extending SDK functionality via hooks.
|
892
|
+
#
|
893
|
+
# All provided hook implementations **MUST** include this mixin. Hooks without this mixin will be ignored.
|
894
|
+
#
|
895
|
+
# This mixin includes default implementations for all hook handlers. This allows LaunchDarkly to expand the list
|
896
|
+
# of hook handlers without breaking customer integrations.
|
897
|
+
#
|
898
|
+
module Hook
|
899
|
+
#
|
900
|
+
# Get metadata about the hook implementation.
|
901
|
+
#
|
902
|
+
# @return [Metadata]
|
903
|
+
#
|
904
|
+
def metadata
|
905
|
+
Metadata.new('UNDEFINED')
|
906
|
+
end
|
907
|
+
|
908
|
+
#
|
909
|
+
# The before method is called during the execution of a variation method before the flag value has been
|
910
|
+
# determined. The method is executed synchronously.
|
911
|
+
#
|
912
|
+
# @param evaluation_series_context [EvaluationSeriesContext] Contains information about the evaluation being
|
913
|
+
# performed. This is not mutable.
|
914
|
+
# @param data [Hash] A record associated with each stage of hook invocations. Each stage is called with the data
|
915
|
+
# of the previous stage for a series. The input record should not be modified.
|
916
|
+
# @return [Hash] Data to use when executing the next state of the hook in the evaluation series.
|
917
|
+
#
|
918
|
+
def before_evaluation(evaluation_series_context, data)
|
919
|
+
data
|
920
|
+
end
|
921
|
+
|
922
|
+
#
|
923
|
+
# The after method is called during the execution of the variation method after the flag value has been
|
924
|
+
# determined. The method is executed synchronously.
|
925
|
+
#
|
926
|
+
# @param evaluation_series_context [EvaluationSeriesContext] Contains read-only information about the evaluation
|
927
|
+
# being performed.
|
928
|
+
# @param data [Hash] A record associated with each stage of hook invocations. Each stage is called with the data
|
929
|
+
# of the previous stage for a series.
|
930
|
+
# @param detail [LaunchDarkly::EvaluationDetail] The result of the evaluation. This value should not be
|
931
|
+
# modified.
|
932
|
+
# @return [Hash] Data to use when executing the next state of the hook in the evaluation series.
|
933
|
+
#
|
934
|
+
def after_evaluation(evaluation_series_context, data, detail)
|
935
|
+
data
|
936
|
+
end
|
937
|
+
end
|
938
|
+
|
939
|
+
#
|
940
|
+
# Metadata data class used for annotating hook implementations.
|
941
|
+
#
|
942
|
+
class Metadata
|
943
|
+
attr_reader :name
|
944
|
+
|
945
|
+
def initialize(name)
|
946
|
+
@name = name
|
947
|
+
end
|
948
|
+
end
|
949
|
+
|
950
|
+
#
|
951
|
+
# Contextual information that will be provided to handlers during evaluation series.
|
952
|
+
#
|
953
|
+
class EvaluationSeriesContext
|
954
|
+
attr_reader :key
|
955
|
+
attr_reader :context
|
956
|
+
attr_reader :default_value
|
957
|
+
attr_reader :method
|
958
|
+
|
959
|
+
#
|
960
|
+
# @param key [String]
|
961
|
+
# @param context [LaunchDarkly::LDContext]
|
962
|
+
# @param default_value [any]
|
963
|
+
# @param method [Symbol]
|
964
|
+
#
|
965
|
+
def initialize(key, context, default_value, method)
|
966
|
+
@key = key
|
967
|
+
@context = context
|
968
|
+
@default_value = default_value
|
969
|
+
@method = method
|
970
|
+
end
|
971
|
+
end
|
972
|
+
end
|
888
973
|
end
|
889
974
|
end
|
data/lib/ldclient-rb/ldclient.rb
CHANGED
@@ -4,9 +4,11 @@ require "ldclient-rb/impl/data_source"
|
|
4
4
|
require "ldclient-rb/impl/data_store"
|
5
5
|
require "ldclient-rb/impl/diagnostic_events"
|
6
6
|
require "ldclient-rb/impl/evaluator"
|
7
|
+
require "ldclient-rb/impl/evaluation_with_hook_result"
|
7
8
|
require "ldclient-rb/impl/flag_tracker"
|
8
9
|
require "ldclient-rb/impl/store_client_wrapper"
|
9
10
|
require "ldclient-rb/impl/migrations/tracker"
|
11
|
+
require "concurrent"
|
10
12
|
require "concurrent/atomics"
|
11
13
|
require "digest/sha1"
|
12
14
|
require "forwardable"
|
@@ -54,6 +56,7 @@ module LaunchDarkly
|
|
54
56
|
end
|
55
57
|
|
56
58
|
@sdk_key = sdk_key
|
59
|
+
@hooks = Concurrent::Array.new(config.hooks)
|
57
60
|
|
58
61
|
@shared_executor = Concurrent::SingleThreadExecutor.new
|
59
62
|
|
@@ -131,6 +134,23 @@ module LaunchDarkly
|
|
131
134
|
end
|
132
135
|
end
|
133
136
|
|
137
|
+
#
|
138
|
+
# Add a hook to the client. In order to register a hook before the client starts, please use the `hooks` property of
|
139
|
+
# {#LDConfig}.
|
140
|
+
#
|
141
|
+
# Hooks provide entrypoints which allow for observation of SDK functions.
|
142
|
+
#
|
143
|
+
# @param hook [Interfaces::Hooks::Hook]
|
144
|
+
#
|
145
|
+
def add_hook(hook)
|
146
|
+
unless hook.is_a?(Interfaces::Hooks::Hook)
|
147
|
+
@config.logger.error { "[LDClient] Attempted to add a hook that does not include the LaunchDarkly::Intefaces::Hooks::Hook mixin. Ignoring." }
|
148
|
+
return
|
149
|
+
end
|
150
|
+
|
151
|
+
@hooks.push(hook)
|
152
|
+
end
|
153
|
+
|
134
154
|
#
|
135
155
|
# Tells the client that all pending analytics events should be delivered as soon as possible.
|
136
156
|
#
|
@@ -198,8 +218,13 @@ module LaunchDarkly
|
|
198
218
|
# @return the variation for the provided context, or the default value if there's an error
|
199
219
|
#
|
200
220
|
def variation(key, context, default)
|
201
|
-
|
202
|
-
|
221
|
+
context = Impl::Context::make_context(context)
|
222
|
+
result = evaluate_with_hooks(key, context, default, :variation) do
|
223
|
+
detail, _, _ = variation_with_flag(key, context, default)
|
224
|
+
LaunchDarkly::Impl::EvaluationWithHookResult.new(detail)
|
225
|
+
end
|
226
|
+
|
227
|
+
result.evaluation_detail.value
|
203
228
|
end
|
204
229
|
|
205
230
|
#
|
@@ -226,8 +251,118 @@ module LaunchDarkly
|
|
226
251
|
# @return [EvaluationDetail] an object describing the result
|
227
252
|
#
|
228
253
|
def variation_detail(key, context, default)
|
229
|
-
|
230
|
-
|
254
|
+
context = Impl::Context::make_context(context)
|
255
|
+
result = evaluate_with_hooks(key, context, default, :variation_detail) do
|
256
|
+
detail, _, _ = evaluate_internal(key, context, default, true)
|
257
|
+
LaunchDarkly::Impl::EvaluationWithHookResult.new(detail)
|
258
|
+
end
|
259
|
+
|
260
|
+
result.evaluation_detail
|
261
|
+
end
|
262
|
+
|
263
|
+
#
|
264
|
+
# evaluate_with_hook will run the provided block, wrapping it with evaluation hook support.
|
265
|
+
#
|
266
|
+
# Example:
|
267
|
+
#
|
268
|
+
# ```ruby
|
269
|
+
# evaluate_with_hooks(key, context, default, method) do
|
270
|
+
# puts 'This is being wrapped with evaluation hooks'
|
271
|
+
# end
|
272
|
+
# ```
|
273
|
+
#
|
274
|
+
# @param key [String]
|
275
|
+
# @param context [LDContext]
|
276
|
+
# @param default [any]
|
277
|
+
# @param method [Symbol]
|
278
|
+
# @param &block [#call] Implicit passed block
|
279
|
+
#
|
280
|
+
# @return [LaunchDarkly::Impl::EvaluationWithHookResult]
|
281
|
+
#
|
282
|
+
private def evaluate_with_hooks(key, context, default, method)
|
283
|
+
return yield if @hooks.empty?
|
284
|
+
|
285
|
+
hooks, evaluation_series_context = prepare_hooks(key, context, default, method)
|
286
|
+
hook_data = execute_before_evaluation(hooks, evaluation_series_context)
|
287
|
+
evaluation_result = yield
|
288
|
+
execute_after_evaluation(hooks, evaluation_series_context, hook_data, evaluation_result.evaluation_detail)
|
289
|
+
|
290
|
+
evaluation_result
|
291
|
+
end
|
292
|
+
|
293
|
+
#
|
294
|
+
# Execute the :before_evaluation stage of the evaluation series.
|
295
|
+
#
|
296
|
+
# This method will return the results of each hook, indexed into an array in the same order as the hooks. If a hook
|
297
|
+
# raised an uncaught exception, the value will be nil.
|
298
|
+
#
|
299
|
+
# @param hooks [Array<Interfaces::Hooks::Hook>]
|
300
|
+
# @param evaluation_series_context [EvaluationSeriesContext]
|
301
|
+
#
|
302
|
+
# @return [Array<any>]
|
303
|
+
#
|
304
|
+
private def execute_before_evaluation(hooks, evaluation_series_context)
|
305
|
+
hooks.map do |hook|
|
306
|
+
try_execute_stage(:before_evaluation, hook.metadata.name) do
|
307
|
+
hook.before_evaluation(evaluation_series_context, {})
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
#
|
313
|
+
# Execute the :after_evaluation stage of the evaluation series.
|
314
|
+
#
|
315
|
+
# This method will return the results of each hook, indexed into an array in the same order as the hooks. If a hook
|
316
|
+
# raised an uncaught exception, the value will be nil.
|
317
|
+
#
|
318
|
+
# @param hooks [Array<Interfaces::Hooks::Hook>]
|
319
|
+
# @param evaluation_series_context [EvaluationSeriesContext]
|
320
|
+
# @param hook_data [Array<any>]
|
321
|
+
# @param evaluation_detail [EvaluationDetail]
|
322
|
+
#
|
323
|
+
# @return [Array<any>]
|
324
|
+
#
|
325
|
+
private def execute_after_evaluation(hooks, evaluation_series_context, hook_data, evaluation_detail)
|
326
|
+
hooks.zip(hook_data).reverse.map do |(hook, data)|
|
327
|
+
try_execute_stage(:after_evaluation, hook.metadata.name) do
|
328
|
+
hook.after_evaluation(evaluation_series_context, data, evaluation_detail)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
#
|
334
|
+
# Try to execute the provided block. If execution raises an exception, catch and log it, then move on with
|
335
|
+
# execution.
|
336
|
+
#
|
337
|
+
# @return [any]
|
338
|
+
#
|
339
|
+
private def try_execute_stage(method, hook_name)
|
340
|
+
begin
|
341
|
+
yield
|
342
|
+
rescue => e
|
343
|
+
@config.logger.error { "[LDClient] An error occurred in #{method} of the hook #{hook_name}: #{e}" }
|
344
|
+
nil
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
#
|
349
|
+
# Return a copy of the existing hooks and a few instance of the EvaluationSeriesContext used for the evaluation series.
|
350
|
+
#
|
351
|
+
# @param key [String]
|
352
|
+
# @param context [LDContext]
|
353
|
+
# @param default [any]
|
354
|
+
# @param method [Symbol]
|
355
|
+
# @return [Array[Array<Interfaces::Hooks::Hook>, Interfaces::Hooks::EvaluationSeriesContext]]
|
356
|
+
#
|
357
|
+
private def prepare_hooks(key, context, default, method)
|
358
|
+
# Copy the hooks to use a consistent set during the evaluation series.
|
359
|
+
#
|
360
|
+
# Hooks can be added and we want to ensure all correct stages for a given hook execute. For example, we do not
|
361
|
+
# want to trigger the after_evaluation method without also triggering the before_evaluation method.
|
362
|
+
hooks = @hooks.dup
|
363
|
+
evaluation_series_context = Interfaces::Hooks::EvaluationSeriesContext.new(key, context, default, method)
|
364
|
+
|
365
|
+
[hooks, evaluation_series_context]
|
231
366
|
end
|
232
367
|
|
233
368
|
#
|
@@ -249,20 +384,24 @@ module LaunchDarkly
|
|
249
384
|
end
|
250
385
|
|
251
386
|
context = Impl::Context::make_context(context)
|
252
|
-
|
387
|
+
result = evaluate_with_hooks(key, context, default_stage, :migration_variation) do
|
388
|
+
detail, flag, _ = variation_with_flag(key, context, default_stage.to_s)
|
253
389
|
|
254
|
-
|
255
|
-
|
390
|
+
stage = detail.value
|
391
|
+
stage = stage.to_sym if stage.respond_to? :to_sym
|
256
392
|
|
257
|
-
|
393
|
+
if Migrations::VALID_STAGES.include?(stage)
|
394
|
+
tracker = Impl::Migrations::OpTracker.new(@config.logger, key, flag, context, detail, default_stage)
|
395
|
+
next LaunchDarkly::Impl::EvaluationWithHookResult.new(detail, {stage: stage, tracker: tracker})
|
396
|
+
end
|
397
|
+
|
398
|
+
detail = LaunchDarkly::Impl::Evaluator.error_result(LaunchDarkly::EvaluationReason::ERROR_WRONG_TYPE, default_stage.to_s)
|
258
399
|
tracker = Impl::Migrations::OpTracker.new(@config.logger, key, flag, context, detail, default_stage)
|
259
|
-
return stage, tracker
|
260
|
-
end
|
261
400
|
|
262
|
-
|
263
|
-
|
401
|
+
LaunchDarkly::Impl::EvaluationWithHookResult.new(detail, {stage: default_stage, tracker: tracker})
|
402
|
+
end
|
264
403
|
|
265
|
-
[
|
404
|
+
[result.results[:stage], result.results[:tracker]]
|
266
405
|
end
|
267
406
|
|
268
407
|
#
|
@@ -502,7 +641,7 @@ module LaunchDarkly
|
|
502
641
|
|
503
642
|
#
|
504
643
|
# @param key [String]
|
505
|
-
# @param context [
|
644
|
+
# @param context [LDContext]
|
506
645
|
# @param default [Object]
|
507
646
|
#
|
508
647
|
# @return [Array<EvaluationDetail, [LaunchDarkly::Impl::Model::FeatureFlag, nil], [String, nil]>]
|
@@ -513,7 +652,7 @@ module LaunchDarkly
|
|
513
652
|
|
514
653
|
#
|
515
654
|
# @param key [String]
|
516
|
-
# @param context [
|
655
|
+
# @param context [LDContext]
|
517
656
|
# @param default [Object]
|
518
657
|
# @param with_reasons [Boolean]
|
519
658
|
#
|
@@ -530,7 +669,6 @@ module LaunchDarkly
|
|
530
669
|
return detail, nil, "no context provided"
|
531
670
|
end
|
532
671
|
|
533
|
-
context = Impl::Context::make_context(context)
|
534
672
|
unless context.valid?
|
535
673
|
@config.logger.error { "[LDClient] Context was invalid for evaluation of flag '#{key}' (#{context.error}); returning default value" }
|
536
674
|
detail = Evaluator.error_result(EvaluationReason::ERROR_USER_NOT_SPECIFIED, default)
|
data/lib/ldclient-rb/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: launchdarkly-server-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 8.
|
4
|
+
version: 8.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- LaunchDarkly
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-dynamodb
|
@@ -234,6 +234,20 @@ dependencies:
|
|
234
234
|
- - '='
|
235
235
|
- !ruby/object:Gem::Version
|
236
236
|
version: 2.2.2
|
237
|
+
- !ruby/object:Gem::Dependency
|
238
|
+
name: observer
|
239
|
+
requirement: !ruby/object:Gem::Requirement
|
240
|
+
requirements:
|
241
|
+
- - "~>"
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: 0.1.2
|
244
|
+
type: :runtime
|
245
|
+
prerelease: false
|
246
|
+
version_requirements: !ruby/object:Gem::Requirement
|
247
|
+
requirements:
|
248
|
+
- - "~>"
|
249
|
+
- !ruby/object:Gem::Version
|
250
|
+
version: 0.1.2
|
237
251
|
- !ruby/object:Gem::Dependency
|
238
252
|
name: json
|
239
253
|
requirement: !ruby/object:Gem::Requirement
|
@@ -295,6 +309,7 @@ files:
|
|
295
309
|
- lib/ldclient-rb/impl/data_store.rb
|
296
310
|
- lib/ldclient-rb/impl/dependency_tracker.rb
|
297
311
|
- lib/ldclient-rb/impl/diagnostic_events.rb
|
312
|
+
- lib/ldclient-rb/impl/evaluation_with_hook_result.rb
|
298
313
|
- lib/ldclient-rb/impl/evaluator.rb
|
299
314
|
- lib/ldclient-rb/impl/evaluator_bucketing.rb
|
300
315
|
- lib/ldclient-rb/impl/evaluator_helpers.rb
|
@@ -361,7 +376,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
361
376
|
- !ruby/object:Gem::Version
|
362
377
|
version: '0'
|
363
378
|
requirements: []
|
364
|
-
rubygems_version: 3.5.
|
379
|
+
rubygems_version: 3.5.9
|
365
380
|
signing_key:
|
366
381
|
specification_version: 4
|
367
382
|
summary: LaunchDarkly SDK for Ruby
|