Skip to content

Commit 7022093

Browse files
fix(policies): handle null policies list in listDynamicPolicies (#40)
* fix(policies): handle null policies list in listDynamicPolicies When the API returns {"policies": null} instead of {"policies": []}, the SDK now correctly returns an empty list instead of throwing NPE. Fixes getaxonflow/axonflow-enterprise#40 * chore: bump version to 2.1.1 for patch release - Added changelog entry for null policies list fix - Updated pom.xml version from 2.1.0 to 2.1.1 * fix: handle null policies list in all policy methods - Add null checks for wrapper and list fields in: - listDynamicPolicies() - getEffectiveDynamicPolicies() - listStaticPolicies() - getEffectiveStaticPolicies() - Return empty list when wrapper or list is null - Add unit tests for null handling in all 4 methods - Bump version to 2.1.1 Fixes #40
1 parent 6e41897 commit 7022093

4 files changed

Lines changed: 89 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ All notable changes to the AxonFlow Java SDK will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.1.1] - 2026-01-06
9+
10+
### Fixed
11+
12+
- **Null Policies List Handling**: Fixed `NullPointerException` in list-returning policy methods when API returns null instead of empty array
13+
- Affected methods: `listDynamicPolicies()`, `getEffectiveDynamicPolicies()`, `listStaticPolicies()`, `getEffectiveStaticPolicies()`
14+
- Added explicit null check for wrapper and list fields before returning
15+
- Returns empty list when wrapper or list field is null
16+
817
## [2.1.0] - 2026-01-05
918

1019
### Added

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.getaxonflow</groupId>
88
<artifactId>axonflow-sdk</artifactId>
9-
<version>2.1.0</version>
9+
<version>2.1.1</version>
1010
<packaging>jar</packaging>
1111

1212
<name>AxonFlow Java SDK</name>

src/main/java/com/getaxonflow/sdk/AxonFlow.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,11 @@ public List<StaticPolicy> listStaticPolicies(ListStaticPoliciesOptions options)
906906
Request httpRequest = buildRequest("GET", path, null);
907907
try (Response response = httpClient.newCall(httpRequest).execute()) {
908908
StaticPoliciesResponse wrapper = parseResponse(response, StaticPoliciesResponse.class);
909-
return wrapper.getPolicies() != null ? wrapper.getPolicies() : java.util.Collections.emptyList();
909+
// Handle null wrapper or null policies list (Issue #40)
910+
if (wrapper == null || wrapper.getPolicies() == null) {
911+
return java.util.Collections.emptyList();
912+
}
913+
return wrapper.getPolicies();
910914
}
911915
}, "listStaticPolicies");
912916
}
@@ -1081,7 +1085,11 @@ public List<StaticPolicy> getEffectiveStaticPolicies(EffectivePoliciesOptions op
10811085
Request httpRequest = buildRequest("GET", path.toString(), null);
10821086
try (Response response = httpClient.newCall(httpRequest).execute()) {
10831087
EffectivePoliciesResponse wrapper = parseResponse(response, EffectivePoliciesResponse.class);
1084-
return wrapper.getStaticPolicies() != null ? wrapper.getStaticPolicies() : java.util.Collections.emptyList();
1088+
// Handle null wrapper or null policies list (Issue #40)
1089+
if (wrapper == null || wrapper.getStaticPolicies() == null) {
1090+
return java.util.Collections.emptyList();
1091+
}
1092+
return wrapper.getStaticPolicies();
10851093
}
10861094
}, "getEffectiveStaticPolicies");
10871095
}
@@ -1228,7 +1236,11 @@ public List<DynamicPolicy> listDynamicPolicies(ListDynamicPoliciesOptions option
12281236
try (Response response = httpClient.newCall(httpRequest).execute()) {
12291237
// Agent proxy (Issue #886) returns {"policies": [...]} wrapper
12301238
DynamicPoliciesResponse wrapper = parseResponse(response, DynamicPoliciesResponse.class);
1231-
return wrapper != null ? wrapper.getPolicies() : java.util.Collections.emptyList();
1239+
// Handle null wrapper or null policies list (Issue #40)
1240+
if (wrapper == null || wrapper.getPolicies() == null) {
1241+
return java.util.Collections.emptyList();
1242+
}
1243+
return wrapper.getPolicies();
12321244
}
12331245
}, "listDynamicPolicies");
12341246
}
@@ -1360,7 +1372,11 @@ public List<DynamicPolicy> getEffectiveDynamicPolicies(EffectivePoliciesOptions
13601372
try (Response response = httpClient.newCall(httpRequest).execute()) {
13611373
// Agent proxy (Issue #886) returns {"policies": [...]} wrapper
13621374
DynamicPoliciesResponse wrapper = parseResponse(response, DynamicPoliciesResponse.class);
1363-
return wrapper != null ? wrapper.getPolicies() : java.util.Collections.emptyList();
1375+
// Handle null wrapper or null policies list (Issue #40)
1376+
if (wrapper == null || wrapper.getPolicies() == null) {
1377+
return java.util.Collections.emptyList();
1378+
}
1379+
return wrapper.getPolicies();
13641380
}
13651381
}, "getEffectiveDynamicPolicies");
13661382
}

src/test/java/com/getaxonflow/sdk/PolicyTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,20 @@ void listStaticPoliciesShouldReturnPolicies() {
111111
assertThat(policies.get(0).getName()).isEqualTo("Block SQL Injection");
112112
}
113113

114+
@Test
115+
@DisplayName("listStaticPolicies should return empty list when policies is null")
116+
void listStaticPoliciesShouldReturnEmptyListWhenNull() {
117+
stubFor(get(urlPathEqualTo("/api/v1/static-policies"))
118+
.willReturn(aResponse()
119+
.withStatus(200)
120+
.withHeader("Content-Type", "application/json")
121+
.withBody("{\"policies\": null}")));
122+
123+
List<StaticPolicy> policies = axonflow.listStaticPolicies();
124+
125+
assertThat(policies).isEmpty();
126+
}
127+
114128
@Test
115129
@DisplayName("listStaticPolicies with filters should include query params")
116130
void listStaticPoliciesWithFiltersShouldIncludeQueryParams() {
@@ -259,6 +273,21 @@ void getEffectiveStaticPoliciesShouldReturnEffectivePolicies() {
259273
assertThat(policies).hasSize(1);
260274
}
261275

276+
@Test
277+
@DisplayName("getEffectiveStaticPolicies should return empty list when static is null")
278+
void getEffectiveStaticPoliciesShouldReturnEmptyListWhenNull() {
279+
// Issue #40: Handle null policies list
280+
stubFor(get(urlPathEqualTo("/api/v1/static-policies/effective"))
281+
.willReturn(aResponse()
282+
.withStatus(200)
283+
.withHeader("Content-Type", "application/json")
284+
.withBody("{\"static\": null, \"dynamic\": []}")));
285+
286+
List<StaticPolicy> policies = axonflow.getEffectiveStaticPolicies();
287+
288+
assertThat(policies).isEmpty();
289+
}
290+
262291
@Test
263292
@DisplayName("testPattern should test pattern against inputs")
264293
void testPatternShouldTestPatternAgainstInputs() {
@@ -411,6 +440,21 @@ void listDynamicPoliciesShouldReturnPolicies() {
411440
assertThat(policies.get(0).getName()).isEqualTo("Rate Limit API");
412441
}
413442

443+
@Test
444+
@DisplayName("listDynamicPolicies should return empty list when policies is null")
445+
void listDynamicPoliciesShouldReturnEmptyListWhenNull() {
446+
// Issue #40: Handle null policies list
447+
stubFor(get(urlPathEqualTo("/api/v1/dynamic-policies"))
448+
.willReturn(aResponse()
449+
.withStatus(200)
450+
.withHeader("Content-Type", "application/json")
451+
.withBody("{\"policies\": null}")));
452+
453+
List<DynamicPolicy> policies = axonflow.listDynamicPolicies();
454+
455+
assertThat(policies).isEmpty();
456+
}
457+
414458
@Test
415459
@DisplayName("listDynamicPolicies with filters should include query params")
416460
void listDynamicPoliciesWithFiltersShouldIncludeQueryParams() {
@@ -563,6 +607,21 @@ void getEffectiveDynamicPoliciesShouldReturnEffectivePolicies() {
563607

564608
assertThat(policies).hasSize(1);
565609
}
610+
611+
@Test
612+
@DisplayName("getEffectiveDynamicPolicies should return empty list when policies is null")
613+
void getEffectiveDynamicPoliciesShouldReturnEmptyListWhenNull() {
614+
// Issue #40: Handle null policies list
615+
stubFor(get(urlPathEqualTo("/api/v1/dynamic-policies/effective"))
616+
.willReturn(aResponse()
617+
.withStatus(200)
618+
.withHeader("Content-Type", "application/json")
619+
.withBody("{\"policies\": null}")));
620+
621+
List<DynamicPolicy> policies = axonflow.getEffectiveDynamicPolicies();
622+
623+
assertThat(policies).isEmpty();
624+
}
566625
}
567626

568627
// ========================================================================

0 commit comments

Comments
 (0)