From 8380b5eb47f4254d0d7561725644b4ebc766171c Mon Sep 17 00:00:00 2001 From: DBlanchard88 Date: Mon, 23 Oct 2023 15:27:57 -0400 Subject: [PATCH 1/8] time stamp and testing Signed-off-by: DBlanchard88 --- .../resolver/process/targeting/Operator.java | 38 ++++++++++++------- .../process/targeting/OperatorTest.java | 29 +++++++++++--- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java index 88d2e42b1..2479db5df 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java @@ -3,15 +3,16 @@ 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 { @@ -19,6 +20,7 @@ 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; @@ -38,8 +40,12 @@ public Operator() { */ public Object apply(final String flagKey, final String targetingRule, final EvaluationContext ctx) throws TargetingRuleException { - final Map flagdProperties = new HashMap<>(); + final Map flagdProperties = new HashMap<>(); flagdProperties.put(FLAG_KEY, flagKey); + + long unixTimestamp = Instant.now().getEpochSecond(); + flagdProperties.put(TIME_STAMP, unixTimestamp); + final Map valueMap = ctx.asObjectMap(); valueMap.put(FLAGD_PROPS_KEY, flagdProperties); @@ -53,34 +59,38 @@ public Object apply(final String flagKey, final String targetingRule, final Eval @Getter static class FlagProperties { private final String flagKey; + private final String 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; } } + + private static String extractSubPropertyFromFlagd(Map dataMap, String propertyName) { + return Optional.ofNullable(dataMap.get(FLAGD_PROPS_KEY)) + .filter(flagdProps -> flagdProps instanceof Map) + .map(flagdProps -> ((Map) flagdProps).get(propertyName)) + .filter(value -> value instanceof String) + .map(String::valueOf) + .orElse(null); + } } } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java index c3f8e92eb..78d5638f4 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java @@ -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; @@ -33,6 +35,27 @@ 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 fractionalTestA() throws TargetingRuleException { // given @@ -64,7 +87,6 @@ void fractionalTestA() throws TargetingRuleException { Map ctxData = new HashMap<>(); ctxData.put("email", new Value("rachel@faas.com")); - // when Object evalVariant = OPERATOR.apply("headerColor", targetingRule, new ImmutableContext(ctxData)); @@ -103,7 +125,6 @@ void fractionalTestB() throws TargetingRuleException { Map ctxData = new HashMap<>(); ctxData.put("email", new Value("monica@faas.com")); - // when Object evalVariant = OPERATOR.apply("headerColor", targetingRule, new ImmutableContext(ctxData)); @@ -142,7 +163,6 @@ void fractionalTestC() throws TargetingRuleException { Map ctxData = new HashMap<>(); ctxData.put("email", new Value("joey@faas.com")); - // when Object evalVariant = OPERATOR.apply("headerColor", targetingRule, new ImmutableContext(ctxData)); @@ -166,7 +186,6 @@ void stringCompStartsWith() throws TargetingRuleException { Map ctxData = new HashMap<>(); ctxData.put("email", new Value("admin@faas.com")); - // when Object evalVariant = OPERATOR.apply("adminRule", targetingRule, new ImmutableContext(ctxData)); @@ -190,7 +209,6 @@ void stringCompEndsWith() throws TargetingRuleException { Map ctxData = new HashMap<>(); ctxData.put("email", new Value("admin@faas.com")); - // when Object evalVariant = OPERATOR.apply("isFaas", targetingRule, new ImmutableContext(ctxData)); @@ -215,7 +233,6 @@ void semVerA() throws TargetingRuleException { Map ctxData = new HashMap<>(); ctxData.put("version", new Value("1.1.0")); - // when Object evalVariant = OPERATOR.apply("versionFlag", targetingRule, new ImmutableContext(ctxData)); From 495eb6c733433c417fedaaa8fb023ee454fe266e Mon Sep 17 00:00:00 2001 From: DBlanchard88 Date: Mon, 23 Oct 2023 15:27:57 -0400 Subject: [PATCH 2/8] time stamp and testing Signed-off-by: DBlanchard88 --- .../resolver/process/targeting/Operator.java | 38 ++++++++++++------- .../process/targeting/OperatorTest.java | 29 +++++++++++--- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java index 88d2e42b1..2479db5df 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java @@ -3,15 +3,16 @@ 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 { @@ -19,6 +20,7 @@ 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; @@ -38,8 +40,12 @@ public Operator() { */ public Object apply(final String flagKey, final String targetingRule, final EvaluationContext ctx) throws TargetingRuleException { - final Map flagdProperties = new HashMap<>(); + final Map flagdProperties = new HashMap<>(); flagdProperties.put(FLAG_KEY, flagKey); + + long unixTimestamp = Instant.now().getEpochSecond(); + flagdProperties.put(TIME_STAMP, unixTimestamp); + final Map valueMap = ctx.asObjectMap(); valueMap.put(FLAGD_PROPS_KEY, flagdProperties); @@ -53,34 +59,38 @@ public Object apply(final String flagKey, final String targetingRule, final Eval @Getter static class FlagProperties { private final String flagKey; + private final String 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; } } + + private static String extractSubPropertyFromFlagd(Map dataMap, String propertyName) { + return Optional.ofNullable(dataMap.get(FLAGD_PROPS_KEY)) + .filter(flagdProps -> flagdProps instanceof Map) + .map(flagdProps -> ((Map) flagdProps).get(propertyName)) + .filter(value -> value instanceof String) + .map(String::valueOf) + .orElse(null); + } } } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java index c3f8e92eb..78d5638f4 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java @@ -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; @@ -33,6 +35,27 @@ 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 fractionalTestA() throws TargetingRuleException { // given @@ -64,7 +87,6 @@ void fractionalTestA() throws TargetingRuleException { Map ctxData = new HashMap<>(); ctxData.put("email", new Value("rachel@faas.com")); - // when Object evalVariant = OPERATOR.apply("headerColor", targetingRule, new ImmutableContext(ctxData)); @@ -103,7 +125,6 @@ void fractionalTestB() throws TargetingRuleException { Map ctxData = new HashMap<>(); ctxData.put("email", new Value("monica@faas.com")); - // when Object evalVariant = OPERATOR.apply("headerColor", targetingRule, new ImmutableContext(ctxData)); @@ -142,7 +163,6 @@ void fractionalTestC() throws TargetingRuleException { Map ctxData = new HashMap<>(); ctxData.put("email", new Value("joey@faas.com")); - // when Object evalVariant = OPERATOR.apply("headerColor", targetingRule, new ImmutableContext(ctxData)); @@ -166,7 +186,6 @@ void stringCompStartsWith() throws TargetingRuleException { Map ctxData = new HashMap<>(); ctxData.put("email", new Value("admin@faas.com")); - // when Object evalVariant = OPERATOR.apply("adminRule", targetingRule, new ImmutableContext(ctxData)); @@ -190,7 +209,6 @@ void stringCompEndsWith() throws TargetingRuleException { Map ctxData = new HashMap<>(); ctxData.put("email", new Value("admin@faas.com")); - // when Object evalVariant = OPERATOR.apply("isFaas", targetingRule, new ImmutableContext(ctxData)); @@ -215,7 +233,6 @@ void semVerA() throws TargetingRuleException { Map ctxData = new HashMap<>(); ctxData.put("version", new Value("1.1.0")); - // when Object evalVariant = OPERATOR.apply("versionFlag", targetingRule, new ImmutableContext(ctxData)); From 0db74464e52fd758cf27dca11c23cc4d02e13edb Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Mon, 23 Oct 2023 15:55:02 -0400 Subject: [PATCH 3/8] Update providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java Signed-off-by: Todd Baert --- .../providers/flagd/resolver/process/targeting/Operator.java | 1 - 1 file changed, 1 deletion(-) diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java index 2479db5df..c6f703234 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java @@ -70,7 +70,6 @@ static class FlagProperties { this.timestamp = extractSubPropertyFromFlagd(dataMap, TIME_STAMP); final Object targetKey = dataMap.get(TARGET_KEY); - if (targetKey instanceof String) { targetingKey = (String) targetKey; } else { From d819194cf4fdb26cd70b88e0ee62a5d702806aa7 Mon Sep 17 00:00:00 2001 From: David Blanchard <89858058+DBlanchard88@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:13:10 -0400 Subject: [PATCH 4/8] Update providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java Co-authored-by: Todd Baert Signed-off-by: David Blanchard <89858058+DBlanchard88@users.noreply.github.com> --- .../flagd/resolver/process/targeting/OperatorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java index 78d5638f4..51c206662 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java @@ -52,7 +52,7 @@ void timestampPresent() throws TargetingRuleException { long thresholdPast = currentTimestamp - (5); long thresholdFuture = currentTimestamp + (5); - // checks if the timestamp is within 5 minutes of the current time + // checks if the timestamp is within 5 seconds of the current time (test tolerance) assertTrue(timestamp >= thresholdPast && timestamp <= thresholdFuture); } From 0f04fc54cfbbd88f1a6c6955006a46ee140bba42 Mon Sep 17 00:00:00 2001 From: David Blanchard <89858058+DBlanchard88@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:13:19 -0400 Subject: [PATCH 5/8] Update providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java Co-authored-by: Todd Baert Signed-off-by: David Blanchard <89858058+DBlanchard88@users.noreply.github.com> --- .../flagd/resolver/process/targeting/OperatorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java index 51c206662..fa0695062 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java @@ -47,7 +47,7 @@ void timestampPresent() throws TargetingRuleException { long timestamp = (long)Double.parseDouble(timestampString.toString()); - // generating current unix timestamp & 5 minute threshold + // generating current unix timestamp for testing long currentTimestamp = Instant.now().getEpochSecond(); long thresholdPast = currentTimestamp - (5); long thresholdFuture = currentTimestamp + (5); From f6b3fa5e50e87be305c1ef627f75ee9d2d7fd7f2 Mon Sep 17 00:00:00 2001 From: DBlanchard88 Date: Tue, 31 Oct 2023 19:14:25 -0400 Subject: [PATCH 6/8] yml file fix Signed-off-by: DBlanchard88 --- .github/workflows/ci.yml | 4 ---- .github/workflows/release-please.yml | 4 ---- 2 files changed, 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 091b06d7e..836457fbb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,11 +28,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - name: Set up JDK 8 -<<<<<<< Updated upstream uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3 -======= - uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3 ->>>>>>> Stashed changes with: java-version: '8' distribution: 'temurin' diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index bbc60e1eb..ab5018f7e 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -23,11 +23,7 @@ jobs: - name: Set up JDK 8 if: ${{ steps.release.outputs.releases_created }} -<<<<<<< Updated upstream uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3 -======= - uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3 ->>>>>>> Stashed changes with: java-version: '8' distribution: 'temurin' From 5fa3f5fb75910931c050e08824117226bcf671f5 Mon Sep 17 00:00:00 2001 From: DBlanchard88 Date: Sun, 5 Nov 2023 11:07:09 -0500 Subject: [PATCH 7/8] extractSubPro back to private Signed-off-by: DBlanchard88 --- .../resolver/process/targeting/OperatorTest.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java index 39e9e8d36..79ca85f45 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java @@ -57,22 +57,21 @@ void timestampPresent() throws TargetingRuleException { } @Test - void testExtractSubPropertyFromFlagd() { + void testFlagPropertiesConstructor() { // Given Map flagdProperties = new HashMap<>(); flagdProperties.put(Operator.FLAG_KEY, "some-key"); - flagdProperties.put(Operator.TIME_STAMP, 1634000000L); // Changed to long + flagdProperties.put(Operator.TIME_STAMP, 1634000000L); Map 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); + Operator.FlagProperties flagProperties = new Operator.FlagProperties(dataMap); // Then - assertEquals("some-key", flagKey); - assertEquals(1634000000L, timestamp); // Changed to long + assertEquals("some-key", flagProperties.getFlagKey()); + assertEquals(1634000000L, flagProperties.getTimestamp()); } @Test From 687ae4088c05c68feaaf050bb18078e52104d8ef Mon Sep 17 00:00:00 2001 From: DBlanchard88 Date: Sun, 5 Nov 2023 11:08:46 -0500 Subject: [PATCH 8/8] extractSubPro back to private Signed-off-by: DBlanchard88 --- .../providers/flagd/resolver/process/targeting/Operator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java index 69727ab0d..e58861972 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java @@ -84,7 +84,7 @@ static class FlagProperties { } } - public static Object extractSubPropertyFromFlagd(Map dataMap, String propertyName) { + private 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))