cloud.eppo.ufc.dto.adapters.FlagConfigResponseAdapter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sdk-common-jvm Show documentation
Show all versions of sdk-common-jvm Show documentation
Eppo SDK for JVM shared library
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;
}
}