Skip to content
This repository has been archived by the owner on May 30, 2024. It is now read-only.

Commit

Permalink
Merge pull request #138 from launchdarkly/eb/ch49042/value-type
Browse files Browse the repository at this point in the history
add LDValue type and deprecate use of JsonElement
  • Loading branch information
eli-darkly authored Oct 7, 2019
2 parents da4dde5 + 41d52f0 commit c1c6c32
Show file tree
Hide file tree
Showing 48 changed files with 2,694 additions and 740 deletions.
45 changes: 21 additions & 24 deletions src/main/java/com/launchdarkly/client/Clause.java
Original file line number Diff line number Diff line change
@@ -1,66 +1,65 @@
package com.launchdarkly.client;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.launchdarkly.client.value.LDValue;
import com.launchdarkly.client.value.LDValueType;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static com.launchdarkly.client.VersionedDataKind.SEGMENTS;

import java.util.List;

import static com.launchdarkly.client.VersionedDataKind.SEGMENTS;

class Clause {
private final static Logger logger = LoggerFactory.getLogger(Clause.class);

private String attribute;
private Operator op;
private List<JsonPrimitive> values; //interpreted as an OR of values
private List<LDValue> values; //interpreted as an OR of values
private boolean negate;

public Clause() {
}

public Clause(String attribute, Operator op, List<JsonPrimitive> values, boolean negate) {
public Clause(String attribute, Operator op, List<LDValue> values, boolean negate) {
this.attribute = attribute;
this.op = op;
this.values = values;
this.negate = negate;
}

boolean matchesUserNoSegments(LDUser user) {
JsonElement userValue = user.getValueForEvaluation(attribute);
if (userValue == null) {
LDValue userValue = user.getValueForEvaluation(attribute);
if (userValue.isNull()) {
return false;
}

if (userValue.isJsonArray()) {
JsonArray array = userValue.getAsJsonArray();
for (JsonElement jsonElement : array) {
if (!jsonElement.isJsonPrimitive()) {
logger.error("Invalid custom attribute value in user object for user key \"{}\": {}", user.getKey(), jsonElement);
if (userValue.getType() == LDValueType.ARRAY) {
for (LDValue value: userValue.values()) {
if (value.getType() == LDValueType.ARRAY || value.getType() == LDValueType.OBJECT) {
logger.error("Invalid custom attribute value in user object for user key \"{}\": {}", user.getKey(), value);
return false;
}
if (matchAny(jsonElement.getAsJsonPrimitive())) {
if (matchAny(value)) {
return maybeNegate(true);
}
}
return maybeNegate(false);
} else if (userValue.isJsonPrimitive()) {
return maybeNegate(matchAny(userValue.getAsJsonPrimitive()));
} else if (userValue.getType() != LDValueType.OBJECT) {
return maybeNegate(matchAny(userValue));
}
logger.warn("Got unexpected user attribute type \"{}\" for user key \"{}\" and attribute \"{}\"",
userValue.getClass().getName(), user.getKey(), attribute);
userValue.getType(), user.getKey(), attribute);
return false;
}

boolean matchesUser(FeatureStore store, LDUser user) {
// In the case of a segment match operator, we check if the user is in any of the segments,
// and possibly negate
if (op == Operator.segmentMatch) {
for (JsonPrimitive j: values) {
for (LDValue j: values) {
if (j.isString()) {
Segment segment = store.get(SEGMENTS, j.getAsString());
Segment segment = store.get(SEGMENTS, j.stringValue());
if (segment != null) {
if (segment.matchesUser(user)) {
return maybeNegate(true);
Expand All @@ -74,9 +73,9 @@ boolean matchesUser(FeatureStore store, LDUser user) {
return matchesUserNoSegments(user);
}

private boolean matchAny(JsonPrimitive userValue) {
private boolean matchAny(LDValue userValue) {
if (op != null) {
for (JsonPrimitive v : values) {
for (LDValue v : values) {
if (op.apply(userValue, v)) {
return true;
}
Expand All @@ -91,6 +90,4 @@ private boolean maybeNegate(boolean b) {
else
return b;
}


}
32 changes: 27 additions & 5 deletions src/main/java/com/launchdarkly/client/EvaluationDetail.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,48 @@
package com.launchdarkly.client;

import com.google.common.base.Objects;
import com.launchdarkly.client.value.LDValue;

/**
* An object returned by the "variation detail" methods such as {@link LDClientInterface#boolVariationDetail(String, LDUser, boolean)},
* combining the result of a flag evaluation with an explanation of how it was calculated.
* @param <T> the type of the wrapped value
* @since 4.3.0
*/
public class EvaluationDetail<T> {

private final EvaluationReason reason;
private final Integer variationIndex;
private final T value;


/**
* Constructs an instance with all properties specified.
*
* @param reason an {@link EvaluationReason} (should not be null)
* @param variationIndex an optional variation index
* @param value a value of the desired type
*/
public EvaluationDetail(EvaluationReason reason, Integer variationIndex, T value) {
this.reason = reason;
this.variationIndex = variationIndex;
this.value = value;
this.variationIndex = variationIndex;
this.reason = reason;
}

/**
* Factory method for an arbitrary value.
*
* @param value a value of the desired type
* @param variationIndex an optional variation index
* @param reason an {@link EvaluationReason} (should not be null)
* @return an {@link EvaluationDetail}
* @since 4.8.0
*/
public static <T> EvaluationDetail<T> fromValue(T value, Integer variationIndex, EvaluationReason reason) {
return new EvaluationDetail<T>(reason, variationIndex, value);
}

static <T> EvaluationDetail<T> error(EvaluationReason.ErrorKind errorKind, T defaultValue) {
return new EvaluationDetail<>(EvaluationReason.error(errorKind), null, defaultValue);
static EvaluationDetail<LDValue> error(EvaluationReason.ErrorKind errorKind, LDValue defaultValue) {
return new EvaluationDetail<LDValue>(EvaluationReason.error(errorKind), null, LDValue.normalize(defaultValue));
}

/**
Expand Down
44 changes: 30 additions & 14 deletions src/main/java/com/launchdarkly/client/Event.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.launchdarkly.client;

import com.google.gson.JsonElement;
import com.launchdarkly.client.value.LDValue;

/**
* Base class for all analytics events that are generated by the client. Also defines all of its own subclasses.
Expand All @@ -16,18 +17,22 @@ public Event(long creationDate, LDUser user) {

public static final class Custom extends Event {
final String key;
final JsonElement data;
final LDValue data;
final Double metricValue;

public Custom(long timestamp, String key, LDUser user, JsonElement data, Double metricValue) {
/**
* @since 4.8.0
*/
public Custom(long timestamp, String key, LDUser user, LDValue data, Double metricValue) {
super(timestamp, user);
this.key = key;
this.data = data;
this.data = data == null ? LDValue.ofNull() : data;
this.metricValue = metricValue;
}

@Deprecated
public Custom(long timestamp, String key, LDUser user, JsonElement data) {
this(timestamp, key, user, data, null);
this(timestamp, key, user, LDValue.unsafeFromJsonElement(data), null);
}
}

Expand All @@ -46,23 +51,20 @@ public Index(long timestamp, LDUser user) {
public static final class FeatureRequest extends Event {
final String key;
final Integer variation;
final JsonElement value;
final JsonElement defaultVal;
final LDValue value;
final LDValue defaultVal;
final Integer version;
final String prereqOf;
final boolean trackEvents;
final Long debugEventsUntilDate;
final EvaluationReason reason;
final boolean debug;

@Deprecated
public FeatureRequest(long timestamp, String key, LDUser user, Integer version, Integer variation, JsonElement value,
JsonElement defaultVal, String prereqOf, boolean trackEvents, Long debugEventsUntilDate, boolean debug) {
this(timestamp, key, user, version, variation, value, defaultVal, prereqOf, trackEvents, debugEventsUntilDate, null, debug);
}

public FeatureRequest(long timestamp, String key, LDUser user, Integer version, Integer variation, JsonElement value,
JsonElement defaultVal, String prereqOf, boolean trackEvents, Long debugEventsUntilDate, EvaluationReason reason, boolean debug) {
/**
* @since 4.8.0
*/
public FeatureRequest(long timestamp, String key, LDUser user, Integer version, Integer variation, LDValue value,
LDValue defaultVal, EvaluationReason reason, String prereqOf, boolean trackEvents, Long debugEventsUntilDate, boolean debug) {
super(timestamp, user);
this.key = key;
this.version = version;
Expand All @@ -75,6 +77,20 @@ public FeatureRequest(long timestamp, String key, LDUser user, Integer version,
this.reason = reason;
this.debug = debug;
}

@Deprecated
public FeatureRequest(long timestamp, String key, LDUser user, Integer version, Integer variation, JsonElement value,
JsonElement defaultVal, String prereqOf, boolean trackEvents, Long debugEventsUntilDate, boolean debug) {
this(timestamp, key, user, version, variation, LDValue.unsafeFromJsonElement(value), LDValue.unsafeFromJsonElement(defaultVal),
null, prereqOf, trackEvents, debugEventsUntilDate, debug);
}

@Deprecated
public FeatureRequest(long timestamp, String key, LDUser user, Integer version, Integer variation, JsonElement value,
JsonElement defaultVal, String prereqOf, boolean trackEvents, Long debugEventsUntilDate, EvaluationReason reason, boolean debug) {
this(timestamp, key, user, version, variation, LDValue.unsafeFromJsonElement(value), LDValue.unsafeFromJsonElement(defaultVal),
reason, prereqOf, trackEvents, debugEventsUntilDate, debug);
}
}

}
30 changes: 15 additions & 15 deletions src/main/java/com/launchdarkly/client/EventFactory.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.launchdarkly.client;

import com.google.gson.JsonElement;
import com.launchdarkly.client.value.LDValue;

abstract class EventFactory {
public static final EventFactory DEFAULT = new DefaultEventFactory(false);
Expand All @@ -9,8 +9,8 @@ abstract class EventFactory {
protected abstract long getTimestamp();
protected abstract boolean isIncludeReasons();

public Event.FeatureRequest newFeatureRequestEvent(FeatureFlag flag, LDUser user, JsonElement value,
Integer variationIndex, EvaluationReason reason, JsonElement defaultValue, String prereqOf) {
public Event.FeatureRequest newFeatureRequestEvent(FeatureFlag flag, LDUser user, LDValue value,
Integer variationIndex, EvaluationReason reason, LDValue defaultValue, String prereqOf) {
boolean requireExperimentData = isExperiment(flag, reason);
return new Event.FeatureRequest(
getTimestamp(),
Expand All @@ -20,46 +20,46 @@ public Event.FeatureRequest newFeatureRequestEvent(FeatureFlag flag, LDUser user
variationIndex,
value,
defaultValue,
(requireExperimentData || isIncludeReasons()) ? reason : null,
prereqOf,
requireExperimentData || flag.isTrackEvents(),
flag.getDebugEventsUntilDate(),
(requireExperimentData || isIncludeReasons()) ? reason : null,
false
);
}

public Event.FeatureRequest newFeatureRequestEvent(FeatureFlag flag, LDUser user, EvaluationDetail<JsonElement> result, JsonElement defaultVal) {
public Event.FeatureRequest newFeatureRequestEvent(FeatureFlag flag, LDUser user, EvaluationDetail<LDValue> result, LDValue defaultVal) {
return newFeatureRequestEvent(flag, user, result == null ? null : result.getValue(),
result == null ? null : result.getVariationIndex(), result == null ? null : result.getReason(),
defaultVal, null);
}

public Event.FeatureRequest newDefaultFeatureRequestEvent(FeatureFlag flag, LDUser user, JsonElement defaultValue,
public Event.FeatureRequest newDefaultFeatureRequestEvent(FeatureFlag flag, LDUser user, LDValue defaultValue,
EvaluationReason.ErrorKind errorKind) {
return new Event.FeatureRequest(getTimestamp(), flag.getKey(), user, flag.getVersion(),
null, defaultValue, defaultValue, null, flag.isTrackEvents(), flag.getDebugEventsUntilDate(),
isIncludeReasons() ? EvaluationReason.error(errorKind) : null, false);
null, defaultValue, defaultValue, isIncludeReasons() ? EvaluationReason.error(errorKind) : null,
null, flag.isTrackEvents(), flag.getDebugEventsUntilDate(), false);
}

public Event.FeatureRequest newUnknownFeatureRequestEvent(String key, LDUser user, JsonElement defaultValue,
public Event.FeatureRequest newUnknownFeatureRequestEvent(String key, LDUser user, LDValue defaultValue,
EvaluationReason.ErrorKind errorKind) {
return new Event.FeatureRequest(getTimestamp(), key, user, null, null, defaultValue, defaultValue, null, false, null,
isIncludeReasons() ? EvaluationReason.error(errorKind) : null, false);
return new Event.FeatureRequest(getTimestamp(), key, user, null, null, defaultValue, defaultValue,
isIncludeReasons() ? EvaluationReason.error(errorKind) : null, null, false, null, false);
}

public Event.FeatureRequest newPrerequisiteFeatureRequestEvent(FeatureFlag prereqFlag, LDUser user, EvaluationDetail<JsonElement> result,
public Event.FeatureRequest newPrerequisiteFeatureRequestEvent(FeatureFlag prereqFlag, LDUser user, EvaluationDetail<LDValue> result,
FeatureFlag prereqOf) {
return newFeatureRequestEvent(prereqFlag, user, result == null ? null : result.getValue(),
result == null ? null : result.getVariationIndex(), result == null ? null : result.getReason(),
null, prereqOf.getKey());
LDValue.ofNull(), prereqOf.getKey());
}

public Event.FeatureRequest newDebugEvent(Event.FeatureRequest from) {
return new Event.FeatureRequest(from.creationDate, from.key, from.user, from.version, from.variation, from.value,
from.defaultVal, from.prereqOf, from.trackEvents, from.debugEventsUntilDate, from.reason, true);
from.defaultVal, from.reason, from.prereqOf, from.trackEvents, from.debugEventsUntilDate, true);
}

public Event.Custom newCustomEvent(String key, LDUser user, JsonElement data, Double metricValue) {
public Event.Custom newCustomEvent(String key, LDUser user, LDValue data, Double metricValue) {
return new Event.Custom(getTimestamp(), key, user, data, metricValue);
}

Expand Down
Loading

0 comments on commit c1c6c32

Please sign in to comment.