Skip to content

Commit fa2e3d9

Browse files
committed
add targeting key extraction for in-process provider
Signed-off-by: Kavindu Dodanduwa <[email protected]>
1 parent e3f738a commit fa2e3d9

File tree

4 files changed

+69
-35
lines changed

4 files changed

+69
-35
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
package dev.openfeature.contrib.providers.flagd.resolver.process.targeting;
22

3-
import java.util.HashMap;
4-
import java.util.Map;
5-
import java.util.Optional;
6-
import java.time.Instant;
7-
83
import dev.openfeature.sdk.EvaluationContext;
94
import io.github.jamsesso.jsonlogic.JsonLogic;
105
import io.github.jamsesso.jsonlogic.JsonLogicException;
116
import lombok.Getter;
127

8+
import java.time.Instant;
9+
import java.util.HashMap;
10+
import java.util.Map;
11+
import java.util.Optional;
12+
1313
/**
1414
* Targeting operator wraps JsonLogic handlers and expose a simple API for
1515
* external layers.
@@ -46,49 +46,57 @@ public Object apply(final String flagKey, final String targetingRule, final Eval
4646
long unixTimestamp = Instant.now().getEpochSecond();
4747
flagdProperties.put(TIME_STAMP, unixTimestamp);
4848

49-
final Map<String, Object> valueMap = ctx.asObjectMap();
50-
valueMap.put(FLAGD_PROPS_KEY, flagdProperties);
49+
final Map<String, Object> targetingCtxData = ctx.asObjectMap();
50+
51+
// asObjectMap() does not provide explicitly set targeting key (ex:- new ImmutableContext("TargetingKey") ).
52+
// Hence, we add this explicitly here for targeting rule processing.
53+
targetingCtxData.put(TARGET_KEY, ctx.getTargetingKey());
54+
targetingCtxData.put(FLAGD_PROPS_KEY, flagdProperties);
5155

5256
try {
53-
return jsonLogicHandler.apply(targetingRule, valueMap);
57+
return jsonLogicHandler.apply(targetingRule, targetingCtxData);
5458
} catch (JsonLogicException e) {
5559
throw new TargetingRuleException("Error evaluating json logic", e);
5660
}
5761
}
5862

63+
/**
64+
* A utility class to extract well-known properties such as flag key, targeting key and timestamp from json logic
65+
* evaluation context data for further processing at evaluators.
66+
*/
5967
@Getter
6068
static class FlagProperties {
61-
private final Object flagKey;
62-
private final Object timestamp;
63-
private final String targetingKey;
69+
private Object flagKey = null;
70+
private Object timestamp = null;
71+
private String targetingKey = null;
6472

6573
FlagProperties(Object from) {
66-
if (from instanceof Map) {
67-
Map<?, ?> dataMap = (Map<?, ?>) from;
68-
69-
this.flagKey = extractSubPropertyFromFlagd(dataMap, FLAG_KEY);
70-
this.timestamp = extractSubPropertyFromFlagd(dataMap, TIME_STAMP);
71-
72-
final Object targetKey = dataMap.get(TARGET_KEY);
73-
74-
if (targetKey instanceof String) {
75-
targetingKey = (String) targetKey;
76-
} else {
77-
targetingKey = null;
78-
}
79-
80-
} else {
81-
flagKey = null;
82-
timestamp = null;
83-
targetingKey = null;
74+
if (!(from instanceof Map)) {
75+
return;
76+
}
77+
78+
final Map<?, ?> dataMap = (Map<?, ?>) from;
79+
final Object targetKey = dataMap.get(TARGET_KEY);
80+
if (targetKey instanceof String) {
81+
targetingKey = (String) targetKey;
82+
}
83+
84+
final Map<?, ?> flagdPropertyMap = flagdPropertyMap(dataMap);
85+
if (flagdPropertyMap == null){
86+
return;
8487
}
88+
89+
this.flagKey = flagdPropertyMap.get(FLAG_KEY);
90+
this.timestamp = flagdPropertyMap.get(TIME_STAMP);
8591
}
8692

87-
private static Object extractSubPropertyFromFlagd(Map<?, ?> dataMap, String propertyName) {
88-
return Optional.ofNullable(dataMap.get(FLAGD_PROPS_KEY))
89-
.filter(flagdProps -> flagdProps instanceof Map)
90-
.map(flagdProps -> ((Map<?, ?>) flagdProps).get(propertyName))
91-
.orElse(null);
92-
}
93+
private static Map<?, ?> flagdPropertyMap(Map<?, ?> dataMap) {
94+
Object o = dataMap.get(FLAGD_PROPS_KEY);
95+
if (o instanceof Map) {
96+
return (Map<?, ?>) o;
97+
}
98+
99+
return null;
100+
}
93101
}
94102
}

