From 6dbb8d692b53daea0fa297468eff610ff5b78403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Mon, 19 Jan 2026 16:04:53 +0100 Subject: [PATCH 01/33] fix: Update code --- src/main/java/org/hisp/dhis/api/ApiFields.java | 3 ++- src/main/java/org/hisp/dhis/model/user/User.java | 2 ++ src/test/java/org/hisp/dhis/ApiFieldsTest.java | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/hisp/dhis/api/ApiFields.java b/src/main/java/org/hisp/dhis/api/ApiFields.java index 5b627fe2..fed3910d 100644 --- a/src/main/java/org/hisp/dhis/api/ApiFields.java +++ b/src/main/java/org/hisp/dhis/api/ApiFields.java @@ -465,7 +465,8 @@ public class ApiFields { String.format( """ %1$s,username,firstName,surname,email,phoneNumber,externalAuth,lastLogin,\ - organisationUnits[%2$s],groups[%1$s],userRoles[%1$s]\ + disabled,interests,\ + organisationUnits[%2$s],groups[%1$s],userRoles[%1$s],\ dataViewOrganisationUnits[%2$s],\ teiSearchOrganisationUnits[%2$s]""", ID_EXT_FIELDS, NAME_FIELDS); diff --git a/src/main/java/org/hisp/dhis/model/user/User.java b/src/main/java/org/hisp/dhis/model/user/User.java index 27a2bc0d..800e70e5 100644 --- a/src/main/java/org/hisp/dhis/model/user/User.java +++ b/src/main/java/org/hisp/dhis/model/user/User.java @@ -62,6 +62,8 @@ public class User extends IdentifiableObject { @JsonProperty private Boolean disabled; + @JsonProperty private String interests; + @JsonProperty private List organisationUnits = new ArrayList<>(); @JsonProperty private List dataViewOrganisationUnits = new ArrayList<>(); diff --git a/src/test/java/org/hisp/dhis/ApiFieldsTest.java b/src/test/java/org/hisp/dhis/ApiFieldsTest.java index dbfbcb30..7f8d2659 100644 --- a/src/test/java/org/hisp/dhis/ApiFieldsTest.java +++ b/src/test/java/org/hisp/dhis/ApiFieldsTest.java @@ -145,4 +145,19 @@ void testProgramMinFields() { assertEquals(expected, ApiFields.PROGRAM_MIN_FIELDS); } + + @Test + void testUserFields() { + String expected = + """ + id,code,name,created,lastUpdated,attributeValues,translations,sharing,access,\ + username,firstName,surname,email,phoneNumber,externalAuth,lastLogin,disabled,interests,\ + organisationUnits[id,code,name,created,lastUpdated,attributeValues,shortName,description],\ + groups[id,code,name,created,lastUpdated,attributeValues,translations,sharing,access],\ + userRoles[id,code,name,created,lastUpdated,attributeValues,translations,sharing,access],\ + dataViewOrganisationUnits[id,code,name,created,lastUpdated,attributeValues,shortName,description],\ + teiSearchOrganisationUnits[id,code,name,created,lastUpdated,attributeValues,shortName,description]"""; + + assertEquals(expected, ApiFields.USER_FIELDS); + } } From eef36e9585d26cca99bf0883008097f366aab49e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Wed, 21 Jan 2026 21:45:29 +0100 Subject: [PATCH 02/33] fix: Update code --- .../org/hisp/dhis/response/data/DataResponse.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/org/hisp/dhis/response/data/DataResponse.java b/src/main/java/org/hisp/dhis/response/data/DataResponse.java index 241aba57..7ad5eb8f 100644 --- a/src/main/java/org/hisp/dhis/response/data/DataResponse.java +++ b/src/main/java/org/hisp/dhis/response/data/DataResponse.java @@ -30,6 +30,7 @@ import static org.hisp.dhis.util.TextUtils.newToStringBuilder; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -61,4 +62,14 @@ public DataResponse(Status status, HttpStatus httpStatus, String message, Object public String toString() { return newToStringBuilder(this, super.toString()).append("data", data).toString(); } + + /** + * Creates a map with a single entry where the key is "id" and the value is the provided value. + * + * @param value the value. + * @return a map containing the "id" entry. + */ + public static Map getIdMap(String value) { + return Map.of("id", value); + } } From 2aeb160ca43ad7c0bf2ae98a832fda769b854531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Wed, 21 Jan 2026 21:46:39 +0100 Subject: [PATCH 03/33] fix: Update code --- src/main/java/org/hisp/dhis/response/data/DataResponse.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/hisp/dhis/response/data/DataResponse.java b/src/main/java/org/hisp/dhis/response/data/DataResponse.java index 7ad5eb8f..84242af1 100644 --- a/src/main/java/org/hisp/dhis/response/data/DataResponse.java +++ b/src/main/java/org/hisp/dhis/response/data/DataResponse.java @@ -66,10 +66,10 @@ public String toString() { /** * Creates a map with a single entry where the key is "id" and the value is the provided value. * - * @param value the value. + * @param id the identifier value. * @return a map containing the "id" entry. */ - public static Map getIdMap(String value) { - return Map.of("id", value); + public static Map getIdMap(String id) { + return Map.of("id", id); } } From 09e16f5154aee33b465cfa3da8b3b6c859d89704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Thu, 22 Jan 2026 13:47:07 +0100 Subject: [PATCH 04/33] fix: Update code --- src/main/java/org/hisp/dhis/model/Pager.java | 11 +++++ src/main/java/org/hisp/dhis/model/Result.java | 40 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/main/java/org/hisp/dhis/model/Result.java diff --git a/src/main/java/org/hisp/dhis/model/Pager.java b/src/main/java/org/hisp/dhis/model/Pager.java index bd59efdc..1df7a534 100644 --- a/src/main/java/org/hisp/dhis/model/Pager.java +++ b/src/main/java/org/hisp/dhis/model/Pager.java @@ -45,4 +45,15 @@ public class Pager { @JsonProperty private Long total; @JsonProperty private String nextPage; + + /** + * Constructor. + * + * @param page the page number. + * @param pageSize the page size. + */ + public Pager(Integer page, Integer pageSize) { + this.page = page; + this.pageSize = pageSize; + } } diff --git a/src/main/java/org/hisp/dhis/model/Result.java b/src/main/java/org/hisp/dhis/model/Result.java new file mode 100644 index 00000000..690c96c6 --- /dev/null +++ b/src/main/java/org/hisp/dhis/model/Result.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2004-2026, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.model; + +import java.util.List; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class Result { + private final Pager pager; + + private final List objects; +} From 22baaf02e690bd25b82a36a791a4d0c4015114f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Thu, 22 Jan 2026 13:52:59 +0100 Subject: [PATCH 05/33] fix: Update code --- src/main/java/org/hisp/dhis/model/Result.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/hisp/dhis/model/Result.java b/src/main/java/org/hisp/dhis/model/Result.java index 690c96c6..662699de 100644 --- a/src/main/java/org/hisp/dhis/model/Result.java +++ b/src/main/java/org/hisp/dhis/model/Result.java @@ -36,5 +36,5 @@ public class Result { private final Pager pager; - private final List objects; + private final List results; } From 65e4c6a4e225c9f83cefbfc98c73bccdfb411a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Thu, 22 Jan 2026 14:30:09 +0100 Subject: [PATCH 06/33] fix: Update code --- src/main/java/org/hisp/dhis/model/Pager.java | 2 ++ .../Result.java => response/PagedResponse.java} | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) rename src/main/java/org/hisp/dhis/{model/Result.java => response/PagedResponse.java} (82%) diff --git a/src/main/java/org/hisp/dhis/model/Pager.java b/src/main/java/org/hisp/dhis/model/Pager.java index 1df7a534..b3188e68 100644 --- a/src/main/java/org/hisp/dhis/model/Pager.java +++ b/src/main/java/org/hisp/dhis/model/Pager.java @@ -55,5 +55,7 @@ public class Pager { public Pager(Integer page, Integer pageSize) { this.page = page; this.pageSize = pageSize; + this.pageCount = -1L; + this.total = -1L; } } diff --git a/src/main/java/org/hisp/dhis/model/Result.java b/src/main/java/org/hisp/dhis/response/PagedResponse.java similarity index 82% rename from src/main/java/org/hisp/dhis/model/Result.java rename to src/main/java/org/hisp/dhis/response/PagedResponse.java index 662699de..ff1be6ae 100644 --- a/src/main/java/org/hisp/dhis/model/Result.java +++ b/src/main/java/org/hisp/dhis/response/PagedResponse.java @@ -25,16 +25,21 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.model; +package org.hisp.dhis.response; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.hisp.dhis.model.Pager; +/** Response containing a paged list of objects. */ @Getter @RequiredArgsConstructor -public class Result { - private final Pager pager; +public class PagedResponse { + /** Paging information. */ + @JsonProperty private Pager pager; - private final List results; + /** List of objects. */ + @JsonProperty private List objects; } From 67333630f42d6b8a36430366eb5f48b9a2532ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Thu, 22 Jan 2026 14:31:00 +0100 Subject: [PATCH 07/33] fix: Update code --- src/main/java/org/hisp/dhis/response/PagedResponse.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/hisp/dhis/response/PagedResponse.java b/src/main/java/org/hisp/dhis/response/PagedResponse.java index ff1be6ae..5fa6dcac 100644 --- a/src/main/java/org/hisp/dhis/response/PagedResponse.java +++ b/src/main/java/org/hisp/dhis/response/PagedResponse.java @@ -38,8 +38,8 @@ @RequiredArgsConstructor public class PagedResponse { /** Paging information. */ - @JsonProperty private Pager pager; + @JsonProperty private final Pager pager; /** List of objects. */ - @JsonProperty private List objects; + @JsonProperty private final List objects; } From 524576b6f424a60e1fccf456e7842459b252c34f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Tue, 27 Jan 2026 00:34:36 +0100 Subject: [PATCH 08/33] fix: Update code --- .../hisp/dhis/util/IdentifiableObjectUtils.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/org/hisp/dhis/util/IdentifiableObjectUtils.java b/src/main/java/org/hisp/dhis/util/IdentifiableObjectUtils.java index 148b52b3..2bb515dc 100644 --- a/src/main/java/org/hisp/dhis/util/IdentifiableObjectUtils.java +++ b/src/main/java/org/hisp/dhis/util/IdentifiableObjectUtils.java @@ -32,6 +32,7 @@ import java.util.Collection; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.hisp.dhis.model.IdentifiableObject; @@ -86,6 +87,20 @@ public static List toIdObjects( return objects.stream().filter(Objects::nonNull).map(o -> getInstance(o, type)).toList(); } + /** + * Generates a fingerprint for the given collection of identifiable objects. + * + * @param the type. + * @param objects the collection of {@link IdentifiableObject}. + * @return a fingerprint string. + */ + public static String getFingerprint(Collection objects) { + return objects.stream() + .map(IdentifiableObject::getId) + .sorted() + .collect(Collectors.joining("-")); + } + /** * Creates a new instance of the given identifiable object type and sets the identifier based on * the given object. From 76803c82f2376674fc4b5cc09367cec940c9a8e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Tue, 27 Jan 2026 08:52:24 +0100 Subject: [PATCH 09/33] fix: Update code --- .../org/hisp/dhis/support/TestObjects.java | 8 ++++ .../util/IdentifiableObjectUtilsTest.java | 43 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/test/java/org/hisp/dhis/support/TestObjects.java b/src/test/java/org/hisp/dhis/support/TestObjects.java index b37a9b4b..4e1455f4 100644 --- a/src/test/java/org/hisp/dhis/support/TestObjects.java +++ b/src/test/java/org/hisp/dhis/support/TestObjects.java @@ -36,6 +36,14 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class TestObjects { + // Identifiers + + public static final String ID_A = "AghLTXtnopm"; + public static final String ID_B = "BLBOLoM3ZfV"; + public static final String ID_C = "CfVJ3xx9e60"; + public static final String ID_D = "D45cQxNLV7e"; + public static final String ID_E = "EjtjkvcwP6W"; + /** * Sets nameable properties on the given object. * diff --git a/src/test/java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java b/src/test/java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java index 5a728e8f..710dd5fb 100644 --- a/src/test/java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java +++ b/src/test/java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java @@ -28,8 +28,10 @@ package org.hisp.dhis.util; import static org.hisp.dhis.support.Assertions.assertContainsExactlyInOrder; +import static org.hisp.dhis.support.TestObjects.*; import static org.hisp.dhis.support.TestObjects.set; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -109,4 +111,45 @@ void testToIdObjects() { assertContainsExactlyInOrder(objects, idA, idB, idC); } + + @Test + void testGetFingerprintMatch() { + DataElement deA = set(new DataElement(), 'A'); + deA.setId(ID_A); + DataElement deB = set(new DataElement(), 'B'); + deB.setId(ID_B); + DataElement deC = set(new DataElement(), 'C'); + deC.setId(ID_C); + DataElement deD = set(new DataElement(), 'C'); + deD.setId(ID_D); + + String expected = ID_A + "-" + ID_B + "-" + ID_C; + String actualA = IdentifiableObjectUtils.getFingerprint(List.of(deB, deC, deA)); + String actualB = IdentifiableObjectUtils.getFingerprint(List.of(deA, deB, deC)); + String actualC = IdentifiableObjectUtils.getFingerprint(List.of(deC, deB, deA)); + + assertEquals(expected, actualA); + assertEquals(expected, actualB); + assertEquals(expected, actualC); + } + + @Test + void testGetFingerprintMismatch() { + DataElement deA = set(new DataElement(), 'A'); + deA.setId(ID_A); + DataElement deB = set(new DataElement(), 'B'); + deB.setId(ID_B); + DataElement deC = set(new DataElement(), 'C'); + deC.setId(ID_C); + DataElement deD = set(new DataElement(), 'C'); + deD.setId(ID_D); + + String actualA = IdentifiableObjectUtils.getFingerprint(List.of(deB, deA)); + String actualB = IdentifiableObjectUtils.getFingerprint(List.of(deA, deB, deC)); + String actualC = IdentifiableObjectUtils.getFingerprint(List.of(deB, deC)); + + assertNotEquals(actualA, actualB); + assertNotEquals(actualA, actualC); + assertNotEquals(actualB, actualC); + } } From 6a2afb81fc764ca75fabb640ee640f9de462feef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Tue, 27 Jan 2026 08:53:24 +0100 Subject: [PATCH 10/33] fix: Update code --- .../java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java b/src/test/java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java index 710dd5fb..baaad611 100644 --- a/src/test/java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java +++ b/src/test/java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java @@ -120,7 +120,7 @@ void testGetFingerprintMatch() { deB.setId(ID_B); DataElement deC = set(new DataElement(), 'C'); deC.setId(ID_C); - DataElement deD = set(new DataElement(), 'C'); + DataElement deD = set(new DataElement(), 'D'); deD.setId(ID_D); String expected = ID_A + "-" + ID_B + "-" + ID_C; From 996e4accb30d9b62e3535264e916488ce59c0748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Tue, 27 Jan 2026 08:53:42 +0100 Subject: [PATCH 11/33] fix: Update code --- .../java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java b/src/test/java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java index baaad611..9d0db173 100644 --- a/src/test/java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java +++ b/src/test/java/org/hisp/dhis/util/IdentifiableObjectUtilsTest.java @@ -141,7 +141,7 @@ void testGetFingerprintMismatch() { deB.setId(ID_B); DataElement deC = set(new DataElement(), 'C'); deC.setId(ID_C); - DataElement deD = set(new DataElement(), 'C'); + DataElement deD = set(new DataElement(), 'D'); deD.setId(ID_D); String actualA = IdentifiableObjectUtils.getFingerprint(List.of(deB, deA)); From adcd707d5c809e07fc63c07c2571a60fe329579d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Tue, 27 Jan 2026 09:05:12 +0100 Subject: [PATCH 12/33] fix: Update code --- .../org/hisp/dhis/CategoryComboApiTest.java | 18 +++++++++++++----- src/test/java/org/hisp/dhis/TestFixture.java | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/hisp/dhis/CategoryComboApiTest.java b/src/test/java/org/hisp/dhis/CategoryComboApiTest.java index 4b5498ce..9552d853 100644 --- a/src/test/java/org/hisp/dhis/CategoryComboApiTest.java +++ b/src/test/java/org/hisp/dhis/CategoryComboApiTest.java @@ -27,6 +27,7 @@ */ package org.hisp.dhis; +import static org.hisp.dhis.support.Assertions.assertNotBlank; import static org.hisp.dhis.support.Assertions.assertNotEmpty; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -35,6 +36,7 @@ import java.util.List; import org.hisp.dhis.model.Category; import org.hisp.dhis.model.CategoryCombo; +import org.hisp.dhis.model.CategoryOption; import org.hisp.dhis.model.CategoryOptionCombo; import org.hisp.dhis.model.dimension.DataDimensionType; import org.hisp.dhis.query.Query; @@ -60,12 +62,18 @@ void testGetCategoryCombo() { assertFalse(categoryCombo.getSkipTotal()); List categories = categoryCombo.getCategories(); - assertFalse(categories.isEmpty()); + assertNotEmpty(categories); List categoryOptionCombos = categoryCombo.getCategoryOptionCombos(); assertNotEmpty(categoryOptionCombos); - assertFalse(categoryOptionCombos.get(0).getIgnoreApproval()); - assertFalse(categoryOptionCombos.get(0).getCategoryOptions().isEmpty()); + + CategoryOptionCombo categoryOptionCombo = categoryOptionCombos.get(0); + assertFalse(categoryOptionCombo.getIgnoreApproval()); + assertNotEmpty(categoryOptionCombo.getCategoryOptions()); + + CategoryOption categoryOption = categoryOptionCombo.getCategoryOptions().iterator().next(); + assertNotNull(categoryOption); + assertNotBlank(categoryOption.getId()); } @Test @@ -75,7 +83,7 @@ void testGetCategoryCombos() { List categoryCombos = dhis2.getCategoryCombos(Query.instance()); assertNotNull(categoryCombos); - assertFalse(categoryCombos.isEmpty()); + assertNotEmpty(categoryCombos); CategoryCombo categoryCombo = categoryCombos.get(0); assertNotNull(categoryCombo.getId()); @@ -91,7 +99,7 @@ void testGetCategoryCombosExpandAssociations() { dhis2.getCategoryCombos(Query.instance().withExpandAssociations()); assertNotNull(categoryCombos); - assertFalse(categoryCombos.isEmpty()); + assertNotEmpty(categoryCombos); CategoryCombo categoryCombo = categoryCombos.get(0); assertNotNull(categoryCombo.getId()); diff --git a/src/test/java/org/hisp/dhis/TestFixture.java b/src/test/java/org/hisp/dhis/TestFixture.java index e38c0e6a..764b9626 100644 --- a/src/test/java/org/hisp/dhis/TestFixture.java +++ b/src/test/java/org/hisp/dhis/TestFixture.java @@ -38,7 +38,7 @@ public final class TestFixture { public static final String DEV_URL = "https://play.im.dhis2.org/dev"; - public static final String V41_URL = "https://play.im.dhis2.org/stable-2-41-6-1"; + public static final String V41_URL = "https://play.im.dhis2.org/stable-2-41-7"; public static final String V42_URL = "https://play.im.dhis2.org/stable-2-42-3-1"; From baed0e531f6cefce89a320b89c145898ef63a37d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Tue, 27 Jan 2026 16:24:54 +0100 Subject: [PATCH 13/33] fix: Update code --- src/test/java/org/hisp/dhis/BaseDhis2Test.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/hisp/dhis/BaseDhis2Test.java b/src/test/java/org/hisp/dhis/BaseDhis2Test.java index 1bd2e0ef..651c8119 100644 --- a/src/test/java/org/hisp/dhis/BaseDhis2Test.java +++ b/src/test/java/org/hisp/dhis/BaseDhis2Test.java @@ -137,8 +137,9 @@ void testWithAnalyticsQueryParams() { String decodedUrl = CodecUtils.decode(url); String expected = - """ - https://play.im.dhis2.org/stable-2-41-6-1/api/analytics\ + TestFixture.DEFAULT_URL + + """ + /api/analytics\ ?dimension=dx:fbfJHSPpUQD;cYeuwXTCPkU;Jtf34kNZhzP\ &dimension=pe:202501;202502;202503\ &filter=ou:ImspTQPwCqd\ From efe180fe3f64ac5f65ef09f533203c7fdcc2f6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Tue, 27 Jan 2026 16:26:36 +0100 Subject: [PATCH 14/33] fix: Update code --- src/test/java/org/hisp/dhis/BaseDhis2Test.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/hisp/dhis/BaseDhis2Test.java b/src/test/java/org/hisp/dhis/BaseDhis2Test.java index 651c8119..48ddec6d 100644 --- a/src/test/java/org/hisp/dhis/BaseDhis2Test.java +++ b/src/test/java/org/hisp/dhis/BaseDhis2Test.java @@ -137,16 +137,17 @@ void testWithAnalyticsQueryParams() { String decodedUrl = CodecUtils.decode(url); String expected = - TestFixture.DEFAULT_URL - + """ - /api/analytics\ + String.format( + """ + %s/api/analytics\ ?dimension=dx:fbfJHSPpUQD;cYeuwXTCPkU;Jtf34kNZhzP\ &dimension=pe:202501;202502;202503\ &filter=ou:ImspTQPwCqd\ &skipMeta=false\ &skipData=false\ &includeNumDen=true\ - &includeMetadataDetails=true"""; + &includeMetadataDetails=true""", + TestFixture.DEFAULT_URL); assertEquals(expected, decodedUrl); } From 733abaccce44fde3ee48eeaea28beb4c6bcc096c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Tue, 27 Jan 2026 16:26:46 +0100 Subject: [PATCH 15/33] fix: Update code --- src/test/java/org/hisp/dhis/BaseDhis2Test.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/hisp/dhis/BaseDhis2Test.java b/src/test/java/org/hisp/dhis/BaseDhis2Test.java index 48ddec6d..bf991e53 100644 --- a/src/test/java/org/hisp/dhis/BaseDhis2Test.java +++ b/src/test/java/org/hisp/dhis/BaseDhis2Test.java @@ -139,14 +139,14 @@ void testWithAnalyticsQueryParams() { String expected = String.format( """ - %s/api/analytics\ - ?dimension=dx:fbfJHSPpUQD;cYeuwXTCPkU;Jtf34kNZhzP\ - &dimension=pe:202501;202502;202503\ - &filter=ou:ImspTQPwCqd\ - &skipMeta=false\ - &skipData=false\ - &includeNumDen=true\ - &includeMetadataDetails=true""", + %s/api/analytics\ + ?dimension=dx:fbfJHSPpUQD;cYeuwXTCPkU;Jtf34kNZhzP\ + &dimension=pe:202501;202502;202503\ + &filter=ou:ImspTQPwCqd\ + &skipMeta=false\ + &skipData=false\ + &includeNumDen=true\ + &includeMetadataDetails=true""", TestFixture.DEFAULT_URL); assertEquals(expected, decodedUrl); From 947da72a8abfb1912e1fa15400ac6865177c9cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Tue, 27 Jan 2026 16:31:53 +0100 Subject: [PATCH 16/33] fix: Update code --- src/test/java/org/hisp/dhis/BaseDhis2Test.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/hisp/dhis/BaseDhis2Test.java b/src/test/java/org/hisp/dhis/BaseDhis2Test.java index bf991e53..1b20104f 100644 --- a/src/test/java/org/hisp/dhis/BaseDhis2Test.java +++ b/src/test/java/org/hisp/dhis/BaseDhis2Test.java @@ -104,11 +104,11 @@ void testWithMetadataImportParams() throws Exception { URI expected = new URI( """ - https://server.org/api/metadata\ - ?importStrategy=CREATE_AND_UPDATE\ - &atomicMode=ALL\ - &skipSharing=false\ - &async=false"""); + https://server.org/api/metadata\ + ?importStrategy=CREATE_AND_UPDATE\ + &atomicMode=ALL\ + &skipSharing=false\ + &async=false"""); assertEquals(expected, dhis2.withMetadataImportParams(uriBuilder)); } From 13005f56c63894bf8b7b38b9821d090920979209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Mon, 2 Feb 2026 13:18:49 +0100 Subject: [PATCH 17/33] fix: Update code --- src/main/java/org/hisp/dhis/model/OrgUnit.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/hisp/dhis/model/OrgUnit.java b/src/main/java/org/hisp/dhis/model/OrgUnit.java index b8d67905..c64c4b11 100644 --- a/src/main/java/org/hisp/dhis/model/OrgUnit.java +++ b/src/main/java/org/hisp/dhis/model/OrgUnit.java @@ -92,4 +92,13 @@ public OrgUnit(String id, String name, String shortName, OrgUnit parent, Date op public DimensionItemType getDimensionItemType() { return DimensionItemType.ORGANISATION_UNIT; } + + /** + * Indicates whether this org unit has a parent. + * + * @return true if this org unit has a parent, false otherwise. + */ + public boolean hasParent() { + return parent != null; + } } From 6adf1d16f542753f8da2b3f69a808aa42d73253b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Wed, 18 Feb 2026 09:41:47 +0100 Subject: [PATCH 18/33] fix: Update code --- .../java/org/hisp/dhis/util/UidUtils.java | 24 +++++++++++++++---- .../java/org/hisp/dhis/util/UidUtilsTest.java | 10 ++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/hisp/dhis/util/UidUtils.java b/src/main/java/org/hisp/dhis/util/UidUtils.java index f24f1758..1a8f9222 100644 --- a/src/main/java/org/hisp/dhis/util/UidUtils.java +++ b/src/main/java/org/hisp/dhis/util/UidUtils.java @@ -78,13 +78,29 @@ public static List generateUids(int n) { } /** - * Tests whether the given code is a valid UID. + * Tests whether the given input is a valid UID. * - * @param code the code to validate. + * @param input the input to validate. * @return true if the code is valid. */ - public static boolean isValidUid(String code) { - return code != null && UID_PATTERN.matcher(code).matches(); + public static boolean isValidUid(String input) { + return input != null && UID_PATTERN.matcher(input).matches(); + } + + /** + * Tests whether the given input is a valid UID. Throws an {@link IllegalArgumentException} if + * not. + * + * @param input the input to validate. + * @return the input UID if valid. + * @throws IllegalArgumentException if input is not a valid UID. + */ + public static String requireUid(String input) { + if (!isValidUid(input)) { + String message = String.format("Input must be a valid UID: '%s'", input); + throw new IllegalArgumentFormatException(message); + } + return input; } /** diff --git a/src/test/java/org/hisp/dhis/util/UidUtilsTest.java b/src/test/java/org/hisp/dhis/util/UidUtilsTest.java index be311d03..de9e81d9 100644 --- a/src/test/java/org/hisp/dhis/util/UidUtilsTest.java +++ b/src/test/java/org/hisp/dhis/util/UidUtilsTest.java @@ -31,6 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.stream.IntStream; @@ -64,6 +65,15 @@ void testUidIsValid() { assertFalse(UidUtils.isValidUid("1T1hdS_WjfD")); } + @Test + void testRequireUid() { + assertEquals("mq4jAnN6fg3", UidUtils.requireUid("mq4jAnN6fg3")); + assertEquals("iz9HDQXFDrQ", UidUtils.requireUid("iz9HDQXFDrQ")); + + assertThrows(IllegalArgumentException.class, () -> UidUtils.requireUid("1T1hdSWjfDC")); + assertThrows(IllegalArgumentException.class, () -> UidUtils.requireUid("QX4LpiTZmUHg")); + } + @Test void testToUid() { assertToUid("PpZ!m3thN#sm8QVcOdwTcil4"); From a2124a4b50bbf3d91e637e3a9f71ffcdfb643bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Wed, 18 Feb 2026 09:43:45 +0100 Subject: [PATCH 19/33] fix: Update code --- src/main/java/org/hisp/dhis/util/UidUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/hisp/dhis/util/UidUtils.java b/src/main/java/org/hisp/dhis/util/UidUtils.java index 1a8f9222..fa000053 100644 --- a/src/main/java/org/hisp/dhis/util/UidUtils.java +++ b/src/main/java/org/hisp/dhis/util/UidUtils.java @@ -98,7 +98,7 @@ public static boolean isValidUid(String input) { public static String requireUid(String input) { if (!isValidUid(input)) { String message = String.format("Input must be a valid UID: '%s'", input); - throw new IllegalArgumentFormatException(message); + throw new IllegalArgumentException(message); } return input; } From 1b3b4ff53528e773625dfe5d83c94adf5ef3a5e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Mon, 23 Feb 2026 22:27:13 +0100 Subject: [PATCH 20/33] fix: Update code --- .../java/org/hisp/dhis/util/UidUtils.java | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/hisp/dhis/util/UidUtils.java b/src/main/java/org/hisp/dhis/util/UidUtils.java index fa000053..cad3b7ea 100644 --- a/src/main/java/org/hisp/dhis/util/UidUtils.java +++ b/src/main/java/org/hisp/dhis/util/UidUtils.java @@ -31,27 +31,30 @@ import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.util.List; -import java.util.concurrent.ThreadLocalRandom; import java.util.regex.Pattern; import java.util.stream.IntStream; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; import org.hisp.dhis.model.exception.IllegalArgumentFormatException; /** Utilities for DHIS2 UID generation. */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class UidUtils { - private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; private static final String DIGITS = "0123456789"; - private static final String ALLOWED_CHARS = ALPHABET + DIGITS; + private static final String ALPHANUMERIC = LETTERS + DIGITS; - private static final int CHAR_LENGTH = ALLOWED_CHARS.length(); private static final int UID_LENGTH = 11; private static final Pattern UID_PATTERN = Pattern.compile("^[a-zA-Z]{1}[a-zA-Z0-9]{10}$"); + /** Random generator which is thread-safe and provides high-quality entropy. */ + private static final SecureRandom RANDOM = new SecureRandom(); + /** * Generates a DHIS2 UID according to the following rules. * @@ -104,24 +107,27 @@ public static String requireUid(String input) { } /** - * Generates a pseudo random string with alphanumeric characters. + * Generates a random string with alphanumeric characters. * * @param length the number of characters in the code. * @return the code. */ public static String generateCode(int length) { - ThreadLocalRandom rand = ThreadLocalRandom.current(); + Validate.inclusiveBetween(1, 10_000, length); - char[] randomChars = new char[length]; + StringBuilder sb = new StringBuilder(length); - // First char must be a letter - randomChars[0] = ALPHABET.charAt(rand.nextInt(ALPHABET.length())); + // First character must be a letter + int firstCharIndex = RANDOM.nextInt(LETTERS.length()); + sb.append(LETTERS.charAt(firstCharIndex)); - for (int i = 1; i < length; ++i) { - randomChars[i] = ALLOWED_CHARS.charAt(rand.nextInt(CHAR_LENGTH)); + // Remaining 10 characters are alphanumeric + for (int i = 1; i < length; i++) { + int index = RANDOM.nextInt(ALPHANUMERIC.length()); + sb.append(ALPHANUMERIC.charAt(index)); } - return new String(randomChars); + return sb.toString(); } /** @@ -154,13 +160,13 @@ public static String toUid(String input) { BigInteger bigInteger = new BigInteger(1, hashBytes); // Convert BigInteger to Base62 - String base62 = fromBigInteger(bigInteger, ALPHABET, UID_LENGTH); + String base62 = fromBigInteger(bigInteger, LETTERS, UID_LENGTH); // Ensure the UID starts with a letter if (Character.isDigit(base62.charAt(0))) { // If first character is a digit, shift Base62 string by one character by moving first char // to the end and append 'A' - base62 = base62.substring(1) + ALPHABET.charAt(0); + base62 = base62.substring(1) + LETTERS.charAt(0); } return base62; From 72e2b9c8ca7704ad878912d2e99faf31b63b1699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Wed, 25 Feb 2026 17:32:00 +0100 Subject: [PATCH 21/33] fix: Update code --- .../java/org/hisp/dhis/api/ApiFields.java | 6 +- .../org/hisp/dhis/model/AnalyticsType.java | 33 +++++++++++ .../org/hisp/dhis/model/ProgramIndicator.java | 13 +++++ .../hisp/dhis/ProgramIndicatorApiTest.java | 58 +++++++++++++++++++ 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/hisp/dhis/model/AnalyticsType.java create mode 100644 src/test/java/org/hisp/dhis/ProgramIndicatorApiTest.java diff --git a/src/main/java/org/hisp/dhis/api/ApiFields.java b/src/main/java/org/hisp/dhis/api/ApiFields.java index fed3910d..cfba63e0 100644 --- a/src/main/java/org/hisp/dhis/api/ApiFields.java +++ b/src/main/java/org/hisp/dhis/api/ApiFields.java @@ -332,7 +332,11 @@ public class ApiFields { ID_EXT_FIELDS); /** Program indicator fields. */ - public static final String PROGRAM_INDICATOR_FIELDS = NAME_FIELDS; + public static final String PROGRAM_INDICATOR_FIELDS = + String.format( + """ + %1$s,program[%1$s],expression,filter,decimals,aggregationType,analyticsType""", + NAME_FIELDS); /** Program section fields. */ public static final String PROGRAM_SECTION_FIELDS = diff --git a/src/main/java/org/hisp/dhis/model/AnalyticsType.java b/src/main/java/org/hisp/dhis/model/AnalyticsType.java new file mode 100644 index 00000000..947b6d32 --- /dev/null +++ b/src/main/java/org/hisp/dhis/model/AnalyticsType.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2004-2026, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.model; + +public enum AnalyticsType { + EVENT, + ENROLLMENT; +} diff --git a/src/main/java/org/hisp/dhis/model/ProgramIndicator.java b/src/main/java/org/hisp/dhis/model/ProgramIndicator.java index d5741c2d..59a47ce4 100644 --- a/src/main/java/org/hisp/dhis/model/ProgramIndicator.java +++ b/src/main/java/org/hisp/dhis/model/ProgramIndicator.java @@ -27,6 +27,7 @@ */ package org.hisp.dhis.model; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -37,6 +38,18 @@ @Setter @NoArgsConstructor public class ProgramIndicator extends DimensionItem { + @JsonProperty private Program program; + + @JsonProperty private String expression; + + @JsonProperty private String filter; + + @JsonProperty private Integer decimals; + + @JsonProperty private AggregationType aggregationType; + + @JsonProperty private AnalyticsType analyticsType; + @Override public DimensionItemType getDimensionItemType() { return DimensionItemType.PROGRAM_INDICATOR; diff --git a/src/test/java/org/hisp/dhis/ProgramIndicatorApiTest.java b/src/test/java/org/hisp/dhis/ProgramIndicatorApiTest.java new file mode 100644 index 00000000..94a1e098 --- /dev/null +++ b/src/test/java/org/hisp/dhis/ProgramIndicatorApiTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2004-2026, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis; + +import static org.hisp.dhis.support.Assertions.assertNotBlank; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.hisp.dhis.model.ProgramIndicator; +import org.hisp.dhis.support.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +@Tag(TestTags.INTEGRATION) +class ProgramIndicatorApiTest { + @Test + void testGetProgramIndicator() { + Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG); + + ProgramIndicator programIndicator = dhis2.getProgramIndicator("GSae40Fyppf"); + + assertNotNull(programIndicator); + assertNotNull("GSae40Fyppf", programIndicator.getId()); + assertNotBlank(programIndicator.getName()); + assertNotBlank(programIndicator.getShortName()); + assertNotNull(programIndicator.getProgram()); + assertEquals("uy2gU8kT1jF", programIndicator.getProgram().getId()); + assertNotBlank(programIndicator.getExpression()); + assertNotBlank(programIndicator.getFilter()); + assertNotNull(programIndicator.getAggregationType()); + assertNotNull(programIndicator.getAnalyticsType()); + } +} From b296677e9b826661a13075d45044041fb845eaaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Wed, 25 Feb 2026 17:33:58 +0100 Subject: [PATCH 22/33] fix: Update code --- src/main/java/org/hisp/dhis/api/ApiFields.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/hisp/dhis/api/ApiFields.java b/src/main/java/org/hisp/dhis/api/ApiFields.java index cfba63e0..0513a23d 100644 --- a/src/main/java/org/hisp/dhis/api/ApiFields.java +++ b/src/main/java/org/hisp/dhis/api/ApiFields.java @@ -334,8 +334,7 @@ public class ApiFields { /** Program indicator fields. */ public static final String PROGRAM_INDICATOR_FIELDS = String.format( - """ - %1$s,program[%1$s],expression,filter,decimals,aggregationType,analyticsType""", + "%1$s,program[%1$s],expression,filter,decimals,aggregationType,analyticsType", NAME_FIELDS); /** Program section fields. */ From 151ed4ff29580d8c458dccd18881594ee332f6cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Wed, 25 Feb 2026 18:01:43 +0100 Subject: [PATCH 23/33] chore: Bump version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2e768a9b..30385a73 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.hisp dhis2-java-client - 2.5.1 + 2.5.2 jar DHIS 2 API client for Java From cc0ac36a20a71270aad6ae2bd2d3f88cd805098e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Thu, 26 Feb 2026 22:23:18 +0100 Subject: [PATCH 24/33] fix: Add test --- .../java/org/hisp/dhis/DataElementApiTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/test/java/org/hisp/dhis/DataElementApiTest.java b/src/test/java/org/hisp/dhis/DataElementApiTest.java index 9a400e6e..c546c13f 100644 --- a/src/test/java/org/hisp/dhis/DataElementApiTest.java +++ b/src/test/java/org/hisp/dhis/DataElementApiTest.java @@ -33,6 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.List; import java.util.Set; import org.hisp.dhis.model.AggregationType; import org.hisp.dhis.model.CategoryCombo; @@ -47,6 +48,7 @@ import org.hisp.dhis.model.sharing.UserAccess; import org.hisp.dhis.model.sharing.UserGroupAccess; import org.hisp.dhis.model.translation.Translation; +import org.hisp.dhis.query.Filter; import org.hisp.dhis.query.Query; import org.hisp.dhis.response.HttpStatus; import org.hisp.dhis.response.Status; @@ -82,6 +84,21 @@ void testGetDataElement() { assertNotNull(optionSet.getValueType()); } + @Test + void testGetDataElementsWithGroupFilter() { + Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG); + + List dataElements = + dhis2.getDataElements( + Query.instance().addFilter(Filter.eq("dataElementGroups.id", "qfxEYY9xAl6"))); + + assertNotEmpty(dataElements); + + DataElement dataElement = dataElements.get(0); + + assertNotNull(dataElement); + } + @Test void testGetDataElementWithSharingAndTranslations() { Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG); From 7f2016ff22306d74763e81a7e8f4a96dbfbeb1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Thu, 26 Feb 2026 22:30:16 +0100 Subject: [PATCH 25/33] fix: Update code --- .../java/org/hisp/dhis/DataElementApiTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/test/java/org/hisp/dhis/DataElementApiTest.java b/src/test/java/org/hisp/dhis/DataElementApiTest.java index c546c13f..2fd69f1d 100644 --- a/src/test/java/org/hisp/dhis/DataElementApiTest.java +++ b/src/test/java/org/hisp/dhis/DataElementApiTest.java @@ -99,6 +99,23 @@ void testGetDataElementsWithGroupFilter() { assertNotNull(dataElement); } + @Test + void testGetDataElementsWithGroupsFilter() { + Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG); + + List dataElements = + dhis2.getDataElements( + Query.instance() + .addFilter( + Filter.in("dataElementGroups.id", List.of("qfxEYY9xAl6", "h9cuJOkOwY2")))); + + assertNotEmpty(dataElements); + + DataElement dataElement = dataElements.get(0); + + assertNotNull(dataElement); + } + @Test void testGetDataElementWithSharingAndTranslations() { Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG); From 7c02d2072ee9aa60fe1086e0e8057afc38b15841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Thu, 26 Feb 2026 22:43:26 +0100 Subject: [PATCH 26/33] fix: Update code --- .../java/org/hisp/dhis/IndicatorApiTest.java | 17 ++++++++++++++ .../hisp/dhis/ProgramIndicatorApiTest.java | 23 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/hisp/dhis/IndicatorApiTest.java b/src/test/java/org/hisp/dhis/IndicatorApiTest.java index 55eeae50..43d9ceb4 100644 --- a/src/test/java/org/hisp/dhis/IndicatorApiTest.java +++ b/src/test/java/org/hisp/dhis/IndicatorApiTest.java @@ -78,6 +78,23 @@ void testGetIndicator() { assertEquals("ANC 1st visit total", indicator.getDenominatorDescription()); } + @Test + void testGetIndicatorsWithGroupsFilter() { + Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG); + + Query query = + Query.instance() + .addFilter(Filter.in("indicatorGroups.id", List.of("oehv9EO3vP7", "srxUNt2w5jn"))); + + List indicators = dhis2.getIndicators(query); + + assertNotEmpty(indicators); + + Indicator indicator = indicators.get(0); + + assertNotNull(indicator); + } + @Test void testGetIndicatorsWithInFilter() { Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG); diff --git a/src/test/java/org/hisp/dhis/ProgramIndicatorApiTest.java b/src/test/java/org/hisp/dhis/ProgramIndicatorApiTest.java index 94a1e098..851d847b 100644 --- a/src/test/java/org/hisp/dhis/ProgramIndicatorApiTest.java +++ b/src/test/java/org/hisp/dhis/ProgramIndicatorApiTest.java @@ -28,10 +28,14 @@ package org.hisp.dhis; import static org.hisp.dhis.support.Assertions.assertNotBlank; +import static org.hisp.dhis.support.Assertions.assertNotEmpty; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.util.List; import org.hisp.dhis.model.ProgramIndicator; +import org.hisp.dhis.query.Filter; +import org.hisp.dhis.query.Query; import org.hisp.dhis.support.TestTags; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -51,8 +55,25 @@ void testGetProgramIndicator() { assertNotNull(programIndicator.getProgram()); assertEquals("uy2gU8kT1jF", programIndicator.getProgram().getId()); assertNotBlank(programIndicator.getExpression()); - assertNotBlank(programIndicator.getFilter()); assertNotNull(programIndicator.getAggregationType()); assertNotNull(programIndicator.getAnalyticsType()); } + + @Test + void testGetProgramIndicatorsWithGroupsFilter() { + Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG); + + Query query = + Query.instance() + .addFilter( + Filter.in("programIndicatorGroups.id", List.of("xxkcH5TCVRF", "E2ZBxdQLNDm"))); + + List indicators = dhis2.getProgramIndicators(query); + + assertNotEmpty(indicators); + + ProgramIndicator indicator = indicators.get(0); + + assertNotNull(indicator); + } } From a7568f50dac54dcfedfef225384137eb6cc950e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Wed, 4 Mar 2026 09:48:29 +0100 Subject: [PATCH 27/33] fix: Update code --- .../java/org/hisp/dhis/util/JacksonUtils.java | 16 ++++++++++ .../org/hisp/dhis/util/JacksonUtilsTest.java | 31 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/main/java/org/hisp/dhis/util/JacksonUtils.java b/src/main/java/org/hisp/dhis/util/JacksonUtils.java index 6e27e08d..cf9f3a3e 100644 --- a/src/main/java/org/hisp/dhis/util/JacksonUtils.java +++ b/src/main/java/org/hisp/dhis/util/JacksonUtils.java @@ -42,6 +42,7 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; +import java.util.Set; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.hisp.dhis.util.json.DateJsonDeserializer; @@ -198,6 +199,21 @@ public static List fromJsonToList(String string) { } } + /** + * Deserializes the given JSON string into a set of objects of the specified type. + * + * @param string the JSON string to deserialize. + * @param the type of the set items to return. + * @return an set of items of type T. + */ + public static Set fromJsonToSet(String string) { + try { + return OBJECT_MAPPER.readValue(string, new TypeReference>() {}); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + /** * Deserializes JSON from the given {@link InputStream} into an object of the specified type. * diff --git a/src/test/java/org/hisp/dhis/util/JacksonUtilsTest.java b/src/test/java/org/hisp/dhis/util/JacksonUtilsTest.java index ba9eb40e..26e3a000 100644 --- a/src/test/java/org/hisp/dhis/util/JacksonUtilsTest.java +++ b/src/test/java/org/hisp/dhis/util/JacksonUtilsTest.java @@ -36,6 +36,7 @@ import java.time.Month; import java.util.Date; import java.util.List; +import java.util.Set; import org.hisp.dhis.model.AggregationType; import org.hisp.dhis.model.DataDomain; import org.hisp.dhis.model.DataElement; @@ -246,6 +247,36 @@ void testFromJsonToIntegerList() { assertEquals(expected, list); } + @Test + void testFromJsonToStringSet() { + String content = + """ + ["s46m5MS0hxu", "YtbsuPPo010", "l6byfWFUGaP"] + """; + + Set list = JacksonUtils.fromJsonToSet(content); + + Set expected = Set.of("s46m5MS0hxu", "YtbsuPPo010", "l6byfWFUGaP"); + + assertSize(3, list); + assertEquals(expected, list); + } + + @Test + void testFromJsonToIntegerSet() { + String content = + """ + [1, 2, 3, 4, 5] + """; + + Set list = JacksonUtils.fromJsonToSet(content); + + Set expected = Set.of(1, 2, 3, 4, 5); + + assertSize(5, list); + assertEquals(expected, list); + } + /** * Returns a {@link DataElement}. * From 9fc6e7a4d230202dbb089b8646336d3605baf023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Fri, 6 Mar 2026 11:47:56 +0100 Subject: [PATCH 28/33] fix: Update code --- .../org/hisp/dhis/util/CollectionUtils.java | 44 ++++++++++++++++--- .../hisp/dhis/util/CollectionUtilsTest.java | 35 +++++++++++++++ 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/hisp/dhis/util/CollectionUtils.java b/src/main/java/org/hisp/dhis/util/CollectionUtils.java index 62ce81c1..ad105b3a 100644 --- a/src/main/java/org/hisp/dhis/util/CollectionUtils.java +++ b/src/main/java/org/hisp/dhis/util/CollectionUtils.java @@ -41,6 +41,7 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.IntStream; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -117,8 +118,8 @@ public static List mutableList(T... items) { * @return a sublist. */ public static List sublist(List items, int length) { - if (items == null || items.isEmpty() || length <= 0) { - return Collections.emptyList(); + if (empty(items) || length <= 0) { + return List.of(); } if (items.size() <= length) { @@ -281,6 +282,25 @@ public static Set filterToSet(Collection collection, Predicate pred return collection.stream().filter(predicate).collect(Collectors.toUnmodifiableSet()); } + /** + * Returns a new list of the items in the given list which are not at the given indexes. + * + * @param type. + * @param list the list. + * @param indexes the indexes to exclude. + * @return a new list of the items in the given list which are not at the given indexes. + */ + public static List excludeIndexes(List list, Set indexes) { + if (empty(list) || empty(indexes)) { + return List.of(); + } + + return IntStream.range(0, list.size()) + .filter(i -> !indexes.contains(i)) + .mapToObj(list::get) + .collect(Collectors.toList()); + } + /** * Indicates if any item in the given collection is a prefix of the given input string. * @@ -289,7 +309,7 @@ public static Set filterToSet(Collection collection, Predicate pred * @return true if any item is a prefix, false otherwise. */ public static boolean anyStartsWith(Collection collection, String input) { - if (input == null) { + if (empty(collection) || input == null) { return false; } @@ -326,6 +346,17 @@ public static Map index(Collection collection, Function ke return map; } + /** + * Indicates if the given collection is null or empty. + * + * @param type. + * @param collection the collection. + * @return true if the given collection is null or empty. + */ + public static boolean empty(Collection collection) { + return collection == null || collection.isEmpty(); + } + /** * Indicates if the given collection is not null and not empty. * @@ -363,9 +394,7 @@ public static T get(List list, int index) { * @return an optional first item in the given collection. */ public static Optional first(Collection collection) { - return collection == null || collection.isEmpty() - ? Optional.empty() - : Optional.ofNullable(collection.iterator().next()); + return empty(collection) ? Optional.empty() : Optional.ofNullable(collection.iterator().next()); } /** @@ -377,9 +406,10 @@ public static Optional first(Collection collection) { * @return a comma separated string. */ public static String toCommaSeparated(Collection collection) { - if (collection == null || collection.isEmpty()) { + if (empty(collection)) { return null; } + return collection.stream().map(Object::toString).collect(Collectors.joining(",")); } } diff --git a/src/test/java/org/hisp/dhis/util/CollectionUtilsTest.java b/src/test/java/org/hisp/dhis/util/CollectionUtilsTest.java index 59b08f7a..039ff8a1 100644 --- a/src/test/java/org/hisp/dhis/util/CollectionUtilsTest.java +++ b/src/test/java/org/hisp/dhis/util/CollectionUtilsTest.java @@ -30,6 +30,8 @@ import static org.hisp.dhis.support.Assertions.assertContainsExactly; import static org.hisp.dhis.support.Assertions.assertEmpty; import static org.hisp.dhis.util.CollectionUtils.anyStartsWith; +import static org.hisp.dhis.util.CollectionUtils.empty; +import static org.hisp.dhis.util.CollectionUtils.excludeIndexes; import static org.hisp.dhis.util.CollectionUtils.filterToList; import static org.hisp.dhis.util.CollectionUtils.filterToSet; import static org.hisp.dhis.util.CollectionUtils.first; @@ -44,6 +46,7 @@ import static org.hisp.dhis.util.CollectionUtils.mapToSet; import static org.hisp.dhis.util.CollectionUtils.mutableList; import static org.hisp.dhis.util.CollectionUtils.mutableSet; +import static org.hisp.dhis.util.CollectionUtils.notEmpty; import static org.hisp.dhis.util.CollectionUtils.set; import static org.hisp.dhis.util.CollectionUtils.sublist; import static org.hisp.dhis.util.CollectionUtils.toCommaSeparated; @@ -128,6 +131,23 @@ void testFilterToSet() { filterToSet(list, p -> mutableSet(null, "P01", "P02").contains(p.getId())), pA, pB); } + @Test + void testExcludeIndexes() { + Product pA = new Product("P01", "Keyboard"); + Product pB = new Product("P02", "Mouse"); + Product pC = new Product("P03", "Monitor"); + Product pD = new Product("P04", "Printer"); + Product pE = new Product("P05", "Scanner"); + + List list = List.of(pA, pB, pC, pD, pE); + + assertContainsExactly(excludeIndexes(list, Set.of(1, 3, 4)), pA, pC); + assertContainsExactly(excludeIndexes(list, Set.of(2, 4)), pA, pB, pD); + assertEmpty(excludeIndexes(list, Set.of(0, 1, 2, 3, 4))); + assertEmpty(excludeIndexes(null, Set.of(0, 2))); + assertEmpty(excludeIndexes(list, null)); + } + @Test void testAnyStartsWith() { List list = @@ -142,6 +162,7 @@ void testAnyStartsWith() { assertFalse(anyStartsWith(list, "/ImspTQPwCqd/at6UHUQatSo/qtr8GGlm4gg")); assertFalse(anyStartsWith(list, "/ImspTQPwCqd/PMa2VCrupOd/FlBemv1NfEC/rxc497GUdDt")); assertFalse(anyStartsWith(list, null)); + assertFalse(anyStartsWith(null, "/MpcMjLmbATv")); } @Test @@ -387,4 +408,18 @@ void testToCommaSeparated() { "jUb6fnbZPhV,qEiCafULhoW,wOahXFjLq4V", toCommaSeparated(List.of("jUb6fnbZPhV", "qEiCafULhoW", "wOahXFjLq4V"))); } + + @Test + void testEmpty() { + assertTrue(empty(List.of())); + assertTrue(empty(null)); + assertFalse(empty(List.of("a"))); + } + + @Test + void testNotEmpty() { + assertFalse(notEmpty(List.of())); + assertFalse(notEmpty(null)); + assertTrue(notEmpty(List.of("a"))); + } } From a537088383f9f346a8c982c77c9eaf5fdbea9f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Fri, 6 Mar 2026 12:29:46 +0100 Subject: [PATCH 29/33] fix: Update code --- src/main/java/org/hisp/dhis/model/analytics/AnalyticsData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/hisp/dhis/model/analytics/AnalyticsData.java b/src/main/java/org/hisp/dhis/model/analytics/AnalyticsData.java index aaa2c9d9..e075f83b 100644 --- a/src/main/java/org/hisp/dhis/model/analytics/AnalyticsData.java +++ b/src/main/java/org/hisp/dhis/model/analytics/AnalyticsData.java @@ -59,7 +59,7 @@ public class AnalyticsData implements Serializable { /** Analytics data rows. */ @JsonProperty private List> rows; - /** Whether the data rows were truncatd to max limit. */ + /** Whether the data rows were truncated to max limit. */ @JsonProperty private boolean truncated; /** Default constructor. */ From c3b96d4af4d695cc54ed51b972dd62a7ace6ef27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Fri, 6 Mar 2026 12:48:41 +0100 Subject: [PATCH 30/33] fix: Update code --- src/main/java/org/hisp/dhis/util/CollectionUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/hisp/dhis/util/CollectionUtils.java b/src/main/java/org/hisp/dhis/util/CollectionUtils.java index ad105b3a..96ebb8cf 100644 --- a/src/main/java/org/hisp/dhis/util/CollectionUtils.java +++ b/src/main/java/org/hisp/dhis/util/CollectionUtils.java @@ -298,7 +298,7 @@ public static List excludeIndexes(List list, Set indexes) { return IntStream.range(0, list.size()) .filter(i -> !indexes.contains(i)) .mapToObj(list::get) - .collect(Collectors.toList()); + .toList(); } /** From ab9a0ae75d730dded09ff2a03e5ef6a545806375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Sat, 7 Mar 2026 14:49:53 +0100 Subject: [PATCH 31/33] fix: Update code --- .../java/org/hisp/dhis/model/analytics/AnalyticsData.java | 2 ++ src/main/java/org/hisp/dhis/util/CollectionUtils.java | 4 ++-- src/test/java/org/hisp/dhis/util/CollectionUtilsTest.java | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/hisp/dhis/model/analytics/AnalyticsData.java b/src/main/java/org/hisp/dhis/model/analytics/AnalyticsData.java index e075f83b..68f990d5 100644 --- a/src/main/java/org/hisp/dhis/model/analytics/AnalyticsData.java +++ b/src/main/java/org/hisp/dhis/model/analytics/AnalyticsData.java @@ -45,10 +45,12 @@ import java.util.stream.Collectors; import lombok.Getter; import lombok.Setter; +import lombok.ToString; import org.apache.commons.lang3.StringUtils; @Getter @Setter +@ToString public class AnalyticsData implements Serializable { /** Analytics column headers. */ @JsonProperty private List headers; diff --git a/src/main/java/org/hisp/dhis/util/CollectionUtils.java b/src/main/java/org/hisp/dhis/util/CollectionUtils.java index 96ebb8cf..6d6ec08d 100644 --- a/src/main/java/org/hisp/dhis/util/CollectionUtils.java +++ b/src/main/java/org/hisp/dhis/util/CollectionUtils.java @@ -291,8 +291,8 @@ public static Set filterToSet(Collection collection, Predicate pred * @return a new list of the items in the given list which are not at the given indexes. */ public static List excludeIndexes(List list, Set indexes) { - if (empty(list) || empty(indexes)) { - return List.of(); + if (empty(indexes)) { + return list; } return IntStream.range(0, list.size()) diff --git a/src/test/java/org/hisp/dhis/util/CollectionUtilsTest.java b/src/test/java/org/hisp/dhis/util/CollectionUtilsTest.java index 039ff8a1..ee4175e2 100644 --- a/src/test/java/org/hisp/dhis/util/CollectionUtilsTest.java +++ b/src/test/java/org/hisp/dhis/util/CollectionUtilsTest.java @@ -144,8 +144,10 @@ void testExcludeIndexes() { assertContainsExactly(excludeIndexes(list, Set.of(1, 3, 4)), pA, pC); assertContainsExactly(excludeIndexes(list, Set.of(2, 4)), pA, pB, pD); assertEmpty(excludeIndexes(list, Set.of(0, 1, 2, 3, 4))); - assertEmpty(excludeIndexes(null, Set.of(0, 2))); - assertEmpty(excludeIndexes(list, null)); + assertEquals(list, excludeIndexes(list, Set.of())); + assertEquals(list, excludeIndexes(list, null)); + assertEmpty(excludeIndexes(List.of(), Set.of(0, 2))); + assertEmpty(excludeIndexes(List.of(), Set.of())); } @Test From 437749e257b1a2e664021d0e438f338a55667dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Helge=20=C3=98verland?= Date: Sun, 8 Mar 2026 22:16:42 +0100 Subject: [PATCH 32/33] fix: Update code --- .../java/org/hisp/dhis/model/AttributeValue.java | 3 ++- .../java/org/hisp/dhis/model/DataSetElement.java | 3 ++- src/main/java/org/hisp/dhis/model/Dhis2Objects.java | 3 ++- src/main/java/org/hisp/dhis/model/IdScheme.java | 3 ++- .../java/org/hisp/dhis/model/OptionSetObjects.java | 3 ++- .../java/org/hisp/dhis/model/ProgramObjects.java | 3 ++- src/main/java/org/hisp/dhis/model/SystemInfo.java | 3 ++- .../java/org/hisp/dhis/model/SystemSettings.java | 3 ++- .../dhis/model/analytics/AnalyticsDimension.java | 3 ++- .../hisp/dhis/model/analytics/AnalyticsKeyword.java | 3 ++- .../CompleteDataSetRegistration.java | 3 ++- .../CompleteDataSetRegistrationImportOptions.java | 3 ++- .../org/hisp/dhis/model/dashboard/Dashboard.java | 1 - .../hisp/dhis/model/datastore/DataStoreEntries.java | 3 ++- .../hisp/dhis/model/datastore/EntryMetadata.java | 3 ++- .../org/hisp/dhis/model/enrollment/Enrollment.java | 3 ++- .../dhis/model/enrollment/EnrollmentsResult.java | 3 ++- .../hisp/dhis/model/relationship/Relationship.java | 3 ++- .../dhis/model/relationship/RelationshipItem.java | 3 ++- .../model/relationship/RelationshipsResult.java | 3 ++- .../dhis/model/trackedentity/TrackedEntity.java | 3 ++- .../trackedentity/TrackedEntityAttributeValue.java | 3 ++- .../trackedentity/TrackedEntityTypeAttribute.java | 3 ++- .../dhis/model/tracker/TrackedEntityObjects.java | 3 ++- .../java/org/hisp/dhis/model/user/UserMetadata.java | 3 ++- .../java/org/hisp/dhis/model/user/UserSettings.java | 3 ++- .../org/hisp/dhis/model/validation/Validation.java | 3 ++- .../model/validation/ValidationRuleViolation.java | 13 ++++++++++++- .../hisp/dhis/model/validation/ValidationSide.java | 9 ++++++++- 29 files changed, 72 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/hisp/dhis/model/AttributeValue.java b/src/main/java/org/hisp/dhis/model/AttributeValue.java index 551c07cf..e2a212f0 100644 --- a/src/main/java/org/hisp/dhis/model/AttributeValue.java +++ b/src/main/java/org/hisp/dhis/model/AttributeValue.java @@ -29,6 +29,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; import java.util.Objects; import lombok.Getter; import lombok.NoArgsConstructor; @@ -39,7 +40,7 @@ @Getter @Setter @NoArgsConstructor -public class AttributeValue { +public class AttributeValue implements Serializable { @JsonProperty private Attribute attribute; @JsonProperty private String value; diff --git a/src/main/java/org/hisp/dhis/model/DataSetElement.java b/src/main/java/org/hisp/dhis/model/DataSetElement.java index 03de30a7..438ba19c 100644 --- a/src/main/java/org/hisp/dhis/model/DataSetElement.java +++ b/src/main/java/org/hisp/dhis/model/DataSetElement.java @@ -28,6 +28,7 @@ package org.hisp.dhis.model; import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -35,7 +36,7 @@ @Getter @Setter @NoArgsConstructor -public class DataSetElement { +public class DataSetElement implements Serializable { @JsonProperty private CategoryCombo categoryCombo; @JsonProperty private DataSet dataSet; diff --git a/src/main/java/org/hisp/dhis/model/Dhis2Objects.java b/src/main/java/org/hisp/dhis/model/Dhis2Objects.java index a626addf..f9341edf 100644 --- a/src/main/java/org/hisp/dhis/model/Dhis2Objects.java +++ b/src/main/java/org/hisp/dhis/model/Dhis2Objects.java @@ -28,6 +28,7 @@ package org.hisp.dhis.model; import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; import lombok.Getter; @@ -55,7 +56,7 @@ @Setter @Accessors(chain = true) @NoArgsConstructor -public class Dhis2Objects { +public class Dhis2Objects implements Serializable { @JsonProperty private List analyticsTableHooks = new ArrayList<>(); @JsonProperty private List attributes = new ArrayList<>(); diff --git a/src/main/java/org/hisp/dhis/model/IdScheme.java b/src/main/java/org/hisp/dhis/model/IdScheme.java index ac4037d6..0927bd90 100644 --- a/src/main/java/org/hisp/dhis/model/IdScheme.java +++ b/src/main/java/org/hisp/dhis/model/IdScheme.java @@ -27,13 +27,14 @@ */ package org.hisp.dhis.model; +import java.io.Serializable; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang3.Validate; import org.hisp.dhis.model.exception.IllegalArgumentFormatException; -public class IdScheme { +public class IdScheme implements Serializable { public static final IdScheme UID = new IdScheme(ObjectProperty.UID); public static final IdScheme CODE = new IdScheme(ObjectProperty.CODE); diff --git a/src/main/java/org/hisp/dhis/model/OptionSetObjects.java b/src/main/java/org/hisp/dhis/model/OptionSetObjects.java index b714412d..0527953d 100644 --- a/src/main/java/org/hisp/dhis/model/OptionSetObjects.java +++ b/src/main/java/org/hisp/dhis/model/OptionSetObjects.java @@ -30,6 +30,7 @@ import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; import lombok.Getter; @@ -41,7 +42,7 @@ @Setter @Accessors(chain = true) @NoArgsConstructor -public class OptionSetObjects { +public class OptionSetObjects implements Serializable { @JsonProperty private List optionSets = new ArrayList<>(); @JsonProperty private List