diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index 612abab5..8a33cde6 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -20,9 +20,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@2d7d9f7ff5b310f983d059b68785b3c74d8b8edd + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc - name: Set up JDK 8 - uses: actions/setup-java@8e04ddff28554375a9a1096c888a2ef2c9803cd7 + uses: actions/setup-java@40b9536ce5efadffec365e30f26f62a3640d2548 with: java-version: '8' distribution: 'temurin' diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 4539fc16..3490ffed 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -10,17 +10,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out the code - uses: actions/checkout@2d7d9f7ff5b310f983d059b68785b3c74d8b8edd + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc - name: Set up JDK 8 - uses: actions/setup-java@8e04ddff28554375a9a1096c888a2ef2c9803cd7 + uses: actions/setup-java@40b9536ce5efadffec365e30f26f62a3640d2548 with: java-version: '8' distribution: 'temurin' cache: maven - name: Initialize CodeQL - uses: github/codeql-action/init@889597e41d183636b55d03e1a49c44753c626a2e + uses: github/codeql-action/init@799e477cb3a9d4bd3fd1771bf9ffcf3445401cd8 with: languages: java @@ -45,4 +45,4 @@ jobs: verbose: true # optional (default = false) - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@889597e41d183636b55d03e1a49c44753c626a2e + uses: github/codeql-action/analyze@799e477cb3a9d4bd3fd1771bf9ffcf3445401cd8 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0cb1d8ad..f0df52a4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,10 +28,10 @@ jobs: # These steps are only run if this was a merged release-please PR - name: checkout if: ${{ steps.release.outputs.release_created }} - uses: actions/checkout@2d7d9f7ff5b310f983d059b68785b3c74d8b8edd + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc - name: Set up JDK 8 if: ${{ steps.release.outputs.release_created }} - uses: actions/setup-java@8e04ddff28554375a9a1096c888a2ef2c9803cd7 + uses: actions/setup-java@40b9536ce5efadffec365e30f26f62a3640d2548 with: java-version: '8' distribution: 'temurin' diff --git a/.github/workflows/static-code-scanning.yaml b/.github/workflows/static-code-scanning.yaml index 2f82be00..fb8caae2 100644 --- a/.github/workflows/static-code-scanning.yaml +++ b/.github/workflows/static-code-scanning.yaml @@ -29,16 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@2d7d9f7ff5b310f983d059b68785b3c74d8b8edd + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@889597e41d183636b55d03e1a49c44753c626a2e + uses: github/codeql-action/init@799e477cb3a9d4bd3fd1771bf9ffcf3445401cd8 with: languages: java - name: Autobuild - uses: github/codeql-action/autobuild@889597e41d183636b55d03e1a49c44753c626a2e + uses: github/codeql-action/autobuild@799e477cb3a9d4bd3fd1771bf9ffcf3445401cd8 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@889597e41d183636b55d03e1a49c44753c626a2e + uses: github/codeql-action/analyze@799e477cb3a9d4bd3fd1771bf9ffcf3445401cd8 diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 9053c217..8887e458 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1 +1 @@ -{".":"1.10.0"} \ No newline at end of file +{".":"1.11.0"} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5090ec3e..afeb6742 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,39 @@ # Changelog +## [1.11.0](https://github.com/open-feature/java-sdk/compare/v1.10.0...v1.11.0) (2024-09-20) + + +### ๐Ÿ› Bug Fixes + +* **deps:** update dependency io.cucumber:cucumber-bom to v7.19.0 ([#1110](https://github.com/open-feature/java-sdk/issues/1110)) ([2412f29](https://github.com/open-feature/java-sdk/commit/2412f296b5db43c07dc807390070379e7781dbb3)) + + +### โœจ New Features + +* error resolution flow control without exceptions ([#1095](https://github.com/open-feature/java-sdk/issues/1095)) ([6fc0b90](https://github.com/open-feature/java-sdk/commit/6fc0b9061079e50cbab52c951149cf600a922671)) + + +### ๐Ÿงน Chore + +* **deps:** update actions/checkout digest to 6d193bf ([#1091](https://github.com/open-feature/java-sdk/issues/1091)) ([9f6a40e](https://github.com/open-feature/java-sdk/commit/9f6a40ec9ba490edf13adc5501a8cb2b93c32447)) +* **deps:** update actions/checkout digest to b684943 ([#1088](https://github.com/open-feature/java-sdk/issues/1088)) ([7e0c70f](https://github.com/open-feature/java-sdk/commit/7e0c70f7c547b6eb14f30ba4e77657a317161c1f)) +* **deps:** update actions/setup-java digest to 0a40ce6 ([#1107](https://github.com/open-feature/java-sdk/issues/1107)) ([37b56ac](https://github.com/open-feature/java-sdk/commit/37b56ac2dad3c24ac1d898a253e936aa2e3d49eb)) +* **deps:** update actions/setup-java digest to 2dfa201 ([#1094](https://github.com/open-feature/java-sdk/issues/1094)) ([082f574](https://github.com/open-feature/java-sdk/commit/082f5746c84f1bb4ff54cae4f525949f29e7b9c4)) +* **deps:** update actions/setup-java digest to 40b9536 ([#1109](https://github.com/open-feature/java-sdk/issues/1109)) ([244f216](https://github.com/open-feature/java-sdk/commit/244f216582eae071885a950712115ce8b1ad0c19)) +* **deps:** update actions/setup-java digest to 7467385 ([#1092](https://github.com/open-feature/java-sdk/issues/1092)) ([58ead7f](https://github.com/open-feature/java-sdk/commit/58ead7fa9153a53ec530e349f16f9e5e183aa0fb)) +* **deps:** update actions/setup-java digest to bcfbca5 ([#1100](https://github.com/open-feature/java-sdk/issues/1100)) ([c68f78e](https://github.com/open-feature/java-sdk/commit/c68f78e17b6125d74644a5735f9d58348edcc383)) +* **deps:** update dependency org.apache.maven.plugins:maven-gpg-plugin to v3.2.6 ([#1103](https://github.com/open-feature/java-sdk/issues/1103)) ([29901b8](https://github.com/open-feature/java-sdk/commit/29901b87ea93757abe5f5d7a8afc6fc4555aef5c)) +* **deps:** update github/codeql-action digest to 4a01ec7 ([#1101](https://github.com/open-feature/java-sdk/issues/1101)) ([b80fd6d](https://github.com/open-feature/java-sdk/commit/b80fd6d307dee87d8b8f148c954c33b8ff6efae2)) +* **deps:** update github/codeql-action digest to 5618c9f ([#1102](https://github.com/open-feature/java-sdk/issues/1102)) ([d1478c0](https://github.com/open-feature/java-sdk/commit/d1478c001a8e88c358d6743ac3d7c9b5523a893e)) +* **deps:** update github/codeql-action digest to 64431c6 ([#1106](https://github.com/open-feature/java-sdk/issues/1106)) ([ce19ac9](https://github.com/open-feature/java-sdk/commit/ce19ac91f62689a0e0e02e53538e1035bf95387f)) +* **deps:** update github/codeql-action digest to 782de45 ([#1104](https://github.com/open-feature/java-sdk/issues/1104)) ([7cb8908](https://github.com/open-feature/java-sdk/commit/7cb89087daa2809d3fd8447388e58e4f73c975b3)) +* **deps:** update github/codeql-action digest to 799e477 ([#1108](https://github.com/open-feature/java-sdk/issues/1108)) ([17a58ef](https://github.com/open-feature/java-sdk/commit/17a58efc7e0244c0db77e77a78f6d0daf948028f)) +* **deps:** update github/codeql-action digest to 8fd294e ([#1097](https://github.com/open-feature/java-sdk/issues/1097)) ([6408261](https://github.com/open-feature/java-sdk/commit/64082617fade3fa7d647302cc0c5b698f7038d81)) +* **deps:** update github/codeql-action digest to 9b41ced ([#1089](https://github.com/open-feature/java-sdk/issues/1089)) ([870fc27](https://github.com/open-feature/java-sdk/commit/870fc27ed7a30cb011080127f7d040c1a3bb209b)) +* **deps:** update github/codeql-action digest to cb28816 ([#1105](https://github.com/open-feature/java-sdk/issues/1105)) ([9d69ebd](https://github.com/open-feature/java-sdk/commit/9d69ebd94a161e6477f2b4f1f5edd0b79f178852)) +* **deps:** update github/codeql-action digest to d8b1697 ([#1093](https://github.com/open-feature/java-sdk/issues/1093)) ([d60593f](https://github.com/open-feature/java-sdk/commit/d60593fa11f39c6587e280633cf6e15965c0960f)) +* **deps:** update github/codeql-action digest to e817992 ([#1099](https://github.com/open-feature/java-sdk/issues/1099)) ([caec6e3](https://github.com/open-feature/java-sdk/commit/caec6e35e9da3ad88a8de105ca170137f42b907c)) + ## [1.10.0](https://github.com/open-feature/java-sdk/compare/v1.9.1...v1.10.0) (2024-09-05) diff --git a/README.md b/README.md index a7e98462..920e55fd 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ - - Release + + Release @@ -59,7 +59,7 @@ Note that this library is intended to be used in server-side contexts and has no dev.openfeature sdk - 1.10.0 + 1.11.0 ``` @@ -84,7 +84,7 @@ If you would like snapshot builds, this is the relevant repository information: ```groovy dependencies { - implementation 'dev.openfeature:sdk:1.10.0' + implementation 'dev.openfeature:sdk:1.11.0' } ``` diff --git a/pom.xml b/pom.xml index 347d4edc..d43f6351 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ dev.openfeature sdk - 1.10.0 + 1.11.0 UTF-8 @@ -172,7 +172,7 @@ io.cucumber cucumber-bom - 7.18.1 + 7.19.0 pom import @@ -490,7 +490,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.5 + 3.2.6 sign-artifacts diff --git a/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java b/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java index d8004e5d..79982a2b 100644 --- a/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java +++ b/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java @@ -21,7 +21,7 @@ * You should not instantiate this or reference this class. * Use the dev.openfeature.sdk.Client interface instead. * @see Client - * + * * @deprecated // TODO: eventually we will make this non-public. See issue #872 */ @Slf4j @@ -132,7 +132,11 @@ private FlagEvaluationDetails evaluateFlag(FlagValueType type, String key details = FlagEvaluationDetails.from(providerEval, key); if (details.getErrorCode() != null) { - throw ExceptionUtils.instantiateErrorByErrorCode(details.getErrorCode(), details.getErrorMessage()); + OpenFeatureError error = ExceptionUtils.instantiateErrorByErrorCode( + details.getErrorCode(), + details.getErrorMessage()); + enrichDetailsWithErrorDefaults(defaultValue, details); + hookSupport.errorHooks(type, afterHookContext, error, mergedHooks, hints); } else { hookSupport.afterHooks(type, afterHookContext, details, mergedHooks, hints); } @@ -146,8 +150,7 @@ private FlagEvaluationDetails evaluateFlag(FlagValueType type, String key details.setErrorCode(ErrorCode.GENERAL); } details.setErrorMessage(e.getMessage()); - details.setValue(defaultValue); - details.setReason(Reason.ERROR.toString()); + enrichDetailsWithErrorDefaults(defaultValue, details); hookSupport.errorHooks(type, afterHookContext, e, mergedHooks, hints); } finally { hookSupport.afterAllHooks(type, afterHookContext, mergedHooks, hints); @@ -156,6 +159,11 @@ private FlagEvaluationDetails evaluateFlag(FlagValueType type, String key return details; } + private static void enrichDetailsWithErrorDefaults(T defaultValue, FlagEvaluationDetails details) { + details.setValue(defaultValue); + details.setReason(Reason.ERROR.toString()); + } + /** * Merge invocation contexts with API, transaction and client contexts. * Does not merge before context. diff --git a/src/main/java/dev/openfeature/sdk/exceptions/FlagNotFoundError.java b/src/main/java/dev/openfeature/sdk/exceptions/FlagNotFoundError.java index 6685f96d..7c88ebb4 100644 --- a/src/main/java/dev/openfeature/sdk/exceptions/FlagNotFoundError.java +++ b/src/main/java/dev/openfeature/sdk/exceptions/FlagNotFoundError.java @@ -4,14 +4,11 @@ import lombok.Getter; import lombok.experimental.StandardException; -@SuppressWarnings("checkstyle:MissingJavadocType") +@SuppressWarnings({"checkstyle:MissingJavadocType", "squid:S110"}) @StandardException -public class FlagNotFoundError extends OpenFeatureError { +public class FlagNotFoundError extends OpenFeatureErrorWithoutStacktrace { private static final long serialVersionUID = 1L; - @Getter private final ErrorCode errorCode = ErrorCode.FLAG_NOT_FOUND; + @Getter + private final ErrorCode errorCode = ErrorCode.FLAG_NOT_FOUND; - @Override - public synchronized Throwable fillInStackTrace() { - return this; - } } diff --git a/src/main/java/dev/openfeature/sdk/exceptions/OpenFeatureErrorWithoutStacktrace.java b/src/main/java/dev/openfeature/sdk/exceptions/OpenFeatureErrorWithoutStacktrace.java new file mode 100644 index 00000000..2931e6bb --- /dev/null +++ b/src/main/java/dev/openfeature/sdk/exceptions/OpenFeatureErrorWithoutStacktrace.java @@ -0,0 +1,14 @@ +package dev.openfeature.sdk.exceptions; + +import lombok.experimental.StandardException; + +@SuppressWarnings("checkstyle:MissingJavadocType") +@StandardException +public abstract class OpenFeatureErrorWithoutStacktrace extends OpenFeatureError { + private static final long serialVersionUID = 1L; + + @Override + public synchronized Throwable fillInStackTrace() { + return this; + } +} diff --git a/src/main/java/dev/openfeature/sdk/exceptions/ProviderNotReadyError.java b/src/main/java/dev/openfeature/sdk/exceptions/ProviderNotReadyError.java index 21807344..0416eae2 100644 --- a/src/main/java/dev/openfeature/sdk/exceptions/ProviderNotReadyError.java +++ b/src/main/java/dev/openfeature/sdk/exceptions/ProviderNotReadyError.java @@ -4,9 +4,9 @@ import lombok.Getter; import lombok.experimental.StandardException; -@SuppressWarnings("checkstyle:MissingJavadocType") +@SuppressWarnings({"checkstyle:MissingJavadocType", "squid:S110"}) @StandardException -public class ProviderNotReadyError extends OpenFeatureError { +public class ProviderNotReadyError extends OpenFeatureErrorWithoutStacktrace { private static final long serialVersionUID = 1L; @Getter private final ErrorCode errorCode = ErrorCode.PROVIDER_NOT_READY; } diff --git a/src/main/java/dev/openfeature/sdk/exceptions/TypeMismatchError.java b/src/main/java/dev/openfeature/sdk/exceptions/TypeMismatchError.java index d27c6209..9d88922c 100644 --- a/src/main/java/dev/openfeature/sdk/exceptions/TypeMismatchError.java +++ b/src/main/java/dev/openfeature/sdk/exceptions/TypeMismatchError.java @@ -7,6 +7,7 @@ /** * The type of the flag value does not match the expected type. */ +@SuppressWarnings({"checkstyle:MissingJavadocType", "squid:S110"}) @StandardException public class TypeMismatchError extends OpenFeatureError { private static final long serialVersionUID = 1L; diff --git a/src/test/java/dev/openfeature/sdk/AlwaysBrokenWithDetailsProvider.java b/src/test/java/dev/openfeature/sdk/AlwaysBrokenWithDetailsProvider.java new file mode 100644 index 00000000..b3ead41b --- /dev/null +++ b/src/test/java/dev/openfeature/sdk/AlwaysBrokenWithDetailsProvider.java @@ -0,0 +1,53 @@ +package dev.openfeature.sdk; + +import dev.openfeature.sdk.exceptions.FlagNotFoundError; + +public class AlwaysBrokenWithDetailsProvider implements FeatureProvider { + + @Override + public Metadata getMetadata() { + return () -> { + throw new FlagNotFoundError(TestConstants.BROKEN_MESSAGE); + }; + } + + @Override + public ProviderEvaluation getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) { + return ProviderEvaluation.builder() + .errorMessage(TestConstants.BROKEN_MESSAGE) + .errorCode(ErrorCode.FLAG_NOT_FOUND) + .build(); + } + + @Override + public ProviderEvaluation getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) { + return ProviderEvaluation.builder() + .errorMessage(TestConstants.BROKEN_MESSAGE) + .errorCode(ErrorCode.FLAG_NOT_FOUND) + .build(); + } + + @Override + public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) { + return ProviderEvaluation.builder() + .errorMessage(TestConstants.BROKEN_MESSAGE) + .errorCode(ErrorCode.FLAG_NOT_FOUND) + .build(); + } + + @Override + public ProviderEvaluation getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) { + return ProviderEvaluation.builder() + .errorMessage(TestConstants.BROKEN_MESSAGE) + .errorCode(ErrorCode.FLAG_NOT_FOUND) + .build(); + } + + @Override + public ProviderEvaluation getObjectEvaluation(String key, Value defaultValue, EvaluationContext invocationContext) { + return ProviderEvaluation.builder() + .errorMessage(TestConstants.BROKEN_MESSAGE) + .errorCode(ErrorCode.FLAG_NOT_FOUND) + .build(); + } +} diff --git a/src/test/java/dev/openfeature/sdk/FlagEvaluationSpecTest.java b/src/test/java/dev/openfeature/sdk/FlagEvaluationSpecTest.java index b4978cb4..c4a6fd6c 100644 --- a/src/test/java/dev/openfeature/sdk/FlagEvaluationSpecTest.java +++ b/src/test/java/dev/openfeature/sdk/FlagEvaluationSpecTest.java @@ -113,7 +113,9 @@ public void initialize(EvaluationContext evaluationContext) throws Exception { OpenFeatureAPI.getInstance().setProvider(providerName, provider); assertThat(api.getProvider(providerName).getState()).isEqualTo(ProviderState.NOT_READY); Client client = OpenFeatureAPI.getInstance().getClient(providerName); - assertEquals(ErrorCode.PROVIDER_NOT_READY, client.getBooleanDetails("return_error_when_not_initialized", false).getErrorCode()); + FlagEvaluationDetails details = client.getBooleanDetails("return_error_when_not_initialized", false); + assertEquals(ErrorCode.PROVIDER_NOT_READY, details.getErrorCode()); + assertEquals(Reason.ERROR.toString(), details.getReason()); } @Specification(number="1.1.5", text="The API MUST provide a function for retrieving the metadata field of the configured provider.") @@ -259,10 +261,29 @@ public void initialize(EvaluationContext evaluationContext) throws Exception { @Test void broken_provider() { FeatureProviderTestUtils.setFeatureProvider(new AlwaysBrokenProvider()); Client c = api.getClient(); - assertFalse(c.getBooleanValue("key", false)); - FlagEvaluationDetails details = c.getBooleanDetails("key", false); + boolean defaultValue = false; + assertFalse(c.getBooleanValue("key", defaultValue)); + FlagEvaluationDetails details = c.getBooleanDetails("key", defaultValue); assertEquals(ErrorCode.FLAG_NOT_FOUND, details.getErrorCode()); assertEquals(TestConstants.BROKEN_MESSAGE, details.getErrorMessage()); + assertEquals(Reason.ERROR.toString(), details.getReason()); + assertEquals(defaultValue, details.getValue()); + } + + @Specification(number="1.4.8", text="In cases of abnormal execution, the `evaluation details` structure's `error code` field **MUST** contain an `error code`.") + @Specification(number="1.4.9", text="In cases of abnormal execution (network failure, unhandled error, etc) the `reason` field in the `evaluation details` SHOULD indicate an error.") + @Specification(number="1.4.10", text="Methods, functions, or operations on the client MUST NOT throw exceptions, or otherwise abnormally terminate. Flag evaluation calls must always return the `default value` in the event of abnormal execution. Exceptions include functions or methods for the purposes for configuration or setup.") + @Specification(number="1.4.13", text="In cases of abnormal execution, the `evaluation details` structure's `error message` field **MAY** contain a string containing additional details about the nature of the error.") + @Test void broken_provider_withDetails() { + FeatureProviderTestUtils.setFeatureProvider(new AlwaysBrokenWithDetailsProvider()); + Client c = api.getClient(); + boolean defaultValue = false; + assertFalse(c.getBooleanValue("key", defaultValue)); + FlagEvaluationDetails details = c.getBooleanDetails("key", defaultValue); + assertEquals(ErrorCode.FLAG_NOT_FOUND, details.getErrorCode()); + assertEquals(TestConstants.BROKEN_MESSAGE, details.getErrorMessage()); + assertEquals(Reason.ERROR.toString(), details.getReason()); + assertEquals(defaultValue, details.getValue()); } @Specification(number="1.4.11", text="Methods, functions, or operations on the client SHOULD NOT write log messages.") diff --git a/version.txt b/version.txt index 81c871de..1cac385c 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.0 +1.11.0