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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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; }