ServerToggleBuilder.java

package io.featureprobe.api.builder;

import io.featureprobe.api.base.exception.ServerToggleBuildException;
import io.featureprobe.api.base.model.TargetingContent;
import io.featureprobe.api.base.model.Variation;
import io.featureprobe.api.base.util.DateTimeTranslateUtil;
import io.featureprobe.api.base.util.JsonMapper;
import io.featureprobe.api.dao.entity.Segment;
import com.featureprobe.sdk.server.model.Condition;
import com.featureprobe.sdk.server.model.ConditionType;
import com.featureprobe.sdk.server.model.Prerequisite;
import com.featureprobe.sdk.server.model.Rule;
import com.featureprobe.sdk.server.model.Toggle;
import com.google.common.collect.Maps;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

public class ServerToggleBuilder {

    private static final String DATETIME_FORMAT_PATTERN = "yyyy-MM-dd'T'HH:mm:ssXXX";

    private Toggle toggle;
    private Variation.ValueConverter variationValueConverter;
    private TargetingContent targetingContent;
    private Map<String, Segment> segments;

    private static final Map<String, Variation.ValueConverter<?>> converters = Maps.newHashMap();


    static {
        converters.put("string", value -> value);
        converters.put("boolean", value -> Boolean.valueOf(value));
        converters.put("json", value -> JsonMapper.toObject(value, Map.class));
        converters.put("number", value -> new BigDecimal(value));
    }

    public ServerToggleBuilder builder() {
        this.toggle = new Toggle();
        return this;
    }

    public ServerToggleBuilder trackAccessEvents(boolean trackAccessEvents) {
        toggle.setTrackAccessEvents(trackAccessEvents);
        return this;
    }

    public ServerToggleBuilder lastModified(Date lastModified) {
        toggle.setLastModified(Objects.isNull(lastModified) ? null : lastModified.getTime());
        return this;
    }

    public ServerToggleBuilder key(String key) {
        toggle.setKey(key);
        return this;
    }

    public ServerToggleBuilder version(Long version) {
        toggle.setVersion(version);
        return this;
    }

    public ServerToggleBuilder disabled(boolean disabled) {
        toggle.setEnabled(!disabled);
        return this;
    }


    public ServerToggleBuilder forClient(Boolean forClient) {
        toggle.setForClient(forClient);
        return this;
    }

    public ServerToggleBuilder rules(String rules) {
        this.targetingContent = JsonMapper.toObject(rules, TargetingContent.class);
        return this;
    }

    public ServerToggleBuilder segments(Map<String, Segment> segments) {
        this.segments = segments;
        return this;
    }

    public ServerToggleBuilder returnType(String returnType) {
        this.variationValueConverter = getReturnTypeConverter(returnType);
        return this;
    }

    private Variation.ValueConverter<?> getReturnTypeConverter(String type) {
        if (converters.containsKey(type.toLowerCase())) {
            return converters.get(type.toLowerCase());
        }
        throw new ServerToggleBuildException("return type is unknown:" + type);
    }

    public Toggle build() {
        this.setDisabledServe();
        this.setDefaultServe();
        this.setVariations();
        this.setPrerequisite();
        this.setRules();
        return toggle;
    }

    private ServerToggleBuilder setDisabledServe() {
        if (targetingContent.getDisabledServe() == null) {
            throw new ServerToggleBuildException("disabled serve is null");
        }
        toggle.setDisabledServe(targetingContent.getDisabledServe().toServe());
        return this;
    }

    private void setDefaultServe() {
        if (targetingContent.getDefaultServe() != null) {
            toggle.setDefaultServe(targetingContent.getDefaultServe().toServe());
        }
        if (toggle.getDefaultServe() == null
                && BooleanUtils.isFalse(toggle.getEnabled())
                && targetingContent.getDisabledServe() != null) {
            toggle.setDefaultServe(targetingContent.getDisabledServe().toServe());
        }
        if (toggle.getDefaultServe() == null) {
            throw new ServerToggleBuildException("default serve is null");
        }
    }

    private void setVariations() {
        if (this.variationValueConverter == null) {
            throw new ServerToggleBuildException("return type not set");
        }
        List<Object> variations = targetingContent.getVariationObjectsByConverter(this.variationValueConverter);
        toggle.setVariations(variations);
    }

    private void setPrerequisite() {
        if (this.variationValueConverter == null) {
            throw new ServerToggleBuildException("return type not set");
        }
        List<Prerequisite> prerequisites = targetingContent
                .getPrerequisiteByConverter(converters);
        toggle.setPrerequisites(prerequisites);
    }

    private void setRules() {
        if (CollectionUtils.isEmpty(targetingContent.getRules())) {
            toggle.setRules(Collections.emptyList());
            return;
        }
        List<Rule> rules = targetingContent.getRules().stream().map(rule ->
                rule.toRule()).collect(Collectors.toList());
        rules.forEach(rule -> rule.getConditions().forEach(condition -> {
            if (condition.getType() == ConditionType.SEGMENT) {
                replaceSegmentKeyToUniqueKey(condition);
            } else if (condition.getType() == ConditionType.DATETIME) {
                convertDatetimeToUnix(condition);
            }
        }));
        toggle.setRules(rules);
    }

    private void convertDatetimeToUnix(Condition condition) {
        condition.setObjects(condition.getObjects().stream().map(datetime ->
                DateTimeTranslateUtil.translateUnix(datetime, DATETIME_FORMAT_PATTERN)).collect(Collectors.toList()));
    }

    private void replaceSegmentKeyToUniqueKey(Condition condition) {
        condition.setObjects(condition.getObjects().stream().map(segmentKey ->
                segments.get(segmentKey).getUniqueKey()).collect(Collectors.toList()));
    }

}