Support for arbitrary classes in EvaluationContext
This commit is contained in:
parent
c9fae1b868
commit
9a8beadfd6
|
|
@ -26,6 +26,8 @@ repositories {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.slf4j:slf4j-log4j12:1.7.29'
|
implementation 'org.slf4j:slf4j-log4j12:1.7.29'
|
||||||
|
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
|
||||||
|
|
||||||
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
|
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
|
||||||
implementation 'com.google.guava:guava:30.1.1-jre'
|
implementation 'com.google.guava:guava:30.1.1-jre'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
package dev.openfeature.javasdk;
|
package dev.openfeature.javasdk;
|
||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.Getter;
|
import lombok.*;
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.ToString;
|
|
||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
@ -13,14 +11,32 @@ import java.util.Map;
|
||||||
@ToString @EqualsAndHashCode
|
@ToString @EqualsAndHashCode
|
||||||
@SuppressWarnings("PMD.BeanMembersShouldSerialize")
|
@SuppressWarnings("PMD.BeanMembersShouldSerialize")
|
||||||
public class EvaluationContext {
|
public class EvaluationContext {
|
||||||
|
@EqualsAndHashCode.Exclude private final ObjectMapper objMapper;
|
||||||
@Setter @Getter private String targetingKey;
|
@Setter @Getter private String targetingKey;
|
||||||
private final Map<String, Integer> integerAttributes;
|
private final Map<String, Integer> integerAttributes;
|
||||||
private final Map<String, String> stringAttributes;
|
private final Map<String, String> stringAttributes;
|
||||||
|
private final Map<String, Boolean> booleanAttributes;
|
||||||
|
final Map<String, String> jsonAttributes;
|
||||||
|
|
||||||
EvaluationContext() {
|
EvaluationContext() {
|
||||||
|
objMapper = new ObjectMapper();
|
||||||
this.targetingKey = "";
|
this.targetingKey = "";
|
||||||
this.integerAttributes = new HashMap<>();
|
this.integerAttributes = new HashMap<>();
|
||||||
this.stringAttributes = new HashMap<>();
|
this.stringAttributes = new HashMap<>();
|
||||||
|
booleanAttributes = new HashMap<>();
|
||||||
|
jsonAttributes = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Not sure if I should have sneakythrows or checked exceptions here..
|
||||||
|
@SneakyThrows
|
||||||
|
public <T> void addStructureAttribute(String key, T value) {
|
||||||
|
jsonAttributes.put(key, objMapper.writeValueAsString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public <T> T getStructureAttribute(String key, Class<T> klass) {
|
||||||
|
String val = jsonAttributes.get(key);
|
||||||
|
return objMapper.readValue(val, klass);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addStringAttribute(String key, String value) {
|
public void addStringAttribute(String key, String value) {
|
||||||
|
|
@ -40,19 +56,17 @@ public class EvaluationContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getBooleanAttribute(String key) {
|
public Boolean getBooleanAttribute(String key) {
|
||||||
return Boolean.valueOf(stringAttributes.get(key));
|
return booleanAttributes.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addBooleanAttribute(String key, Boolean b) {
|
public void addBooleanAttribute(String key, Boolean b) {
|
||||||
stringAttributes.put(key, b.toString());
|
booleanAttributes.put(key, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDatetimeAttribute(String key, ZonedDateTime value) {
|
public void addDatetimeAttribute(String key, ZonedDateTime value) {
|
||||||
this.stringAttributes.put(key, value.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
|
this.stringAttributes.put(key, value.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: addStructure or similar.
|
|
||||||
|
|
||||||
public ZonedDateTime getDatetimeAttribute(String key) {
|
public ZonedDateTime getDatetimeAttribute(String key) {
|
||||||
String attr = this.stringAttributes.get(key);
|
String attr = this.stringAttributes.get(key);
|
||||||
if (attr == null) {
|
if (attr == null) {
|
||||||
|
|
@ -66,21 +80,19 @@ public class EvaluationContext {
|
||||||
*/
|
*/
|
||||||
public static EvaluationContext merge(EvaluationContext ctx1, EvaluationContext ctx2) {
|
public static EvaluationContext merge(EvaluationContext ctx1, EvaluationContext ctx2) {
|
||||||
EvaluationContext ec = new EvaluationContext();
|
EvaluationContext ec = new EvaluationContext();
|
||||||
for (Map.Entry<String, Integer> e : ctx1.integerAttributes.entrySet()) {
|
|
||||||
ec.addIntegerAttribute(e.getKey(), e.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map.Entry<String, Integer> e : ctx2.integerAttributes.entrySet()) {
|
ec.stringAttributes.putAll(ctx1.stringAttributes);
|
||||||
ec.addIntegerAttribute(e.getKey(), e.getValue());
|
ec.stringAttributes.putAll(ctx2.stringAttributes);
|
||||||
}
|
|
||||||
|
|
||||||
for (Map.Entry<String, String> e : ctx1.stringAttributes.entrySet()) {
|
ec.integerAttributes.putAll(ctx1.integerAttributes);
|
||||||
ec.addStringAttribute(e.getKey(), e.getValue());
|
ec.integerAttributes.putAll(ctx2.integerAttributes);
|
||||||
}
|
|
||||||
|
ec.booleanAttributes.putAll(ctx1.booleanAttributes);
|
||||||
|
ec.booleanAttributes.putAll(ctx2.booleanAttributes);
|
||||||
|
|
||||||
|
ec.jsonAttributes.putAll(ctx1.jsonAttributes);
|
||||||
|
ec.jsonAttributes.putAll(ctx2.jsonAttributes);
|
||||||
|
|
||||||
for (Map.Entry<String, String> e : ctx2.stringAttributes.entrySet()) {
|
|
||||||
ec.addStringAttribute(e.getKey(), e.getValue());
|
|
||||||
}
|
|
||||||
if (ctx1.getTargetingKey() != null) {
|
if (ctx1.getTargetingKey() != null) {
|
||||||
ec.setTargetingKey(ctx1.getTargetingKey());
|
ec.setTargetingKey(ctx1.getTargetingKey());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package dev.openfeature.javasdk;
|
package dev.openfeature.javasdk;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
|
@ -40,6 +39,24 @@ public class EvalContextTests {
|
||||||
@Specification(spec="Evaluation Context", number="3.2", text="The evaluation context MUST support the inclusion of " +
|
@Specification(spec="Evaluation Context", number="3.2", text="The evaluation context MUST support the inclusion of " +
|
||||||
"custom fields, having keys of type `string`, and " +
|
"custom fields, having keys of type `string`, and " +
|
||||||
"values of type `boolean | string | number | datetime | structure`.")
|
"values of type `boolean | string | number | datetime | structure`.")
|
||||||
@Disabled("Structure support")
|
@Test void eval_context__structure() {
|
||||||
@Test void eval_context__structure() {}
|
Node<Integer> n1 = new Node<>();
|
||||||
|
n1.value = 4;
|
||||||
|
Node<Integer> n2 = new Node<>();
|
||||||
|
n2.value = 2;
|
||||||
|
n2.left = n1;
|
||||||
|
|
||||||
|
EvaluationContext ec = new EvaluationContext();
|
||||||
|
ec.addStructureAttribute("obj", n2);
|
||||||
|
|
||||||
|
|
||||||
|
String stringyObject = ec.jsonAttributes.get("obj");
|
||||||
|
assertEquals("{\"left\":{\"left\":null,\"right\":null,\"value\":4},\"right\":null,\"value\":2}", stringyObject);
|
||||||
|
|
||||||
|
Node nodeFromString = ec.getStructureAttribute("obj", Node.class);
|
||||||
|
assertEquals(n2, nodeFromString);
|
||||||
|
assertEquals(n1, nodeFromString.left);
|
||||||
|
assertEquals(2, nodeFromString.value);
|
||||||
|
assertEquals(4, nodeFromString.left.value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package dev.openfeature.javasdk;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
/**
|
||||||
|
* This is only for testing that object serialization works with both generics and nested classes.
|
||||||
|
*/
|
||||||
|
class Node<T> {
|
||||||
|
Node<T> left, right;
|
||||||
|
T value;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue