Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 58 additions & 6 deletions src/main/java/dev/openfeature/javasdk/EvaluationContext.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package dev.openfeature.javasdk;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import java.util.stream.Collectors;
import lombok.*;

import java.time.ZonedDateTime;
Expand All @@ -18,7 +20,7 @@ public class EvaluationContext {
private final Map<String, Boolean> booleanAttributes;
final Map<String, String> jsonAttributes;

EvaluationContext() {
public EvaluationContext() {
objMapper = new ObjectMapper();
this.targetingKey = "";
this.integerAttributes = new HashMap<>();
Expand All @@ -29,8 +31,9 @@ public class EvaluationContext {

// TODO Not sure if I should have sneakythrows or checked exceptions here..
@SneakyThrows
public <T> void addStructureAttribute(String key, T value) {
public <T> EvaluationContext withStructureAttribute(String key, T value) {
jsonAttributes.put(key, objMapper.writeValueAsString(value));
return this;
}

@SneakyThrows
Expand All @@ -39,16 +42,18 @@ public <T> T getStructureAttribute(String key, Class<T> klass) {
return objMapper.readValue(val, klass);
}

public void addStringAttribute(String key, String value) {
public EvaluationContext withStringAttribute(String key, String value) {
stringAttributes.put(key, value);
return this;
}

public String getStringAttribute(String key) {
return stringAttributes.get(key);
}

public void addIntegerAttribute(String key, Integer value) {
public EvaluationContext withIntegerAttribute(String key, Integer value) {
integerAttributes.put(key, value);
return this;
}

public Integer getIntegerAttribute(String key) {
Expand All @@ -59,12 +64,14 @@ public Boolean getBooleanAttribute(String key) {
return booleanAttributes.get(key);
}

public void addBooleanAttribute(String key, Boolean b) {
public EvaluationContext withBooleanAttribute(String key, Boolean b) {
booleanAttributes.put(key, b);
return this;
}

public void addDatetimeAttribute(String key, ZonedDateTime value) {
public EvaluationContext withDatetimeAttribute(String key, ZonedDateTime value) {
this.stringAttributes.put(key, value.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
return this;
}

public ZonedDateTime getDatetimeAttribute(String key) {
Expand Down Expand Up @@ -103,4 +110,49 @@ public static EvaluationContext merge(EvaluationContext ctx1, EvaluationContext

return ec;
}

public EvaluationContext fromMap(Map<String, Object> map) {
EvaluationContext context = new EvaluationContext();
context.integerAttributes.putAll(
map.entrySet()
.stream()
.filter(entry -> entry.getValue() instanceof Integer)
.collect(Collectors.toMap(Map.Entry::getKey, e -> (Integer) e.getValue()))
);
context.stringAttributes.putAll(
map.entrySet()
.stream()
.filter(entry -> entry.getValue() instanceof String)
.collect(Collectors.toMap(Map.Entry::getKey, e -> (String)e.getValue()))
);
context.booleanAttributes.putAll(
map.entrySet()
.stream()
.filter(entry -> entry.getValue() instanceof Boolean)
.collect(Collectors.toMap(Map.Entry::getKey, e -> (Boolean)e.getValue()))
);
// Hmmm, this won't work as we already have a string type.
// I would recommend changing the type to Map<String, JsonNode> from Jackson
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a jackson newbie, but my understanding is that jsonnode's are an immutable json tree structure. That seems fine to me for this.

// context.jsonAttributes.putAll(
// map.entrySet()
// .stream()
// .filter(entry -> entry.getValue() instanceof String)
// .collect(Collectors.toMap(e -> e.getKey(), e -> (String)e.getValue()))
// );

return null;
}

/**
* Converts the Evaluation Context into a standard {@link Map}
*/
public Map<String, Object> toMap() {
Map<String, Object> map = new HashMap<>();
// 🤔 This will fail if two different maps have the same key.
map.putAll(integerAttributes);
map.putAll(stringAttributes);
map.putAll(booleanAttributes);
map.putAll(jsonAttributes);
return ImmutableMap.copyOf(map);
}
}
10 changes: 5 additions & 5 deletions src/test/java/dev/openfeature/javasdk/EvalContextTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ public class EvalContextTest {
@Test void eval_context() {
EvaluationContext ec = new EvaluationContext();

ec.addStringAttribute("str", "test");
ec.withStringAttribute("str", "test");
assertEquals("test", ec.getStringAttribute("str"));

ec.addBooleanAttribute("bool", true);
ec.withBooleanAttribute("bool", true);
assertEquals(true, ec.getBooleanAttribute("bool"));

ec.addIntegerAttribute("int", 4);
ec.withIntegerAttribute("int", 4);
assertEquals(4, ec.getIntegerAttribute("int"));

ZonedDateTime dt = ZonedDateTime.now();
ec.addDatetimeAttribute("dt", dt);
ec.withDatetimeAttribute("dt", dt);
assertEquals(dt, ec.getDatetimeAttribute("dt"));
}

Expand All @@ -47,7 +47,7 @@ public class EvalContextTest {
n2.left = n1;

EvaluationContext ec = new EvaluationContext();
ec.addStructureAttribute("obj", n2);
ec.withStructureAttribute("obj", n2);


String stringyObject = ec.jsonAttributes.get("obj");
Expand Down
6 changes: 3 additions & 3 deletions src/test/java/dev/openfeature/javasdk/HookSpecTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -429,11 +429,11 @@ public void finallyAfter(HookContext<Boolean> ctx, Map<String, Object> hints) {
@Specification(number="4.3.4", text="When before hooks have finished executing, any resulting evaluation context MUST be merged with the invocation evaluation context with the invocation evaluation context taking precedence in the case of any conflicts.")
@Test void mergeHappensCorrectly() {
EvaluationContext hookCtx = new EvaluationContext();
hookCtx.addStringAttribute("test", "broken");
hookCtx.addStringAttribute("another", "exists");
hookCtx.withStringAttribute("test", "broken");
hookCtx.withStringAttribute("another", "exists");

EvaluationContext invocationCtx = new EvaluationContext();
invocationCtx.addStringAttribute("test", "works");
invocationCtx.withStringAttribute("test", "works");

Hook<Boolean> hook = mockBooleanHook();
when(hook.before(any(), any())).thenReturn(Optional.of(hookCtx));
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/dev/openfeature/javasdk/HookSupportTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class HookSupportTest implements HookFixtures {
@DisplayName("should merge EvaluationContexts on before hooks correctly")
void shouldMergeEvaluationContextsOnBeforeHooksCorrectly() {
EvaluationContext baseContext = new EvaluationContext();
baseContext.addStringAttribute("baseKey", "baseValue");
baseContext.withStringAttribute("baseKey", "baseValue");
HookContext<String> hookContext = new HookContext<>("flagKey", FlagValueType.STRING, "defaultValue", baseContext, () -> "client", () -> "provider");
Hook<String> hook1 = mockStringHook();
Hook<String> hook2 = mockStringHook();
Expand Down Expand Up @@ -69,7 +69,7 @@ private Object createDefaultValue(FlagValueType flagValueType) {

private EvaluationContext evaluationContextWithValue(String key, String value) {
EvaluationContext result = new EvaluationContext();
result.addStringAttribute(key, value);
result.withStringAttribute(key, value);
return result;
}

Expand Down