All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.openfeature.contrib.providers.flipt.FliptProvider Maven / Gradle / Ivy

The newest version!
package dev.openfeature.contrib.providers.flipt;

import static dev.openfeature.sdk.Reason.DEFAULT;
import static dev.openfeature.sdk.Reason.TARGETING_MATCH;

import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import dev.openfeature.sdk.EvaluationContext;
import dev.openfeature.sdk.EventProvider;
import dev.openfeature.sdk.ImmutableMetadata;
import dev.openfeature.sdk.Metadata;
import dev.openfeature.sdk.ProviderEvaluation;
import dev.openfeature.sdk.Value;
import dev.openfeature.sdk.exceptions.GeneralError;
import io.flipt.api.FliptClient;
import io.flipt.api.evaluation.models.BooleanEvaluationResponse;
import io.flipt.api.evaluation.models.EvaluationRequest;
import io.flipt.api.evaluation.models.VariantEvaluationResponse;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * Provider implementation for Flipt.
 */
@Slf4j
public class FliptProvider extends EventProvider {

    @Getter
    private static final String NAME = "Flipt";
    public static final String PROVIDER_NOT_YET_INITIALIZED = "provider not yet initialized";
    public static final String UNKNOWN_ERROR = "unknown error";

    @Getter(AccessLevel.PROTECTED)
    private FliptProviderConfig fliptProviderConfig;

    @Setter(AccessLevel.PROTECTED)
    @Getter
    private FliptClient fliptClient;
    private final AtomicBoolean isInitialized = new AtomicBoolean(false);

    /**
     * Constructor.
     * 
     * @param fliptProviderConfig FliptProviderConfig
     */
    public FliptProvider(FliptProviderConfig fliptProviderConfig) {
        this.fliptProviderConfig = fliptProviderConfig;
    }

    /**
     * Initialize the provider.
     * 
     * @param evaluationContext evaluation context
     * @throws Exception on error
     */
    @Override
    public void initialize(EvaluationContext evaluationContext) throws Exception {
        boolean initialized = isInitialized.getAndSet(true);
        if (initialized) {
            throw new GeneralError("already initialized");
        }
        super.initialize(evaluationContext);
        fliptClient = fliptProviderConfig.getFliptClientBuilder().build();

        log.info("finished initializing provider");
    }

    @Override
    public Metadata getMetadata() {
        return () -> NAME;
    }

    @Override
    public ProviderEvaluation getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) {
        Map contextMap = ContextTransformer.transform(ctx);
        EvaluationRequest request = EvaluationRequest.builder().namespaceKey(fliptProviderConfig.getNamespace())
                .flagKey(key).entityId(ctx.getTargetingKey()).context(contextMap).build();

        BooleanEvaluationResponse response = null;
        try {
            response = fliptClient.evaluation().evaluateBoolean(request);
        } catch (Exception e) {
            log.error("Error evaluating boolean", e);
            throw new GeneralError(e.getMessage());
        }

        return ProviderEvaluation.builder()
                .value(response.isEnabled())
                .reason(response.getReason().toString())
                .build();
    }

    @Override
    public ProviderEvaluation getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) {
        ProviderEvaluation valueProviderEvaluation = evaluateVariant(String.class, key, new Value(defaultValue),
                ctx);
        return ProviderEvaluation.builder()
                .value(valueProviderEvaluation.getValue().asString())
                .variant(valueProviderEvaluation.getVariant())
                .errorCode(valueProviderEvaluation.getErrorCode())
                .reason(valueProviderEvaluation.getReason())
                .flagMetadata(valueProviderEvaluation.getFlagMetadata())
                .build();
    }

    @Override
    public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) {
        ProviderEvaluation valueProviderEvaluation = evaluateVariant(Integer.class, key, new Value(defaultValue),
                ctx);
        Integer value = getIntegerValue(valueProviderEvaluation, defaultValue);
        return ProviderEvaluation.builder()
                .value(value)
                .variant(valueProviderEvaluation.getVariant())
                .errorCode(valueProviderEvaluation.getErrorCode())
                .reason(valueProviderEvaluation.getReason())
                .flagMetadata(valueProviderEvaluation.getFlagMetadata())
                .build();
    }

    private static Integer getIntegerValue(ProviderEvaluation valueProviderEvaluation, Integer defaultValue) {
        String valueStr = valueProviderEvaluation.getValue().asObject().toString();
        try {
            return Integer.parseInt(valueStr);
        } catch (NumberFormatException ex) {
            return defaultValue;
        }
    }

    @Override
    public ProviderEvaluation getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) {
        ProviderEvaluation valueProviderEvaluation = evaluateVariant(Double.class, key, new Value(defaultValue),
                ctx);
        Double value = getDoubleValue(valueProviderEvaluation, defaultValue);
        return ProviderEvaluation.builder()
                .value(value)
                .variant(valueProviderEvaluation.getVariant())
                .errorCode(valueProviderEvaluation.getErrorCode())
                .reason(valueProviderEvaluation.getReason())
                .flagMetadata(valueProviderEvaluation.getFlagMetadata())
                .build();
    }

    private static Double getDoubleValue(ProviderEvaluation valueProviderEvaluation, Double defaultValue) {
        String valueStr = valueProviderEvaluation.getValue().asObject().toString();
        try {
            return Double.parseDouble(valueStr);
        } catch (NumberFormatException ex) {
            return defaultValue;
        }
    }

    @Override
    public ProviderEvaluation getObjectEvaluation(String key, Value defaultValue, EvaluationContext ctx) {
        return evaluateVariant(Value.class, key, defaultValue, ctx);
    }

    private  ProviderEvaluation evaluateVariant(Class clazz, String key, Value defaultValue,
            EvaluationContext ctx) {

        Map contextMap = ContextTransformer.transform(ctx);
        EvaluationRequest request = EvaluationRequest.builder().namespaceKey(fliptProviderConfig.getNamespace())
                .flagKey(key).entityId(ctx.getTargetingKey()).context(contextMap).build();

        VariantEvaluationResponse response;
        try {
            response = fliptClient.evaluation().evaluateVariant(request);
        } catch (Exception e) {
            log.error("Error evaluating variant", e);
            throw new GeneralError(e.getMessage());
        }

        if (!response.isMatch()) {
            log.debug("non matching variant for {} : {}", key, response.getReason());
            return ProviderEvaluation.builder()
                    .value(defaultValue)
                    .reason(DEFAULT.name())
                    .build();
        }

        Value value = new Value(response.getVariantKey());
        ImmutableMetadata.ImmutableMetadataBuilder flagMetadataBuilder = ImmutableMetadata.builder();
        if (response.getVariantAttachment() != null && !response.getVariantAttachment().isEmpty()) {
            flagMetadataBuilder.addString("variant-attachment", response.getVariantAttachment());

            if (clazz.isAssignableFrom(Value.class)) {
                value = new Value(response.getVariantAttachment());
            }
        }

        return ProviderEvaluation.builder()
                .value(value)
                .variant(response.getVariantKey())
                .reason(TARGETING_MATCH.name())
                .flagMetadata(flagMetadataBuilder.build())
                .build();
    }

    @Override
    public void shutdown() {
        super.shutdown();
        log.info("shutdown");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy