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 @@
-
-
+
+
@@ -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