diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml
index 668e79aa..37739249 100644
--- a/.github/workflows/merge.yml
+++ b/.github/workflows/merge.yml
@@ -32,7 +32,7 @@ jobs:
server-password: ${{ secrets.OSSRH_PASSWORD }}
- name: Cache local Maven repository
- uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319
+ uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml
index 1de5cdfa..61177ed6 100644
--- a/.github/workflows/pullrequest.yml
+++ b/.github/workflows/pullrequest.yml
@@ -20,12 +20,12 @@ jobs:
cache: maven
- name: Initialize CodeQL
- uses: github/codeql-action/init@f055b5e672ed1ea4fd98a276788e4bcb5a64ad17
+ uses: github/codeql-action/init@3d817349a4534f494b019aff837b9a577fdc5496
with:
languages: java
- name: Cache local Maven repository
- uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319
+ uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -45,4 +45,4 @@ jobs:
verbose: true # optional (default = false)
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@f055b5e672ed1ea4fd98a276788e4bcb5a64ad17
+ uses: github/codeql-action/analyze@3d817349a4534f494b019aff837b9a577fdc5496
diff --git a/.github/workflows/static-code-scanning.yaml b/.github/workflows/static-code-scanning.yaml
index a2d896ec..c19702ee 100644
--- a/.github/workflows/static-code-scanning.yaml
+++ b/.github/workflows/static-code-scanning.yaml
@@ -33,12 +33,12 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@f055b5e672ed1ea4fd98a276788e4bcb5a64ad17
+ uses: github/codeql-action/init@3d817349a4534f494b019aff837b9a577fdc5496
with:
languages: java
- name: Autobuild
- uses: github/codeql-action/autobuild@f055b5e672ed1ea4fd98a276788e4bcb5a64ad17
+ uses: github/codeql-action/autobuild@3d817349a4534f494b019aff837b9a577fdc5496
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@f055b5e672ed1ea4fd98a276788e4bcb5a64ad17
+ uses: github/codeql-action/analyze@3d817349a4534f494b019aff837b9a577fdc5496
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index b4c6198d..63f23943 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1 +1 @@
-{".":"1.7.5"}
\ No newline at end of file
+{".":"1.7.6"}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 01b9d7eb..5accb8c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,28 @@
# Changelog
+## [1.7.6](https://github.com/open-feature/java-sdk/compare/v1.7.5...v1.7.6) (2024-03-22)
+
+
+### ๐ Bug Fixes
+
+* **deps:** update dependency io.cucumber:cucumber-bom to v7.16.0 ([#861](https://github.com/open-feature/java-sdk/issues/861)) ([433f94a](https://github.com/open-feature/java-sdk/commit/433f94a6ea541c5be2ee7a0f902098edce8ba3fc))
+* **deps:** update dependency org.projectlombok:lombok to v1.18.32 ([#854](https://github.com/open-feature/java-sdk/issues/854)) ([ee49872](https://github.com/open-feature/java-sdk/commit/ee49872dd56778ebb4a1ee23b596ffe812dca59c))
+* support immutable maps [#859](https://github.com/open-feature/java-sdk/issues/859) ([#860](https://github.com/open-feature/java-sdk/issues/860)) ([d51cacb](https://github.com/open-feature/java-sdk/commit/d51cacbff6827102d4e3ea5a737bd016d27b1fc2))
+* missing targeting key should return null ([#849](https://github.com/open-feature/java-sdk/issues/849)) ([48a196c](https://github.com/open-feature/java-sdk/commit/48a196c50df992e4ee1006d6b73b619e04f7a224))
+
+
+### ๐งน Chore
+
+* **deps:** update actions/cache digest to 0c45773 ([#853](https://github.com/open-feature/java-sdk/issues/853)) ([8b72323](https://github.com/open-feature/java-sdk/commit/8b723232a15d43980a5c78b5724f91efdfd4e5b4))
+* **deps:** update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.13.0 ([#852](https://github.com/open-feature/java-sdk/issues/852)) ([d23e4d8](https://github.com/open-feature/java-sdk/commit/d23e4d89169b8316b11d48014b43bc4dd14d7e29))
+* **deps:** update dependency org.apache.maven.plugins:maven-gpg-plugin to v3.2.1 ([#850](https://github.com/open-feature/java-sdk/issues/850)) ([b249bfb](https://github.com/open-feature/java-sdk/commit/b249bfb2ceb62178b9776023839e71234f76746b))
+* **deps:** update dependency org.awaitility:awaitility to v4.2.1 ([#845](https://github.com/open-feature/java-sdk/issues/845)) ([5b2813c](https://github.com/open-feature/java-sdk/commit/5b2813c659cdea9f6cdc8a145414de672423c15e))
+* **deps:** update github/codeql-action digest to 09d4101 ([#857](https://github.com/open-feature/java-sdk/issues/857)) ([83d7501](https://github.com/open-feature/java-sdk/commit/83d7501551efad1612f104d6c7e3064c361728c5))
+* **deps:** update github/codeql-action digest to 1ecc277 ([#846](https://github.com/open-feature/java-sdk/issues/846)) ([a763740](https://github.com/open-feature/java-sdk/commit/a763740e6b45b0d51c219675cd9e1839def61b8e))
+* **deps:** update github/codeql-action digest to 294b6df ([#851](https://github.com/open-feature/java-sdk/issues/851)) ([f4e17cc](https://github.com/open-feature/java-sdk/commit/f4e17ccae67cfe08f5e33dba984a4ea6117a9624))
+* **deps:** update github/codeql-action digest to 3d81734 ([#862](https://github.com/open-feature/java-sdk/issues/862)) ([675de14](https://github.com/open-feature/java-sdk/commit/675de140e49bd69860662f28aced3d4ff9344cd5))
+* **deps:** update github/codeql-action digest to 964f5e7 ([#856](https://github.com/open-feature/java-sdk/issues/856)) ([e1e15f4](https://github.com/open-feature/java-sdk/commit/e1e15f4442475e918a1ca5101af8c6ca9a4fa082))
+
## [1.7.5](https://github.com/open-feature/java-sdk/compare/v1.7.4...v1.7.5) (2024-03-14)
diff --git a/README.md b/README.md
index b6af8d41..36095f06 100644
--- a/README.md
+++ b/README.md
@@ -18,8 +18,8 @@
-
-
+
+
@@ -59,7 +59,7 @@ Note that this library is intended to be used in server-side contexts and has no
dev.openfeature
sdk
- 1.7.5
+ 1.7.6
```
@@ -84,7 +84,7 @@ If you would like snapshot builds, this is the relevant repository information:
```groovy
dependencies {
- implementation 'dev.openfeature:sdk:1.7.5'
+ implementation 'dev.openfeature:sdk:1.7.6'
}
```
diff --git a/pom.xml b/pom.xml
index e846254a..845824f6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
dev.openfeature
sdk
- 1.7.5
+ 1.7.6
UTF-8
@@ -45,7 +45,7 @@
org.projectlombok
lombok
- 1.18.30
+ 1.18.32
provided
@@ -142,7 +142,7 @@
org.awaitility
awaitility
- 4.2.0
+ 4.2.1
test
@@ -154,7 +154,7 @@
io.cucumber
cucumber-bom
- 7.15.0
+ 7.16.0
pom
import
@@ -229,7 +229,7 @@
maven-compiler-plugin
- 3.12.1
+ 3.13.0
@@ -472,7 +472,7 @@
org.apache.maven.plugins
maven-gpg-plugin
- 3.2.0
+ 3.2.1
sign-artifacts
diff --git a/src/main/java/dev/openfeature/sdk/AbstractStructure.java b/src/main/java/dev/openfeature/sdk/AbstractStructure.java
index a7d7c2ea..e50fbe92 100644
--- a/src/main/java/dev/openfeature/sdk/AbstractStructure.java
+++ b/src/main/java/dev/openfeature/sdk/AbstractStructure.java
@@ -13,7 +13,7 @@ abstract class AbstractStructure implements Structure {
}
AbstractStructure(Map attributes) {
- this.attributes = attributes;
+ this.attributes = new HashMap<>(attributes);
}
/**
diff --git a/src/main/java/dev/openfeature/sdk/ImmutableContext.java b/src/main/java/dev/openfeature/sdk/ImmutableContext.java
index 632448aa..0d02ba31 100644
--- a/src/main/java/dev/openfeature/sdk/ImmutableContext.java
+++ b/src/main/java/dev/openfeature/sdk/ImmutableContext.java
@@ -52,9 +52,12 @@ public ImmutableContext(Map attributes) {
*/
public ImmutableContext(String targetingKey, Map attributes) {
if (targetingKey != null && !targetingKey.trim().isEmpty()) {
- attributes.put(TARGETING_KEY, new Value(targetingKey));
+ final Map actualAttribs = new HashMap<>(attributes);
+ actualAttribs.put(TARGETING_KEY, new Value(targetingKey));
+ this.structure = new ImmutableStructure(actualAttribs);
+ } else {
+ this.structure = new ImmutableStructure(attributes);
}
- this.structure = new ImmutableStructure(attributes);
}
/**
@@ -62,7 +65,8 @@ public ImmutableContext(String targetingKey, Map attributes) {
*/
@Override
public String getTargetingKey() {
- return this.getValue(TARGETING_KEY).asString();
+ Value value = this.getValue(TARGETING_KEY);
+ return value == null ? null : value.asString();
}
/**
diff --git a/src/main/java/dev/openfeature/sdk/MutableContext.java b/src/main/java/dev/openfeature/sdk/MutableContext.java
index 69c22b84..b63f9b31 100644
--- a/src/main/java/dev/openfeature/sdk/MutableContext.java
+++ b/src/main/java/dev/openfeature/sdk/MutableContext.java
@@ -42,10 +42,10 @@ public MutableContext(Map attributes) {
* @param attributes evaluation context attributes
*/
public MutableContext(String targetingKey, Map attributes) {
+ this.structure = new MutableStructure(attributes);
if (targetingKey != null && !targetingKey.trim().isEmpty()) {
- attributes.put(TARGETING_KEY, new Value(targetingKey));
+ this.structure.attributes.put(TARGETING_KEY, new Value(targetingKey));
}
- this.structure = new MutableStructure(attributes);
}
// override @Delegate methods so that we can use "add" methods and still return MutableContext, not Structure
@@ -99,7 +99,8 @@ public void setTargetingKey(String targetingKey) {
*/
@Override
public String getTargetingKey() {
- return this.getValue(TARGETING_KEY).asString();
+ Value value = this.getValue(TARGETING_KEY);
+ return value == null ? null : value.asString();
}
/**
diff --git a/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java b/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java
index 7cfedec9..44a6f479 100644
--- a/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java
+++ b/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java
@@ -1,5 +1,7 @@
package dev.openfeature.sdk;
+import java.util.Collections;
+import java.util.Map;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -12,6 +14,17 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
class ImmutableContextTest {
+ @DisplayName("attributes unable to allow mutation should not affect the immutable context")
+ @Test
+ void shouldNotAttemptToModifyAttributesForImmutableContext() {
+ final Map attributes = new HashMap<>();
+ attributes.put("key1", new Value("val1"));
+ attributes.put("key2", new Value("val2"));
+ // should check the usage of Map.of() which is a more likely use case, but that API isn't available in Java 8
+ EvaluationContext ctx = new ImmutableContext("targeting key", Collections.unmodifiableMap(attributes));
+ attributes.put("key3", new Value("val3"));
+ assertArrayEquals(new Object[]{"key1", "key2", TARGETING_KEY}, ctx.keySet().toArray());
+ }
@DisplayName("attributes mutation should not affect the immutable context")
@Test
@@ -47,6 +60,12 @@ void shouldRetainTargetingKeyWhenOverridingContextTargetingKeyValueIsEmpty() {
EvaluationContext merge = ctx.merge(overriding);
assertEquals("targeting_key", merge.getTargetingKey());
}
+ @DisplayName("missing targeting key should return null")
+ @Test
+ void missingTargetingKeyShould() {
+ EvaluationContext ctx = new ImmutableContext();
+ assertEquals(null, ctx.getTargetingKey());
+ }
@DisplayName("Merge should retain all the attributes from the existing context when overriding context is null")
@Test
diff --git a/src/test/java/dev/openfeature/sdk/MutableContextTest.java b/src/test/java/dev/openfeature/sdk/MutableContextTest.java
new file mode 100644
index 00000000..1d462b12
--- /dev/null
+++ b/src/test/java/dev/openfeature/sdk/MutableContextTest.java
@@ -0,0 +1,119 @@
+package dev.openfeature.sdk;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static dev.openfeature.sdk.EvaluationContext.TARGETING_KEY;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class MutableContextTest {
+
+ @DisplayName("attributes unable to allow mutation should not affect the Mutable context")
+ @Test
+ void shouldNotAttemptToModifyAttributesForMutableContext() {
+ final Map attributes = new HashMap<>();
+ attributes.put("key1", new Value("val1"));
+ attributes.put("key2", new Value("val2"));
+ // should check the usage of Map.of() which is a more likely use case, but that API isn't available in Java 8
+ EvaluationContext ctx = new MutableContext("targeting key", Collections.unmodifiableMap(attributes));
+ attributes.put("key3", new Value("val3"));
+ assertArrayEquals(new Object[]{"key1", "key2", TARGETING_KEY}, ctx.keySet().toArray());
+ }
+
+ @DisplayName("targeting key should be changed from the overriding context")
+ @Test
+ void shouldChangeTargetingKeyFromOverridingContext() {
+ HashMap attributes = new HashMap<>();
+ attributes.put("key1", new Value("val1"));
+ attributes.put("key2", new Value("val2"));
+ EvaluationContext ctx = new MutableContext("targeting key", attributes);
+ EvaluationContext overriding = new MutableContext("overriding_key");
+ EvaluationContext merge = ctx.merge(overriding);
+ assertEquals("overriding_key", merge.getTargetingKey());
+ }
+
+ @DisplayName("targeting key should not changed from the overriding context if missing")
+ @Test
+ void shouldRetainTargetingKeyWhenOverridingContextTargetingKeyValueIsEmpty() {
+ HashMap attributes = new HashMap<>();
+ attributes.put("key1", new Value("val1"));
+ attributes.put("key2", new Value("val2"));
+ EvaluationContext ctx = new MutableContext("targeting_key", attributes);
+ EvaluationContext overriding = new MutableContext("");
+ EvaluationContext merge = ctx.merge(overriding);
+ assertEquals("targeting_key", merge.getTargetingKey());
+ }
+ @DisplayName("missing targeting key should return null")
+ @Test
+ void missingTargetingKeyShould() {
+ EvaluationContext ctx = new MutableContext();
+ assertEquals(null, ctx.getTargetingKey());
+ }
+
+ @DisplayName("Merge should retain all the attributes from the existing context when overriding context is null")
+ @Test
+ void mergeShouldReturnAllTheValuesFromTheContextWhenOverridingContextIsNull() {
+ HashMap attributes = new HashMap<>();
+ attributes.put("key1", new Value("val1"));
+ attributes.put("key2", new Value("val2"));
+ EvaluationContext ctx = new MutableContext("targeting_key", attributes);
+ EvaluationContext merge = ctx.merge(null);
+ assertEquals("targeting_key", merge.getTargetingKey());
+ assertArrayEquals(new Object[]{"key1", "key2", TARGETING_KEY}, merge.keySet().toArray());
+ }
+
+ @DisplayName("Merge should retain subkeys from the existing context when the overriding context has the same targeting key")
+ @Test
+ void mergeShouldRetainItsSubkeysWhenOverridingContextHasTheSameKey() {
+ HashMap attributes = new HashMap<>();
+ HashMap overridingAttributes = new HashMap<>();
+ HashMap key1Attributes = new HashMap<>();
+ HashMap ovKey1Attributes = new HashMap<>();
+
+ key1Attributes.put("key1_1", new Value("val1_1"));
+ attributes.put("key1", new Value(new ImmutableStructure(key1Attributes)));
+ attributes.put("key2", new Value("val2"));
+ ovKey1Attributes.put("overriding_key1_1", new Value("overriding_val_1_1"));
+ overridingAttributes.put("key1", new Value(new ImmutableStructure(ovKey1Attributes)));
+
+ EvaluationContext ctx = new MutableContext("targeting_key", attributes);
+ EvaluationContext overriding = new MutableContext("targeting_key", overridingAttributes);
+ EvaluationContext merge = ctx.merge(overriding);
+ assertEquals("targeting_key", merge.getTargetingKey());
+ assertArrayEquals(new Object[]{"key1", "key2", TARGETING_KEY}, merge.keySet().toArray());
+
+ Value key1 = merge.getValue("key1");
+ assertTrue(key1.isStructure());
+
+ Structure value = key1.asStructure();
+ assertArrayEquals(new Object[]{"key1_1","overriding_key1_1"}, value.keySet().toArray());
+ }
+
+ @DisplayName("Merge should retain subkeys from the existing context when the overriding context doesn't have targeting key")
+ @Test
+ void mergeShouldRetainItsSubkeysWhenOverridingContextHasNoTargetingKey() {
+ HashMap attributes = new HashMap<>();
+ HashMap key1Attributes = new HashMap<>();
+
+ key1Attributes.put("key1_1", new Value("val1_1"));
+ attributes.put("key1", new Value(new ImmutableStructure(key1Attributes)));
+ attributes.put("key2", new Value("val2"));
+
+ EvaluationContext ctx = new MutableContext(attributes);
+ EvaluationContext overriding = new MutableContext();
+ EvaluationContext merge = ctx.merge(overriding);
+ assertArrayEquals(new Object[]{"key1", "key2"}, merge.keySet().toArray());
+
+ Value key1 = merge.getValue("key1");
+ assertTrue(key1.isStructure());
+
+ Structure value = key1.asStructure();
+ assertArrayEquals(new Object[]{"key1_1"}, value.keySet().toArray());
+ }
+}
diff --git a/version.txt b/version.txt
index 6a126f40..de28578a 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-1.7.5
+1.7.6