diff --git a/src/main/java/dev/openfeature/sdk/FlagEvaluationOptions.java b/src/main/java/dev/openfeature/sdk/FlagEvaluationOptions.java index f73bd9631..d586a9371 100644 --- a/src/main/java/dev/openfeature/sdk/FlagEvaluationOptions.java +++ b/src/main/java/dev/openfeature/sdk/FlagEvaluationOptions.java @@ -1,5 +1,6 @@ package dev.openfeature.sdk; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -19,4 +20,13 @@ public class FlagEvaluationOptions { @Builder.Default Map hookHints = new HashMap<>(); + + public static class FlagEvaluationOptionsBuilder { + /** Sets hook hints, normalizing null to an empty map. */ + public FlagEvaluationOptionsBuilder hookHints(Map hookHints) { + this.hookHints$value = hookHints != null ? hookHints : Collections.emptyMap(); + this.hookHints$set = true; + return this; + } + } } diff --git a/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java b/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java index dd64700ef..117b15cc4 100644 --- a/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java +++ b/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java @@ -172,7 +172,8 @@ private FlagEvaluationDetails evaluateFlag( flagOptions = options; } - hookSupportData.hints = Collections.unmodifiableMap(flagOptions.getHookHints()); + var hookHints = flagOptions.getHookHints(); + hookSupportData.hints = hookHints.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(hookHints); var context = new LayeredEvaluationContext( openfeatureApi.getEvaluationContext(), openfeatureApi.getTransactionContext(), diff --git a/src/test/java/dev/openfeature/sdk/HookSpecTest.java b/src/test/java/dev/openfeature/sdk/HookSpecTest.java index 69e47f353..480e81d32 100644 --- a/src/test/java/dev/openfeature/sdk/HookSpecTest.java +++ b/src/test/java/dev/openfeature/sdk/HookSpecTest.java @@ -510,6 +510,27 @@ void missing_hook_hints() { assertTrue(feo.getHookHints().isEmpty()); } + @Test + void null_hook_hints_does_not_throw() { + Hook hook = mockBooleanHook(); + FeatureProvider provider = mock(FeatureProvider.class); + when(provider.getBooleanEvaluation(any(), any(), any())) + .thenReturn(ProviderEvaluation.builder().value(true).build()); + + api.setProviderAndWait(provider); + Client client = api.getClient(); + + assertThatCode(() -> client.getBooleanValue( + "key", + false, + new ImmutableContext(), + FlagEvaluationOptions.builder() + .hook(hook) + .hookHints(null) + .build())) + .doesNotThrowAnyException(); + } + @Test void flag_eval_hook_order() { Hook hook = mockBooleanHook();