in openfeature-provider/src/main/java/com/spotify/confidence/ConfidenceFeatureProvider.java [143:225]
public ProviderEvaluation<Value> getObjectEvaluation(
String key, Value defaultValue, EvaluationContext ctx) {
final FlagPath flagPath;
try {
flagPath = FlagPath.getPath(key);
} catch (IllegalValuePath e) {
log.warn(e.getMessage());
throw new RuntimeException(e);
}
final Struct evaluationContext = OpenFeatureUtils.convertToProto(ctx);
// resolve the flag by calling the resolver API
final ResolveFlagsResponse resolveFlagResponse;
try {
final String requestFlagName = "flags/" + flagPath.getFlag();
resolveFlagResponse =
confidence
.withContext(
Map.of(
OPEN_FEATURE_RESOLVE_CONTEXT_KEY,
ConfidenceValue.Struct.fromProto(evaluationContext)))
.resolveFlags(requestFlagName)
.get();
if (resolveFlagResponse.getResolvedFlagsList().isEmpty()) {
log.warn("No active flag '{}' was found", flagPath.getFlag());
throw new FlagNotFoundError(
String.format("No active flag '%s' was found", flagPath.getFlag()));
}
final String responseFlagName = resolveFlagResponse.getResolvedFlags(0).getFlag();
if (!requestFlagName.equals(responseFlagName)) {
log.warn("Unexpected flag '{}' from remote", responseFlagName.replaceFirst("^flags/", ""));
throw new FlagNotFoundError(
String.format(
"Unexpected flag '%s' from remote", responseFlagName.replaceFirst("^flags/", "")));
}
final ResolvedFlag resolvedFlag = resolveFlagResponse.getResolvedFlags(0);
if (resolvedFlag.getVariant().isEmpty()) {
log.debug(
String.format(
"The server returned no assignment for the flag '%s'. Typically, this happens "
+ "if no configured rules matches the given evaluation context.",
flagPath.getFlag()));
return ProviderEvaluation.<Value>builder()
.value(defaultValue)
.reason(
"The server returned no assignment for the flag. Typically, this happens "
+ "if no configured rules matches the given evaluation context.")
.build();
} else {
final Value fullValue =
OpenFeatureTypeMapper.from(resolvedFlag.getValue(), resolvedFlag.getFlagSchema());
// if a path is given, extract expected portion from the structured value
Value value = OpenFeatureUtils.getValueForPath(flagPath.getPath(), fullValue);
if (value.isNull()) {
value = defaultValue;
}
// regular resolve was successful
return ProviderEvaluation.<Value>builder()
.value(value)
.variant(resolvedFlag.getVariant())
.build();
}
} catch (StatusRuntimeException | InterruptedException | ExecutionException e) {
// If the remote API is unreachable, for now we fall back to the default value. However, we
// should consider maintaining a local resolve-history to avoid flickering experience in case
// of a temporarily unavailable backend
if (e instanceof StatusRuntimeException) {
handleStatusRuntimeException((StatusRuntimeException) e);
} else if (e.getCause() instanceof StatusRuntimeException) {
handleStatusRuntimeException((StatusRuntimeException) e.getCause());
}
throw new GeneralError("Unknown error occurred when calling the provider backend");
}
}