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

cloud.eppo.ufc.dto.adapters.FlagConfigResponseAdapter Maven / Gradle / Ivy

There is a newer version: 3.3.2
Show newest version
package cloud.eppo.ufc.dto.adapters;

import static cloud.eppo.Utils.parseUtcISODateElement;

import cloud.eppo.model.ShardRange;
import cloud.eppo.ufc.dto.*;
import com.google.gson.*;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Hand-rolled deserializer so that we don't rely on annotations and method names, which can be
 * unreliable when ProGuard minification is in-use and not configured to protect
 * JSON-deserialization-related classes and annotations.
 */
public class FlagConfigResponseAdapter implements JsonDeserializer {
  private static final Logger log = LoggerFactory.getLogger(FlagConfigResponseAdapter.class);

  private final EppoValueAdapter eppoValueDeserializer = new EppoValueAdapter();

  @Override
  public FlagConfigResponse deserialize(
      JsonElement rootElement, Type type, JsonDeserializationContext context)
      throws JsonParseException {
    Map flags = new ConcurrentHashMap<>();
    FlagConfigResponse configResponse = new FlagConfigResponse();
    configResponse.setFlags(flags); // Default to a response with an empty map of flags

    if (rootElement == null || !rootElement.isJsonObject()) {
      log.warn("no top-level JSON object");
      return configResponse;
    }

    JsonObject rootObject = rootElement.getAsJsonObject();
    JsonElement flagsElement = rootObject.get("flags");
    if (flagsElement == null) {
      log.warn("no root-level flags property");
      return configResponse;
    }

    JsonObject flagsObject = flagsElement.getAsJsonObject();
    for (Map.Entry flagEntry : flagsObject.entrySet()) {
      String flagKey = flagEntry.getKey();
      FlagConfig flagConfig = deserializeFlag(flagEntry.getValue(), type, context);
      flags.put(
          flagKey,
          flagConfig); // Note this is adding to the map already plugged into configResponse
    }

    return configResponse;
  }

  private FlagConfig deserializeFlag(
      JsonElement jsonElement, Type type, JsonDeserializationContext context) {
    JsonObject flagObject = jsonElement.getAsJsonObject();
    String key = flagObject.get("key").getAsString();
    boolean enabled = flagObject.get("enabled").getAsBoolean();
    int totalShards = flagObject.get("totalShards").getAsInt();
    VariationType variationType =
        VariationType.fromString(flagObject.get("variationType").getAsString());
    Map variations =
        deserializeVariations(flagObject.get("variations"), type, context);
    List allocations =
        deserializeAllocations(flagObject.get("allocations"), type, context);

    FlagConfig flagConfig = new FlagConfig();
    flagConfig.setKey(key);
    flagConfig.setEnabled(enabled);
    flagConfig.setTotalShards(totalShards);
    flagConfig.setVariationType(variationType);
    flagConfig.setVariations(variations);
    flagConfig.setAllocations(allocations);
    return flagConfig;
  }

  private Map deserializeVariations(
      JsonElement jsonElement, Type type, JsonDeserializationContext context) {
    Map variations = new HashMap<>();
    if (jsonElement == null) {
      return variations;
    }

    for (Map.Entry variationEntry : jsonElement.getAsJsonObject().entrySet()) {
      JsonObject variationObject = variationEntry.getValue().getAsJsonObject();
      String key = variationObject.get("key").getAsString();
      EppoValue value =
          eppoValueDeserializer.deserialize(variationObject.get("value"), type, context);

      Variation variation = new Variation();
      variation.setKey(key);
      variation.setValue(value);

      variations.put(variationEntry.getKey(), variation);
    }

    return variations;
  }

