Skip to content

Commit 8067af6

Browse files
committed
fix: Adjust migration variation and hook interaction (#264)
1 parent 05678b4 commit 8067af6

File tree

4 files changed

+114
-23
lines changed

4 files changed

+114
-23
lines changed
Original file line numberDiff line numberDiff line change
@@ -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

lib/ldclient-rb/interfaces.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ def before_evaluation(evaluation_series_context, data)
920920
end
921921

922922
#
923-
# The after method is called during the execution of the variation method # after the flag value has been
923+
# The after method is called during the execution of the variation method after the flag value has been
924924
# determined. The method is executed synchronously.
925925
#
926926
# @param evaluation_series_context [EvaluationSeriesContext] Contains read-only information about the evaluation

lib/ldclient-rb/ldclient.rb

+32-22
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require "ldclient-rb/impl/data_store"
55
require "ldclient-rb/impl/diagnostic_events"
66
require "ldclient-rb/impl/evaluator"
7+
require "ldclient-rb/impl/evaluation_with_hook_result"
78
require "ldclient-rb/impl/flag_tracker"
89
require "ldclient-rb/impl/store_client_wrapper"
910
require "ldclient-rb/impl/migrations/tracker"
@@ -217,8 +218,13 @@ def initialized?
217218
# @return the variation for the provided context, or the default value if there's an error
218219
#
219220
def variation(key, context, default)
220-
detail, _, _, = variation_with_flag(key, context, default)
221-
detail.value
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
222228
end
223229

224230
#
@@ -246,11 +252,12 @@ def variation(key, context, default)
246252
#
247253
def variation_detail(key, context, default)
248254
context = Impl::Context::make_context(context)
249-
detail, _, _ = evaluate_with_hooks(key, context, default, :variation_detail) do
250-
evaluate_internal(key, context, default, true)
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)
251258
end
252259

253-
detail
260+
result.evaluation_detail
254261
end
255262

256263
#
@@ -270,15 +277,17 @@ def variation_detail(key, context, default)
270277
# @param method [Symbol]
271278
# @param &block [#call] Implicit passed block
272279
#
280+
# @return [LaunchDarkly::Impl::EvaluationWithHookResult]
281+
#
273282
private def evaluate_with_hooks(key, context, default, method)
274283
return yield if @hooks.empty?
275284

276285
hooks, evaluation_series_context = prepare_hooks(key, context, default, method)
277286
hook_data = execute_before_evaluation(hooks, evaluation_series_context)
278-
evaluation_detail, flag, error = yield
279-
execute_after_evaluation(hooks, evaluation_series_context, hook_data, evaluation_detail)
287+
evaluation_result = yield
288+
execute_after_evaluation(hooks, evaluation_series_context, hook_data, evaluation_result.evaluation_detail)
280289

281-
[evaluation_detail, flag, error]
290+
evaluation_result
282291
end
283292

284293
#
@@ -375,20 +384,24 @@ def migration_variation(key, context, default_stage)
375384
end
376385

377386
context = Impl::Context::make_context(context)
378-
detail, flag, _ = variation_with_flag(key, context, default_stage.to_s)
387+
result = evaluate_with_hooks(key, context, default_stage, :migration_variation) do
388+
detail, flag, _ = variation_with_flag(key, context, default_stage.to_s)
389+
390+
stage = detail.value
391+
stage = stage.to_sym if stage.respond_to? :to_sym
379392

380-
stage = detail.value
381-
stage = stage.to_sym if stage.respond_to? :to_sym
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
382397

383-
if Migrations::VALID_STAGES.include?(stage)
398+
detail = LaunchDarkly::Impl::Evaluator.error_result(LaunchDarkly::EvaluationReason::ERROR_WRONG_TYPE, default_stage.to_s)
384399
tracker = Impl::Migrations::OpTracker.new(@config.logger, key, flag, context, detail, default_stage)
385-
return stage, tracker
386-
end
387400

388-
detail = LaunchDarkly::Impl::Evaluator.error_result(LaunchDarkly::EvaluationReason::ERROR_WRONG_TYPE, default_stage.to_s)
389-
tracker = Impl::Migrations::OpTracker.new(@config.logger, key, flag, context, detail, default_stage)
401+
LaunchDarkly::Impl::EvaluationWithHookResult.new(detail, {stage: default_stage, tracker: tracker})
402+
end
390403

391-
[default_stage, tracker]
404+
[result.results[:stage], result.results[:tracker]]
392405
end
393406

394407
#
@@ -628,16 +641,13 @@ def create_default_data_source(sdk_key, config, diagnostic_accumulator)
628641

629642
#
630643
# @param key [String]
631-
# @param context [Hash, LDContext]
644+
# @param context [LDContext]
632645
# @param default [Object]
633646
#
634647
# @return [Array<EvaluationDetail, [LaunchDarkly::Impl::Model::FeatureFlag, nil], [String, nil]>]
635648
#
636649
def variation_with_flag(key, context, default)
637-
context = Impl::Context::make_context(context)
638-
evaluate_with_hooks(key, context, default, :variation_detail) do
639-
evaluate_internal(key, context, default, false)
640-
end
650+
evaluate_internal(key, context, default, false)
641651
end
642652

643653
#

spec/ldclient_hooks_spec.rb

+47
Original file line numberDiff line numberDiff line change
@@ -148,5 +148,52 @@ module LaunchDarkly
148148
end
149149
end
150150
end
151+
152+
context "migration variation" do
153+
it "EvaluationDetail contains stage value" do
154+
td = Integrations::TestData.data_source
155+
td.update(td.flag("flagkey").variations("off").variation_for_all(0))
156+
157+
detail = nil
158+
config_hook = MockHook.new(->(_, _) { }, ->(_, _, d) { detail = d })
159+
with_client(test_config(data_source: td, hooks: [config_hook])) do |client|
160+
client.migration_variation("flagkey", basic_context, LaunchDarkly::Migrations::STAGE_LIVE)
161+
162+
expect(detail.value).to eq LaunchDarkly::Migrations::STAGE_OFF.to_s
163+
expect(detail.variation_index).to eq 0
164+
expect(detail.reason).to eq EvaluationReason::fallthrough
165+
end
166+
end
167+
168+
it "EvaluationDetail gets default if flag doesn't evaluate to stage" do
169+
td = Integrations::TestData.data_source
170+
td.update(td.flag("flagkey").variations("nonstage").variation_for_all(0))
171+
172+
detail = nil
173+
config_hook = MockHook.new(->(_, _) { }, ->(_, _, d) { detail = d })
174+
with_client(test_config(data_source: td, hooks: [config_hook])) do |client|
175+
client.migration_variation("flagkey", basic_context, LaunchDarkly::Migrations::STAGE_LIVE)
176+
177+
expect(detail.value).to eq LaunchDarkly::Migrations::STAGE_LIVE.to_s
178+
expect(detail.variation_index).to eq nil
179+
expect(detail.reason).to eq EvaluationReason.error(EvaluationReason::ERROR_WRONG_TYPE)
180+
end
181+
end
182+
183+
it "EvaluationDetail default gets converted to off if invalid" do
184+
td = Integrations::TestData.data_source
185+
td.update(td.flag("flagkey").variations("nonstage").variation_for_all(0))
186+
187+
detail = nil
188+
config_hook = MockHook.new(->(_, _) { }, ->(_, _, d) { detail = d })
189+
with_client(test_config(data_source: td, hooks: [config_hook])) do |client|
190+
client.migration_variation("flagkey", basic_context, :invalid)
191+
192+
expect(detail.value).to eq LaunchDarkly::Migrations::STAGE_OFF.to_s
193+
expect(detail.variation_index).to eq nil
194+
expect(detail.reason).to eq EvaluationReason.error(EvaluationReason::ERROR_WRONG_TYPE)
195+
end
196+
end
197+
end
151198
end
152199
end

0 commit comments

Comments
 (0)