Skip to content

feat: $flagd.timestamp added to in-process evaluator #512

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Nov 9, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,24 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.time.Instant;

import dev.openfeature.sdk.EvaluationContext;
import io.github.jamsesso.jsonlogic.JsonLogic;
import io.github.jamsesso.jsonlogic.JsonLogicException;
import lombok.Getter;


/**
* Targeting operator wraps JsonLogic handlers and expose a simple API for external layers.
* Targeting operator wraps JsonLogic handlers and expose a simple API for
* external layers.
* This helps to isolate external dependencies to this package.
*/
public class Operator {

static final String FLAGD_PROPS_KEY = "$flagd";
static final String FLAG_KEY = "flagKey";
static final String TARGET_KEY = "targetingKey";
static final String TIME_STAMP = "timestamp";

private final JsonLogic jsonLogicHandler;

Expand All @@ -38,8 +40,12 @@ public Operator() {
*/
public Object apply(final String flagKey, final String targetingRule, final EvaluationContext ctx)
throws TargetingRuleException {
final Map<String, String> flagdProperties = new HashMap<>();
final Map<String, Object> flagdProperties = new HashMap<>();
flagdProperties.put(FLAG_KEY, flagKey);

long unixTimestamp = Instant.now().getEpochSecond();
flagdProperties.put(TIME_STAMP, unixTimestamp);

final Map<String, Object> valueMap = ctx.asObjectMap();
valueMap.put(FLAGD_PROPS_KEY, flagdProperties);

Expand All @@ -52,35 +58,37 @@ public Object apply(final String flagKey, final String targetingRule, final Eval

@Getter
static class FlagProperties {
private final String flagKey;
private final Object flagKey;
private final Object timestamp;
private final String targetingKey;

FlagProperties(Object from) {
if (from instanceof Map) {
Map<?, ?> dataMap = (Map<?, ?>) from;

final Object flagKey = Optional.ofNullable(dataMap.get(FLAGD_PROPS_KEY))
.filter(flagdProps -> flagdProps instanceof Map)
.map(flagdProps -> ((Map<?, ?>)flagdProps).get(FLAG_KEY))
.orElse(null);

if (flagKey instanceof String) {
this.flagKey = (String) flagKey;
} else {
this.flagKey = null;
}
this.flagKey = extractSubPropertyFromFlagd(dataMap, FLAG_KEY);
this.timestamp = extractSubPropertyFromFlagd(dataMap, TIME_STAMP);

final Object targetKey = dataMap.get(TARGET_KEY);

if (targetKey instanceof String) {
targetingKey = (String) targetKey;
} else {
targetingKey = null;
}

} else {
flagKey = null;
timestamp = null;
targetingKey = null;
}
}

public static Object extractSubPropertyFromFlagd(Map<?, ?> dataMap, String propertyName) {
return Optional.ofNullable(dataMap.get(FLAGD_PROPS_KEY))
.filter(flagdProps -> flagdProps instanceof Map)
.map(flagdProps -> ((Map<?, ?>) flagdProps).get(propertyName))
.orElse(null);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package dev.openfeature.contrib.providers.flagd.resolver.process.targeting;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.HashMap;
import java.util.Map;
import java.time.Instant;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -33,6 +35,46 @@ void flagKeyPresent() throws TargetingRuleException {
assertEquals(true, evalVariant);
}

@Test
void timestampPresent() throws TargetingRuleException {
// given

// rule asserting $flagd.timestamp is a number (i.e., a Unix timestamp)
final String targetingRule = "{\"var\":[\"$flagd.timestamp\"]}";

// when
Object timestampString = OPERATOR.apply("some-key", targetingRule, new ImmutableContext());

long timestamp = (long) Double.parseDouble(timestampString.toString());

// generating current unix timestamp & 5 minute threshold
long currentTimestamp = Instant.now().getEpochSecond();
long thresholdPast = currentTimestamp - (5);
long thresholdFuture = currentTimestamp + (5);

// checks if the timestamp is within 5 minutes of the current time
assertTrue(timestamp >= thresholdPast && timestamp <= thresholdFuture);
}

@Test
void testExtractSubPropertyFromFlagd() {
// Given
Map<String, Object> flagdProperties = new HashMap<>();
flagdProperties.put(Operator.FLAG_KEY, "some-key");
flagdProperties.put(Operator.TIME_STAMP, 1634000000L); // Changed to long

Map<String, Object> dataMap = new HashMap<>();
dataMap.put(Operator.FLAGD_PROPS_KEY, flagdProperties);

// When
Object flagKey = Operator.FlagProperties.extractSubPropertyFromFlagd(dataMap, Operator.FLAG_KEY);
Object timestamp = Operator.FlagProperties.extractSubPropertyFromFlagd(dataMap, Operator.TIME_STAMP);

// Then
assertEquals("some-key", flagKey);
assertEquals(1634000000L, timestamp); // Changed to long
}

@Test
void fractionalTestA() throws TargetingRuleException {
// given
Expand Down Expand Up @@ -64,7 +106,6 @@ void fractionalTestA() throws TargetingRuleException {
Map<String, Value> ctxData = new HashMap<>();
ctxData.put("email", new Value("[email protected]"));


// when
Object evalVariant = OPERATOR.apply("headerColor", targetingRule, new ImmutableContext(ctxData));

Expand Down Expand Up @@ -103,7 +144,6 @@ void fractionalTestB() throws TargetingRuleException {
Map<String, Value> ctxData = new HashMap<>();
ctxData.put("email", new Value("[email protected]"));


// when
Object evalVariant = OPERATOR.apply("headerColor", targetingRule, new ImmutableContext(ctxData));

Expand Down Expand Up @@ -142,7 +182,6 @@ void fractionalTestC() throws TargetingRuleException {
Map<String, Value> ctxData = new HashMap<>();
ctxData.put("email", new Value("[email protected]"));


// when
Object evalVariant = OPERATOR.apply("headerColor", targetingRule, new ImmutableContext(ctxData));

Expand All @@ -166,7 +205,6 @@ void stringCompStartsWith() throws TargetingRuleException {
Map<String, Value> ctxData = new HashMap<>();
ctxData.put("email", new Value("[email protected]"));


// when
Object evalVariant = OPERATOR.apply("adminRule", targetingRule, new ImmutableContext(ctxData));

Expand All @@ -190,7 +228,6 @@ void stringCompEndsWith() throws TargetingRuleException {
Map<String, Value> ctxData = new HashMap<>();
ctxData.put("email", new Value("[email protected]"));


// when
Object evalVariant = OPERATOR.apply("isFaas", targetingRule, new ImmutableContext(ctxData));

Expand All @@ -215,7 +252,6 @@ void semVerA() throws TargetingRuleException {
Map<String, Value> ctxData = new HashMap<>();
ctxData.put("version", new Value("1.1.0"));


// when
Object evalVariant = OPERATOR.apply("versionFlag", targetingRule, new ImmutableContext(ctxData));

Expand Down