public ProviderEvaluation getObjectEvaluation()

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");
    }
  }