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

templates.evaluatorHeader.mustache Maven / Gradle / Ivy

The newest version!
/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*         http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package {{packageName}};

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.gravitee.common.http.HttpHeader;
import io.gravitee.gateway.reactive.api.ExecutionFailure;
import io.gravitee.gateway.reactive.api.context.DeploymentContext;
import io.gravitee.gateway.reactive.api.context.base.BaseExecutionContext;
import io.gravitee.gateway.reactive.api.context.http.HttpPlainExecutionContext;
import io.gravitee.secrets.api.el.FieldKind;
import io.gravitee.secrets.api.el.SecretFieldAccessControl;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Maybe;
import io.reactivex.rxjava3.core.Single;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import org.hibernate.validator.HibernateValidator;
import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;

public class {{evaluatorSimpleClassName}} {

    private static final String FAILURE_CONFIGURATION_INVALID = "FAILURE_CONFIGURATION_INVALID";

    private final Logger logger = LoggerFactory.getLogger({{evaluatorSimpleClassName}}.class);

    private final ObjectMapper objectMapper = new ObjectMapper();

    private final {{simpleClassName}} configuration;

    private static final Validator validator;

    private final String attributePrefix = "{{attributePrefix}}";

    private final String internalId;

    static {
        ValidatorFactory factory = Validation.byProvider(HibernateValidator.class).configure()
            .messageInterpolator(new ParameterMessageInterpolator()).buildValidatorFactory();
        validator = factory.getValidator();
    }

    public {{evaluatorSimpleClassName}} (
        {{simpleClassName}} configuration)
    {
        this.configuration = configuration;
        this.internalId = UUID.randomUUID().toString();
    }

    // Utility methods
    private String buildAttributeName(String attributePrefix, String name) {
        return attributePrefix.concat(".").concat(name);
    }

    private String buildFieldName(String attributeName) {
        return attributeName.substring(this.attributePrefix.length()+1);
    }

    private Maybe evalStringProperty(String name, String value, String attributePrefix, BaseExecutionContext ctx, String secretKind) {
        //First check attributes
        String attributeName = buildAttributeName(attributePrefix, name);
        String attribute = ctx.getAttribute(attributeName);
        if (attribute != null) {
            //If a value is found for this attribute, override the original value with it
            value = attribute;
        }
        //If value is null, return empty
        if(value == null) {
            return Maybe.empty();
        }

        SecretFieldAccessControl accessControl;
        if(secretKind.isEmpty()) {
            accessControl = new SecretFieldAccessControl(false, null, null);
        } else {
            accessControl = new SecretFieldAccessControl(true, FieldKind.valueOf(secretKind), buildFieldName(attributeName));
        }

        //Then check EL
        String finalValue = value;
        return ctx
            .getTemplateEngine()
            .eval(value, String.class)
            .doOnSubscribe(d -> ctx.getTemplateEngine().getTemplateContext().setVariable(SecretFieldAccessControl.EL_VARIABLE, accessControl))
            .doOnTerminate(() -> ctx.getTemplateEngine().getTemplateContext().setVariable(SecretFieldAccessControl.EL_VARIABLE, null))
            .doOnError(throwable -> logger.error("Unable to evaluate property [{}] with expression [{}].", name, finalValue));
    }

    private Maybe evalStringProperty(String name, String value, String attributePrefix, DeploymentContext ctx, String secretKind) {
        // If value is null, return empty
        if(value == null) {
            return Maybe.empty();
        }

        SecretFieldAccessControl accessControl;
        String property = buildFieldName(buildAttributeName(attributePrefix, name));
        if(secretKind.isEmpty()) {
            accessControl = new SecretFieldAccessControl(false, null, null);
        } else {
            accessControl = new SecretFieldAccessControl(true, FieldKind.valueOf(secretKind), property);
        }

        //Then check EL
        return ctx
            .getTemplateEngine()
            .eval(value, String.class)
            .doOnSubscribe(d -> ctx.getTemplateEngine().getTemplateContext().setVariable(SecretFieldAccessControl.EL_VARIABLE, accessControl))
            .doOnTerminate(() -> ctx.getTemplateEngine().getTemplateContext().setVariable(SecretFieldAccessControl.EL_VARIABLE, null))
            .doOnError(throwable -> logger.error("Unable to evaluate property [{}] with expression [{}].", property, value));
    }

    private > T evalEnumProperty(String name, T value, Class enumClass, String attributePrefix, BaseExecutionContext ctx) {
        String attribute = ctx.getAttribute(buildAttributeName(attributePrefix, name));
        if (attribute != null) {
            return T.valueOf(enumClass, attribute);
        }
        return value;
    }

    private Integer evalIntegerProperty(String name, Integer value, String attributePrefix, BaseExecutionContext ctx) {
        Integer attribute = ctx.getAttribute(buildAttributeName(attributePrefix, name));
        if (attribute != null) {
            return attribute;
        }
        return value;
    }

    private Long evalLongProperty(String name, Long value, String attributePrefix, BaseExecutionContext ctx) {
        Long attribute = ctx.getAttribute(buildAttributeName(attributePrefix, name));
        if (attribute != null) {
            return attribute;
        }
        return value;
    }

    private Boolean evalBooleanProperty(String name, Boolean value, String attributePrefix, BaseExecutionContext ctx) {
        Boolean attribute = ctx.getAttribute(buildAttributeName(attributePrefix, name));
        if (attribute != null) {
            return attribute;
        }
        return value;
    }

    private  Set evalSetProperty(String name, Set value, String attributePrefix, BaseExecutionContext ctx) {
        //Try to get a Set and if it fails try to get a String and split it
        try {
            Set attribute = ctx.getAttribute(buildAttributeName(attributePrefix, name));
            if (attribute != null) {
                return attribute;
            }
        } catch (ClassCastException cce) {
            List attribute = ctx.getAttributeAsList(buildAttributeName(attributePrefix, name));
            if(attribute != null) {
                return Set.copyOf(attribute);
            }
        }

        return value;
    }

    private  List evalListProperty(String name, List value, String attributePrefix, BaseExecutionContext ctx) {
        List attribute = ctx.getAttributeAsList(buildAttributeName(attributePrefix, name));
        if (attribute != null) {
            return attribute;
        }

        return value;
    }

    private Maybe> evalListStringProperty(String name, List value, String attributePrefix, BaseExecutionContext ctx) {
        List attribute = ctx.getAttributeAsList(buildAttributeName(attributePrefix, name));
        if (attribute != null) {
            //If a value is found for this attribute, override the original value with it
            value = attribute;
        }
        //If value is null, return empty
        if(value == null) {
            return Maybe.empty();
        }

        //Then check EL
        return Flowable.fromIterable(value).filter(Objects::nonNull)
            .flatMapMaybe(v -> ctx
                .getTemplateEngine()
                .eval(v, String.class)
                .doOnError(throwable -> logger.error("Unable to evaluate property [{}] with expression [{}].", name, v)))
            .toList()
            .toMaybe();
    }

    private Maybe> evalListStringProperty(String name, List value, String attributePrefix, DeploymentContext ctx) {
        //If value is null, return empty
        if(value == null) {
            return Maybe.empty();
        }

        //Then check EL
        return Flowable.fromIterable(value).filter(Objects::nonNull)
            .flatMapMaybe(v -> ctx
                .getTemplateEngine()
                .eval(v, String.class)
                .doOnError(throwable -> logger.error("Unable to evaluate property [{}] with expression [{}].", name, v)))
            .toList()
            .toMaybe();
    }

    private Maybe> evalListHeaderProperty(
        String name,
        List headers,
        String attributePrefix,
        BaseExecutionContext ctx
    ) {
        //First check attributes
        String attributeName = buildAttributeName(attributePrefix, name);
        List attribute = ctx.getAttribute(attributeName);
        if (attribute != null) {
            //If a value is found for this attribute, override the original value with it
            headers = attribute;
        }
        //If headers is null, return empty
        if (headers == null) {
            return Maybe.empty();
        }

        //Then check EL
        return Flowable
            .fromIterable(headers)
            .filter(Objects::nonNull)
            .flatMapMaybe(header -> {
                SecretFieldAccessControl accessControl = new SecretFieldAccessControl(true, FieldKind.HEADER, header.getName());
                return ctx
                    .getTemplateEngine()
                    .eval(header.getValue(), String.class)
                    .doOnSubscribe(d ->
                        ctx.getTemplateEngine().getTemplateContext().setVariable(SecretFieldAccessControl.EL_VARIABLE, accessControl)
                    )
                    .doOnTerminate(() ->
                        ctx.getTemplateEngine().getTemplateContext().setVariable(SecretFieldAccessControl.EL_VARIABLE, null)
                    )
                    .map(evaluatedValue -> new HttpHeader(header.getName(), evaluatedValue))
                    .doOnError(throwable -> logger.error("Unable to evaluate property [{}] with expression [{}].", name, header.getValue())
                );
            })
            .toList()
            .toMaybe();
        }

    private Maybe> evalListHeaderProperty(
        String name,
        List headers,
        String attributePrefix,
        DeploymentContext ctx
    ) {
        //If headers is null, return empty
        if (headers == null) {
            return Maybe.empty();
        }

        //Then check EL
        return Flowable
            .fromIterable(headers)
            .filter(Objects::nonNull)
            .flatMapMaybe(header -> {
                SecretFieldAccessControl accessControl = new SecretFieldAccessControl(true, FieldKind.HEADER, header.getName());
                return ctx
                    .getTemplateEngine()
                    .eval(header.getValue(), String.class)
                    .doOnSubscribe(d ->
                        ctx.getTemplateEngine().getTemplateContext().setVariable(SecretFieldAccessControl.EL_VARIABLE, accessControl)
                    )
                    .doOnTerminate(() ->
                        ctx.getTemplateEngine().getTemplateContext().setVariable(SecretFieldAccessControl.EL_VARIABLE, null)
                    )
                    .map(evaluatedValue -> new HttpHeader(header.getName(), evaluatedValue))
                    .doOnError(throwable -> logger.error("Unable to evaluate property [{}] with expression [{}].", name, header.getValue())
                    );
            })
            .toList()
            .toMaybe();
    }

    private  void validateConfiguration(T configuration) {
        Set> constraintViolations = validator.validate(configuration);

        if (!constraintViolations.isEmpty()) {
            StringBuilder exceptionMessage = new StringBuilder(constraintViolations.size() + " constraint violations found : ");
            constraintViolations.forEach(violation ->
                exceptionMessage
                    .append("[attribute[")
                    .append(violation.getPropertyPath())
                    .append("] reason[")
                    .append(violation.getMessage())
                    .append("]]")
            );

            //LOG
            logger.error(exceptionMessage.toString());

            //Throw exception with info
            throw new IllegalStateException(exceptionMessage.toString());
        }
    }

    /**
    * Blocking eval method (see {@link #eval(BaseExecutionContext)} Eval})
    * Caution when using this method if the evaluation involves EL expressions that trigger blocking fetches
    * @param ctx the current execution context
    * @return configuration with all dynamic configuration parameters updated
    */
    public {{simpleClassName}} evalNow(BaseExecutionContext ctx) {
        return eval(ctx).blockingGet();
    }

    /**
    * Blocking eval method (see {@link #eval(DeploymentContext)} Eval})
    * Caution when using this method if the evaluation involves EL expressions that trigger blocking fetches
    * @param ctx the current deployment context
    * @return configuration with all dynamic configuration parameters updated
    */
    public {{simpleClassName}} evalNow(DeploymentContext ctx) {
        return eval(ctx).blockingGet();
    }

    /**
    * Evaluates the configuration using the execution context to update parameters using attributes or EL
    * and stores it as an internal attributes to avoid multiple evaluation
    * @param ctx the current context
    * @return configuration with all dynamic configuration parameters updated
    */
    public Single<{{simpleClassName}}> eval(BaseExecutionContext ctx) {
        return eval(ctx, null);
    }

    /**
    * Evaluates the configuration using a deployment context to update parameters using EL
    * and stores it as an internal attributes to avoid multiple evaluation
    * @param ctx the current deployment context
    * @return configuration with all dynamic configuration parameters updated
    */
    public Single<{{simpleClassName}}> eval(DeploymentContext ctx) {
        return eval(null, ctx);
    }

    private Single<{{simpleClassName}}> eval(BaseExecutionContext baseExecutionContext, DeploymentContext deploymentContext) {

        if(baseExecutionContext != null) {
            if (attributePrefix.isEmpty()) {
                return Single.error(new IllegalArgumentException("@ConfigurationEvaluator(attributePrefix=\"...\") is required when using BaseExecutionContext."));
            }
            //First check if the configuration has not been already evaluated
            {{simpleClassName}} evaluatedConf = baseExecutionContext.getInternalAttribute("{{evaluatedConfigurationName}}-"+this.internalId);
            if(evaluatedConf != null) {
                return Single.just(evaluatedConf);
            }
        }

        {{simpleClassName}} evaluatedConfiguration;
        try {
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            evaluatedConfiguration = objectMapper.readValue(objectMapper.writeValueAsString(configuration), {{simpleClassName}}.class);
        } catch (com.fasterxml.jackson.core.JsonProcessingException e) {
            logger.error("Unable to clone configuration", e);
            return Single.error(e);
        }

        String currentAttributePrefix = attributePrefix;

        List> toEval = new ArrayList<>();
        List>> toEvalList = new ArrayList<>();
        List>> toEvalHeaderList = new ArrayList<>();




© 2015 - 2025 Weber Informatics LLC | Privacy Policy