  private List deserializeAllocations(
      JsonElement jsonElement, Type type, JsonDeserializationContext context) {
    List allocations = new ArrayList<>();
    if (jsonElement == null) {
      return allocations;
    }

    for (JsonElement allocationElement : jsonElement.getAsJsonArray()) {
      JsonObject allocationObject = allocationElement.getAsJsonObject();
      String key = allocationObject.get("key").getAsString();
      Set rules =
          deserializeTargetingRules(allocationObject.get("rules"), type, context);

      Date startAt = parseUtcISODateElement(allocationObject.get("startAt"));
      Date endAt = parseUtcISODateElement(allocationObject.get("endAt"));
      List splits = deserializeSplits(allocationObject.get("splits"));
      boolean doLog = allocationObject.get("doLog").getAsBoolean();
      Allocation allocation = new Allocation(key, rules, startAt, endAt, splits, doLog);
      allocations.add(allocation);
    }

    return allocations;
  }

  private Set deserializeTargetingRules(
      JsonElement jsonElement, Type type, JsonDeserializationContext context) {
    Set targetingRules = new HashSet<>();
    if (jsonElement == null) {
      return targetingRules;
    }

    for (JsonElement ruleElement : jsonElement.getAsJsonArray()) {
      Set conditions = new HashSet<>();

      for (JsonElement conditionElement :
          ruleElement.getAsJsonObject().get("conditions").getAsJsonArray()) {
        JsonObject conditionObject = conditionElement.getAsJsonObject();
        String attribute = conditionObject.get("attribute").getAsString();
        String operatorKey = conditionObject.get("operator").getAsString();
        OperatorType operator = OperatorType.fromString(operatorKey);
        if (operator == null) {
          log.warn("Unknown operator \"" + operatorKey + "\"");
          continue;
        }
        EppoValue value =
            eppoValueDeserializer.deserialize(conditionObject.get("value"), type, context);

        TargetingCondition condition = new TargetingCondition();
        condition.setAttribute(attribute);
        condition.setOperator(operator);
        condition.setValue(value);
        conditions.add(condition);
      }

      TargetingRule targetingRule = new TargetingRule();
      targetingRule.setConditions(conditions);
      targetingRules.add(targetingRule);
    }

    return targetingRules;
  }

  private List deserializeSplits(JsonElement jsonElement) {
    List splits = new ArrayList<>();
    if (jsonElement == null) {
      return splits;
    }

    for (JsonElement splitElement : jsonElement.getAsJsonArray()) {
      JsonObject splitObject = splitElement.getAsJsonObject();
      String variationKey = splitObject.get("variationKey").getAsString();
      Set shards = deserializeShards(splitObject.get("shards"));

      Map extraLogging = new HashMap<>();
      JsonElement extraLoggingElement = splitObject.get("extraLogging");
      if (extraLoggingElement != null) {
        for (Map.Entry extraLoggingEntry :
            extraLoggingElement.getAsJsonObject().entrySet()) {
          extraLogging.put(extraLoggingEntry.getKey(), extraLoggingEntry.getValue().getAsString());
        }
      }

      Split split = new Split();
      split.setVariationKey(variationKey);
      split.setShards(shards);
      split.setExtraLogging(extraLogging);
      splits.add(split);
    }

    return splits;
  }

  private Set deserializeShards(JsonElement jsonElement) {
    Set shards = new HashSet<>();
    if (jsonElement == null) {
      return shards;
    }

    for (JsonElement shardElement : jsonElement.getAsJsonArray()) {
      JsonObject shardObject = shardElement.getAsJsonObject();
      String salt = shardObject.get("salt").getAsString();
      Set ranges = new HashSet<>();
      for (JsonElement rangeElement : shardObject.get("ranges").getAsJsonArray()) {
        JsonObject rangeObject = rangeElement.getAsJsonObject();
        int start = rangeObject.get("start").getAsInt();
        int end = rangeObject.get("end").getAsInt();

        ShardRange range = new ShardRange(start, end);
        ranges.add(range);
      }
      Shard shard = new Shard();
      shard.setSalt(salt);
      shard.setRanges(ranges);
      shards.add(shard);
    }

    return shards;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy