Skip to content

Commit

Permalink
Merge branch 'main' into make-provider-stateless
Browse files Browse the repository at this point in the history
Signed-off-by: Todd Baert <[email protected]>
  • Loading branch information
toddbaert authored Sep 18, 2024
2 parents b0d66a5 + ce19ac9 commit 6bdd7ac
Show file tree
Hide file tree
Showing 12 changed files with 118 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
steps:
- uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc
- name: Set up JDK 8
uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88
uses: actions/setup-java@bcfbca5b713b77435aded8de683c7ee5e79f63cb
with:
java-version: '8'
distribution: 'temurin'
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/pullrequest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ jobs:
uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc

- name: Set up JDK 8
uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88
uses: actions/setup-java@bcfbca5b713b77435aded8de683c7ee5e79f63cb
with:
java-version: '8'
distribution: 'temurin'
cache: maven

- name: Initialize CodeQL
uses: github/codeql-action/init@8fd294e26a0e458834582b0fe4988d79966c7c0a
uses: github/codeql-action/init@64431c66d0e98a26d34f9485e5f8e317e14956fc
with:
languages: java

Expand All @@ -45,4 +45,4 @@ jobs:
verbose: true # optional (default = false)

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@8fd294e26a0e458834582b0fe4988d79966c7c0a
uses: github/codeql-action/analyze@64431c66d0e98a26d34f9485e5f8e317e14956fc
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc
- name: Set up JDK 8
if: ${{ steps.release.outputs.release_created }}
uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88
uses: actions/setup-java@bcfbca5b713b77435aded8de683c7ee5e79f63cb
with:
java-version: '8'
distribution: 'temurin'
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/static-code-scanning.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ jobs:

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@8fd294e26a0e458834582b0fe4988d79966c7c0a
uses: github/codeql-action/init@64431c66d0e98a26d34f9485e5f8e317e14956fc
with:
languages: java

- name: Autobuild
uses: github/codeql-action/autobuild@8fd294e26a0e458834582b0fe4988d79966c7c0a
uses: github/codeql-action/autobuild@64431c66d0e98a26d34f9485e5f8e317e14956fc

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@8fd294e26a0e458834582b0fe4988d79966c7c0a
uses: github/codeql-action/analyze@64431c66d0e98a26d34f9485e5f8e317e14956fc
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.2.5</version>
<version>3.2.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
Expand Down
14 changes: 11 additions & 3 deletions src/main/java/dev/openfeature/sdk/OpenFeatureClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ private <T> FlagEvaluationDetails<T> 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);
}
Expand All @@ -160,8 +164,7 @@ private <T> FlagEvaluationDetails<T> 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);
Expand All @@ -170,6 +173,11 @@ private <T> FlagEvaluationDetails<T> evaluateFlag(FlagValueType type, String key
return details;
}

private static <T> void enrichDetailsWithErrorDefaults(T defaultValue, FlagEvaluationDetails<T> details) {
details.setValue(defaultValue);
details.setReason(Reason.ERROR.toString());
}

/**
* Merge invocation contexts with API, transaction and client contexts.
* Does not merge before context.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Boolean> getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) {
return ProviderEvaluation.<Boolean>builder()
.errorMessage(TestConstants.BROKEN_MESSAGE)
.errorCode(ErrorCode.FLAG_NOT_FOUND)
.build();
}

@Override
public ProviderEvaluation<String> getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) {
return ProviderEvaluation.<String>builder()
.errorMessage(TestConstants.BROKEN_MESSAGE)
.errorCode(ErrorCode.FLAG_NOT_FOUND)
.build();
}

@Override
public ProviderEvaluation<Integer> getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) {
return ProviderEvaluation.<Integer>builder()
.errorMessage(TestConstants.BROKEN_MESSAGE)
.errorCode(ErrorCode.FLAG_NOT_FOUND)
.build();
}

@Override
public ProviderEvaluation<Double> getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) {
return ProviderEvaluation.<Double>builder()
.errorMessage(TestConstants.BROKEN_MESSAGE)
.errorCode(ErrorCode.FLAG_NOT_FOUND)
.build();
}

@Override
public ProviderEvaluation<Value> getObjectEvaluation(String key, Value defaultValue, EvaluationContext invocationContext) {
return ProviderEvaluation.<Value>builder()
.errorMessage(TestConstants.BROKEN_MESSAGE)
.errorCode(ErrorCode.FLAG_NOT_FOUND)
.build();
}
}
27 changes: 24 additions & 3 deletions src/test/java/dev/openfeature/sdk/FlagEvaluationSpecTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ void getApiInstance() {
OpenFeatureAPI.getInstance().setProvider(providerName, provider);
assertThat(api.getProviderState(providerName)).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<Boolean> 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.")
Expand Down Expand Up @@ -251,10 +253,29 @@ void getApiInstance() {
@Test void broken_provider() {
FeatureProviderTestUtils.setFeatureProvider(new AlwaysBrokenProvider());
Client c = api.getClient();
assertFalse(c.getBooleanValue("key", false));
FlagEvaluationDetails<Boolean> details = c.getBooleanDetails("key", false);
boolean defaultValue = false;
assertFalse(c.getBooleanValue("key", defaultValue));
FlagEvaluationDetails<Boolean> 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<Boolean> 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.")
Expand Down

0 comments on commit 6bdd7ac

Please sign in to comment.