diff --git a/src/main/java/dev/openfeature/sdk/exceptions/ProviderNotReadyError.java b/src/main/java/dev/openfeature/sdk/exceptions/ProviderNotReadyError.java new file mode 100644 index 000000000..218073441 --- /dev/null +++ b/src/main/java/dev/openfeature/sdk/exceptions/ProviderNotReadyError.java @@ -0,0 +1,12 @@ +package dev.openfeature.sdk.exceptions; + +import dev.openfeature.sdk.ErrorCode; +import lombok.Getter; +import lombok.experimental.StandardException; + +@SuppressWarnings("checkstyle:MissingJavadocType") +@StandardException +public class ProviderNotReadyError extends OpenFeatureError { + private static final long serialVersionUID = 1L; + @Getter private final ErrorCode errorCode = ErrorCode.PROVIDER_NOT_READY; +} diff --git a/src/main/java/dev/openfeature/sdk/providers/memory/InMemoryProvider.java b/src/main/java/dev/openfeature/sdk/providers/memory/InMemoryProvider.java index 1006d88fb..f71e9e364 100644 --- a/src/main/java/dev/openfeature/sdk/providers/memory/InMemoryProvider.java +++ b/src/main/java/dev/openfeature/sdk/providers/memory/InMemoryProvider.java @@ -1,26 +1,29 @@ package dev.openfeature.sdk.providers.memory; -import dev.openfeature.sdk.Value; -import dev.openfeature.sdk.Metadata; -import dev.openfeature.sdk.EventProvider; -import dev.openfeature.sdk.ProviderState; -import dev.openfeature.sdk.ProviderEventDetails; -import dev.openfeature.sdk.ErrorCode; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + import dev.openfeature.sdk.EvaluationContext; +import dev.openfeature.sdk.EventProvider; +import dev.openfeature.sdk.Metadata; import dev.openfeature.sdk.ProviderEvaluation; +import dev.openfeature.sdk.ProviderEventDetails; +import dev.openfeature.sdk.ProviderState; import dev.openfeature.sdk.Reason; +import dev.openfeature.sdk.Value; +import dev.openfeature.sdk.exceptions.FlagNotFoundError; +import dev.openfeature.sdk.exceptions.GeneralError; import dev.openfeature.sdk.exceptions.OpenFeatureError; +import dev.openfeature.sdk.exceptions.ProviderNotReadyError; +import dev.openfeature.sdk.exceptions.TypeMismatchError; import lombok.Getter; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import java.util.Map; -import java.util.HashMap; -import java.util.Set; -import java.util.HashSet; -import java.util.Arrays; -import java.util.ArrayList; - /** * In-memory provider. */ @@ -87,68 +90,52 @@ public void updateFlag(String flagKey, Flag flag) { @Override public ProviderEvaluation getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext evaluationContext) { - return getEvaluation(key, defaultValue, evaluationContext, Boolean.class); + return getEvaluation(key, evaluationContext, Boolean.class); } @Override public ProviderEvaluation getStringEvaluation(String key, String defaultValue, EvaluationContext evaluationContext) { - return getEvaluation(key, defaultValue, evaluationContext, String.class); + return getEvaluation(key, evaluationContext, String.class); } @Override public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext evaluationContext) { - return getEvaluation(key, defaultValue, evaluationContext, Integer.class); + return getEvaluation(key, evaluationContext, Integer.class); } @Override public ProviderEvaluation getDoubleEvaluation(String key, Double defaultValue, EvaluationContext evaluationContext) { - return getEvaluation(key, defaultValue, evaluationContext, Double.class); + return getEvaluation(key, evaluationContext, Double.class); } @SneakyThrows @Override public ProviderEvaluation getObjectEvaluation(String key, Value defaultValue, EvaluationContext evaluationContext) { - return getEvaluation(key, defaultValue, evaluationContext, Value.class); + return getEvaluation(key, evaluationContext, Value.class); } private ProviderEvaluation getEvaluation( - String key, T defaultValue, EvaluationContext evaluationContext, Class expectedType + String key, EvaluationContext evaluationContext, Class expectedType ) throws OpenFeatureError { if (!ProviderState.READY.equals(state)) { - ErrorCode errorCode = ErrorCode.PROVIDER_NOT_READY; - if (ProviderState.ERROR.equals(state)) { - errorCode = ErrorCode.GENERAL; + if (ProviderState.NOT_READY.equals(state)) { + throw new ProviderNotReadyError("provider not yet initialized"); } - return ProviderEvaluation.builder() - .errorCode(errorCode) - .reason(errorCode.name()) - .value(defaultValue) - .build(); + throw new GeneralError("unknown error"); } Flag flag = flags.get(key); if (flag == null) { - return ProviderEvaluation.builder() - .value(defaultValue) - .reason(Reason.ERROR.toString()) - .errorMessage(ErrorCode.FLAG_NOT_FOUND.name()) - .errorCode(ErrorCode.FLAG_NOT_FOUND) - .build(); + throw new FlagNotFoundError("flag " + key + "not found"); } T value; if (flag.getContextEvaluator() != null) { value = (T) flag.getContextEvaluator().evaluate(flag, evaluationContext); } else if (!expectedType.isInstance(flag.getVariants().get(flag.getDefaultVariant()))) { - return ProviderEvaluation.builder() - .value(defaultValue) - .variant(flag.getDefaultVariant()) - .reason(Reason.ERROR.toString()) - .errorMessage(ErrorCode.TYPE_MISMATCH.name()) - .errorCode(ErrorCode.TYPE_MISMATCH) - .build(); + throw new TypeMismatchError("flag " + key + "is not of expected type"); } else { value = (T) flag.getVariants().get(flag.getDefaultVariant()); } diff --git a/src/test/java/dev/openfeature/sdk/e2e/StepDefinitions.java b/src/test/java/dev/openfeature/sdk/e2e/StepDefinitions.java index 650fa242b..903f0bf8e 100644 --- a/src/test/java/dev/openfeature/sdk/e2e/StepDefinitions.java +++ b/src/test/java/dev/openfeature/sdk/e2e/StepDefinitions.java @@ -271,7 +271,6 @@ public void then_the_default_string_value_should_be_returned() { @Then("the reason should indicate an error and the error code should indicate a missing flag with {string}") public void the_reason_should_indicate_an_error_and_the_error_code_should_be_flag_not_found(String errorCode) { assertEquals(Reason.ERROR.toString(), notFoundDetails.getReason()); - assertTrue(notFoundDetails.getErrorMessage().contains(errorCode)); assertTrue(notFoundDetails.getErrorCode().name().equals(errorCode)); } @@ -292,7 +291,6 @@ public void then_the_default_integer_value_should_be_returned() { @Then("the reason should indicate an error and the error code should indicate a type mismatch with {string}") public void the_reason_should_indicate_an_error_and_the_error_code_should_be_type_mismatch(String errorCode) { assertEquals(Reason.ERROR.toString(), typeErrorDetails.getReason()); - assertTrue(typeErrorDetails.getErrorMessage().contains(errorCode)); assertTrue(typeErrorDetails.getErrorCode().name().equals(errorCode)); } diff --git a/src/test/java/dev/openfeature/sdk/providers/memory/InMemoryProviderTest.java b/src/test/java/dev/openfeature/sdk/providers/memory/InMemoryProviderTest.java index f05a6b79f..4cfbb8124 100644 --- a/src/test/java/dev/openfeature/sdk/providers/memory/InMemoryProviderTest.java +++ b/src/test/java/dev/openfeature/sdk/providers/memory/InMemoryProviderTest.java @@ -2,17 +2,22 @@ import com.google.common.collect.ImmutableMap; import dev.openfeature.sdk.Client; +import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.OpenFeatureAPI; import dev.openfeature.sdk.Value; +import dev.openfeature.sdk.exceptions.FlagNotFoundError; +import dev.openfeature.sdk.exceptions.TypeMismatchError; import lombok.SneakyThrows; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.omg.CORBA.DynAnyPackage.TypeMismatch; import java.util.Map; import static dev.openfeature.sdk.Structure.mapToStructure; import static dev.openfeature.sdk.testutils.TestFlagsUtils.buildFlags; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; @@ -81,4 +86,17 @@ void getObjectEvaluation() { assertEquals(expectedObject, client.getObjectValue("object-flag", new Value(true))); } + @Test + void notFound() { + assertThrows(FlagNotFoundError.class, () -> { + provider.getBooleanEvaluation("not-found-flag", false, new ImmutableContext()); + }); + } + + @Test + void typeMismatch() { + assertThrows(TypeMismatchError.class, () -> { + provider.getBooleanEvaluation("string-flag", false, new ImmutableContext()); + }); + } } \ No newline at end of file