Diff for: providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolverTest.java

+20
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_IF_IN_TARGET;
3535
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_INVALID_TARGET;
3636
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_SHORTHAND_TARGETING;
37+
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WITH_TARGETING_KEY;
3738
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.INT_FLAG;
3839
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.OBJECT_FLAG;
3940
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.VARIANT_MISMATCH_FLAG;
@@ -333,6 +334,25 @@ public void targetingUnmatchedEvaluationFlag() throws Exception {
333334
assertEquals(Reason.DEFAULT.toString(), providerEvaluation.getReason());
334335
}
335336

337+
@Test
338+
public void explicitTargetingKeyHandling() throws NoSuchFieldException, IllegalAccessException {
339+
// given
340+
final Map<String, FeatureFlag> flagMap = new HashMap<>();
341+
flagMap.put("stringFlag", FLAG_WITH_TARGETING_KEY);
342+
343+
InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), providerState -> {
344+
});
345+
346+
// when
347+
ProviderEvaluation<String> providerEvaluation =
348+
inProcessResolver.stringEvaluation("stringFlag", "loop", new MutableContext("xyz"));
349+
350+
// then
351+
assertEquals("binetAlg", providerEvaluation.getValue());
352+
assertEquals("binet", providerEvaluation.getVariant());
353+
assertEquals(Reason.TARGETING_MATCH.toString(), providerEvaluation.getReason());
354+
}
355+
336356
@Test
337357
public void targetingErrorEvaluationFlag() throws Exception {
338358
// given

Diff for: providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/MockFlags.java

+3
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ public class MockFlags {
7373
static final FeatureFlag FLAG_WIH_IF_IN_TARGET = new FeatureFlag("ENABLED", "loop", stringVariants,
7474
"{\"if\":[{\"in\":[\"@faas.com\",{\"var\":[\"email\"]}]},\"binet\",null]}");
7575

76+
static final FeatureFlag FLAG_WITH_TARGETING_KEY = new FeatureFlag("ENABLED", "loop", stringVariants,
77+
"{\"if\":[{\"==\":[{\"var\":\"targetingKey\"},\"xyz\"]},\"binet\",null]}");
78+
7679
// flag with incorrect targeting rule
7780
static final FeatureFlag FLAG_WIH_INVALID_TARGET = new FeatureFlag("ENABLED", "loop", stringVariants,
7881
"{if this, then that}");

Diff for: providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.openfeature.contrib.providers.flagd.resolver.process.targeting;
22

3+
import static dev.openfeature.contrib.providers.flagd.resolver.process.targeting.Operator.TARGET_KEY;
34
import static org.junit.jupiter.api.Assertions.assertEquals;
45
import static org.junit.jupiter.api.Assertions.assertTrue;
56

@@ -64,13 +65,15 @@ void testFlagPropertiesConstructor() {
6465
flagdProperties.put(Operator.TIME_STAMP, 1634000000L);
6566

6667
Map<String, Object> dataMap = new HashMap<>();
68+
dataMap.put(TARGET_KEY, "myTargetingKey");
6769
dataMap.put(Operator.FLAGD_PROPS_KEY, flagdProperties);
6870

6971
// When
7072
Operator.FlagProperties flagProperties = new Operator.FlagProperties(dataMap);
7173

7274
// Then
7375
assertEquals("some-key", flagProperties.getFlagKey());
76+
assertEquals("myTargetingKey", flagProperties.getTargetingKey());
7477
assertEquals(1634000000L, flagProperties.getTimestamp());
7578
}
7679

0 commit comments

Comments
 (0)