diff --git a/README.md b/README.md index e02cd8e0..3c8b6a3a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# DHIS 2 Java Client +# DHIS2 Java Client -DHIS 2 API client for Java. The client allows you to create, update and retrieve information from DHIS 2. The client is compatible with Java 17 and later JDK versions. +DHIS2 API client for Java. The client allows you to create, update and retrieve information from DHIS2. The client is compatible with Java 17 and later JDK versions. ## Getting started @@ -22,7 +22,7 @@ This section describes configuration and authentication of the client. #### Basic authentication -A minimal configuration of `dhis2-java-client` where the configuration parameters refer to the base URL, username and password for the DHIS 2 instance to connect to can be specified like this. The default authentication mechanism is *Basic authentication*. Note that you should *not* include the `api` part nor a trailing `/` in the base URL: +A minimal configuration of `dhis2-java-client` where the configuration parameters refer to the base URL, username and password for the DHIS2 instance to connect to can be specified like this. The default authentication mechanism is *Basic authentication*. Note that you should *not* include the `api` part nor a trailing `/` in the base URL: ```java Dhis2Config config = new Dhis2Config( @@ -32,7 +32,7 @@ Dhis2Config config = new Dhis2Config( Dhis2 dhis2 = new Dhis2(config); ``` -Alternatively, to use Basic authentication you can specify the username and password of the DHIS 2 account together with the base URL of the DHIS 2 instance: +Alternatively, to use Basic authentication you can specify the username and password of the DHIS2 account together with the base URL of the DHIS2 instance: ```java Dhis2 dhis2 = Dhis2.withBasicAuth( @@ -40,7 +40,7 @@ Dhis2 dhis2 = Dhis2.withBasicAuth( "admin", "district"); ``` -You can use the username and password of a regular DHIS 2 user account. +You can use the username and password of a regular DHIS2 user account. ### Personal access token authentication @@ -64,7 +64,7 @@ Dhis2 dhis2 = Dhis2.withCookieAuth( "5EC557E60D7E5CE8D78EEC1389592D3E"); ``` -The name of the session cookie used by the DHIS 2 API is `JSESSIONID`. The value can typically be retrieved from the `Cookie` HTTP request header sent with DHIS 2 API requests. +The name of the session cookie used by the DHIS2 API is `JSESSIONID`. The value can typically be retrieved from the `Cookie` HTTP request header sent with DHIS2 API requests. ### Get current user @@ -87,7 +87,7 @@ List authorities = dhis2.getUserAuthorization(); ## Metadata -This section explains operations for DHIS 2 metadata objects. +This section explains operations for DHIS2 metadata objects. ### Query objects @@ -195,9 +195,28 @@ boolean success = response.getHttpStatus().is2xxSuccessful(); String message = response.getMessage(); ``` +## Users + +This section explains operations for DHIS2 users. + +### Get user + +To get a user by ID. + +```java +User user = dhis2.getUser("xE7jOejl9FI"); +``` + +To get a user by username. + +```java +List users = dhis2.getUsers(Query.instance() + .addFilter(Filter.eq("username", "admin"))); +``` + ## System settings -This section explains operations for DHIS 2 system settings. +This section explains operations for DHIS2 system settings. ### Get system settings @@ -209,7 +228,7 @@ SystemSettings settings = dhis2.getSystemSettings(); ## Events -This section explains operations for DHIS 2 events. +This section explains operations for DHIS2 events. ### Save events @@ -255,7 +274,7 @@ EventResponse response = dhis2.removeEvent(event); ## Data values -This section explains operations for DHIS 2 data values. +This section explains operations for DHIS2 data values. ### Save data value set @@ -459,7 +478,7 @@ List notifications = dhis2 ## Development -This section covers development of the DHIS 2 Java client. +This section covers development of the DHIS2 Java client. Package: diff --git a/src/main/java/org/hisp/dhis/BaseDhis2.java b/src/main/java/org/hisp/dhis/BaseDhis2.java index 6c3640df..f60fe46a 100644 --- a/src/main/java/org/hisp/dhis/BaseDhis2.java +++ b/src/main/java/org/hisp/dhis/BaseDhis2.java @@ -245,6 +245,14 @@ public class BaseDhis2 { dataSets,authorities,organisationUnits[%2$s]""", ID_FIELDS, ORG_UNIT_FIELDS); + /** User fields. */ + protected static final String USER_FIELDS = + String.format( + """ + %1$s,username,firstName,surname,email,phoneNumber,externalAuth,lastLogin,\ + organisationUnits[%2$s],dataViewOrganisationUnits[%2$s],teiSearchOrganisationUnits[%2$s]""", + ID_FIELDS, NAME_FIELDS); + /** Log level system property. */ private static final String LOG_LEVEL_SYSTEM_PROPERTY = "log.level.dhis2"; diff --git a/src/main/java/org/hisp/dhis/Dhis2.java b/src/main/java/org/hisp/dhis/Dhis2.java index f13836b2..8dcdaeb7 100644 --- a/src/main/java/org/hisp/dhis/Dhis2.java +++ b/src/main/java/org/hisp/dhis/Dhis2.java @@ -98,6 +98,7 @@ import org.hisp.dhis.model.event.Events; import org.hisp.dhis.model.event.EventsResult; import org.hisp.dhis.model.trackedentity.TrackedEntityType; +import org.hisp.dhis.model.user.User; import org.hisp.dhis.model.validation.Period; import org.hisp.dhis.model.validation.Validation; import org.hisp.dhis.model.validation.ValidationRule; @@ -2157,6 +2158,45 @@ public List getDashboards(Query query) { .getDashboards(); } + // ------------------------------------------------------------------------- + // User + // ------------------------------------------------------------------------- + + /** + * Retrieves a {@link User}. + * + * @param id the object identifier. + * @return the {@link User}. + * @throws Dhis2ClientException if the object does not exist. + */ + public User getUser(String id) { + return getObject( + config + .getResolvedUriBuilder() + .appendPath("users") + .appendPath(id) + .addParameter(FIELDS_PARAM, USER_FIELDS), + Query.instance(), + User.class); + } + + /** + * Retrieves a list of {@link User}. + * + * @param query the {@link Query}. + * @return list of {@link User}. + */ + public List getUsers(Query query) { + return getObject( + config + .getResolvedUriBuilder() + .appendPath("users") + .addParameter(FIELDS_PARAM, USER_FIELDS), + query, + Dhis2Objects.class) + .getUsers(); + } + // ------------------------------------------------------------------------- // Dimension // ------------------------------------------------------------------------- diff --git a/src/main/java/org/hisp/dhis/model/Dhis2Objects.java b/src/main/java/org/hisp/dhis/model/Dhis2Objects.java index fad3beaf..8ac09fc9 100644 --- a/src/main/java/org/hisp/dhis/model/Dhis2Objects.java +++ b/src/main/java/org/hisp/dhis/model/Dhis2Objects.java @@ -37,6 +37,7 @@ import org.hisp.dhis.model.completedatasetregistration.CompleteDataSetRegistration; import org.hisp.dhis.model.dashboard.Dashboard; import org.hisp.dhis.model.trackedentity.TrackedEntityType; +import org.hisp.dhis.model.user.User; import org.hisp.dhis.model.validation.ValidationRule; @Getter @@ -44,12 +45,31 @@ @Accessors(chain = true) @NoArgsConstructor public class Dhis2Objects { + @JsonProperty private List analyticsTableHooks = new ArrayList<>(); + + @JsonProperty private List categoryOptions = new ArrayList<>(); + + @JsonProperty private List categories = new ArrayList<>(); + + @JsonProperty private List categoryCombos = new ArrayList<>(); + + @JsonProperty private List categoryOptionCombos = new ArrayList<>(); + + @JsonProperty private List categoryOptionGroups = new ArrayList<>(); + + @JsonProperty private List categoryOptionGroupSets = new ArrayList<>(); + + @JsonProperty + private List completeDataSetRegistrations = new ArrayList<>(); + @JsonProperty private List dashboards = new ArrayList<>(); @JsonProperty private List dataElements = new ArrayList<>(); @JsonProperty private List dataElementGroups = new ArrayList<>(); + @JsonProperty private List dimensions = new ArrayList<>(); + @JsonProperty private List organisationUnits = new ArrayList<>(); @JsonProperty private List organisationUnitGroups = new ArrayList<>(); @@ -58,21 +78,10 @@ public class Dhis2Objects { @JsonProperty private List organisationUnitLevels = new ArrayList<>(); - @JsonProperty private List categoryOptions = new ArrayList<>(); - - @JsonProperty private List categories = new ArrayList<>(); - - @JsonProperty private List categoryCombos = new ArrayList<>(); - - @JsonProperty private List categoryOptionCombos = new ArrayList<>(); - @JsonProperty private List dataElementGroupSets = new ArrayList<>(); @JsonProperty private List dataSets = new ArrayList<>(); - @JsonProperty - private List completeDataSetRegistrations = new ArrayList<>(); - @JsonProperty private List maps = new ArrayList(); @JsonProperty private List indicatorTypes = new ArrayList<>(); @@ -95,16 +104,10 @@ public class Dhis2Objects { @JsonProperty private List trackedEntityTypes = new ArrayList<>(); - @JsonProperty private List categoryOptionGroups = new ArrayList<>(); - - @JsonProperty private List categoryOptionGroupSets = new ArrayList<>(); - - @JsonProperty private List analyticsTableHooks = new ArrayList<>(); - - @JsonProperty private List dimensions = new ArrayList<>(); - @JsonProperty private List periodTypes = new ArrayList<>(); + @JsonProperty private List users = new ArrayList<>(); + @JsonProperty private List visualizations = new ArrayList<>(); @JsonProperty private List validationRules = new ArrayList<>(); diff --git a/src/main/java/org/hisp/dhis/model/Me.java b/src/main/java/org/hisp/dhis/model/Me.java index cce61aea..47ebcc53 100644 --- a/src/main/java/org/hisp/dhis/model/Me.java +++ b/src/main/java/org/hisp/dhis/model/Me.java @@ -33,6 +33,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.hisp.dhis.model.user.UserSettings; @Getter @Setter diff --git a/src/main/java/org/hisp/dhis/model/user/User.java b/src/main/java/org/hisp/dhis/model/user/User.java new file mode 100644 index 00000000..dc030861 --- /dev/null +++ b/src/main/java/org/hisp/dhis/model/user/User.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2004-2025, 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.user; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hisp.dhis.model.IdentifiableObject; +import org.hisp.dhis.model.OrgUnit; + +@Getter +@Setter +@NoArgsConstructor +public class User extends IdentifiableObject { + @JsonProperty private String username; + + @JsonProperty private String firstName; + + @JsonProperty private String surname; + + @JsonProperty private String email; + + @JsonProperty private String phoneNumber; + + @JsonProperty private Boolean externalAuth; + + @JsonProperty private Date lastLogin; + + @JsonProperty private Boolean disabled; + + @JsonProperty private List organisationUnits = new ArrayList<>(); + + @JsonProperty private List dataViewOrganisationUnits = new ArrayList<>(); + + @JsonProperty private List teiSearchOrganisationUnits = new ArrayList<>(); +} diff --git a/src/main/java/org/hisp/dhis/model/UserSettings.java b/src/main/java/org/hisp/dhis/model/user/UserSettings.java similarity index 98% rename from src/main/java/org/hisp/dhis/model/UserSettings.java rename to src/main/java/org/hisp/dhis/model/user/UserSettings.java index b1b60b81..e0673982 100644 --- a/src/main/java/org/hisp/dhis/model/UserSettings.java +++ b/src/main/java/org/hisp/dhis/model/user/UserSettings.java @@ -25,7 +25,7 @@ * (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.model.user; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; diff --git a/src/test/java/org/hisp/dhis/CompleteDataSetRegistrationApiTest.java b/src/test/java/org/hisp/dhis/CompleteDataSetRegistrationApiTest.java index 92398409..b609aa35 100644 --- a/src/test/java/org/hisp/dhis/CompleteDataSetRegistrationApiTest.java +++ b/src/test/java/org/hisp/dhis/CompleteDataSetRegistrationApiTest.java @@ -47,7 +47,6 @@ @Tag(TestTags.INTEGRATION) class CompleteDataSetRegistrationApiTest { - @Test void testGetCompleteDataSetRegistrationsWithStartEndDates() { CompleteDataSetRegistrationQuery query = diff --git a/src/test/java/org/hisp/dhis/ProgramApiTest.java b/src/test/java/org/hisp/dhis/ProgramApiTest.java index 5816f974..469e836d 100644 --- a/src/test/java/org/hisp/dhis/ProgramApiTest.java +++ b/src/test/java/org/hisp/dhis/ProgramApiTest.java @@ -54,7 +54,7 @@ import org.junit.jupiter.api.Test; @Tag(TestTags.INTEGRATION) -public class ProgramApiTest { +class ProgramApiTest { @Test void testGetProgramChildProgram() { Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG); diff --git a/src/test/java/org/hisp/dhis/UserApiTest.java b/src/test/java/org/hisp/dhis/UserApiTest.java new file mode 100644 index 00000000..237cb678 --- /dev/null +++ b/src/test/java/org/hisp/dhis/UserApiTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2004-2025, 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.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.List; +import org.hisp.dhis.model.OrgUnit; +import org.hisp.dhis.model.user.User; +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; + +@Tag(TestTags.INTEGRATION) +class UserApiTest { + @Test + void testGetUser() { + Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG); + + User user = dhis2.getUser("xE7jOejl9FI"); + + assertNotNull(user); + assertEquals("xE7jOejl9FI", user.getId()); + assertEquals("John Traore", user.getName()); + assertEquals("admin", user.getUsername()); + assertEquals("John", user.getFirstName()); + assertEquals("Traore", user.getSurname()); + assertEquals("dummy@dhis2.org", user.getEmail()); + assertFalse(user.getOrganisationUnits().isEmpty()); + + OrgUnit orgUnit = user.getOrganisationUnits().get(0); + + assertNotNull(orgUnit); + assertNotNull(orgUnit.getId()); + assertNotNull(orgUnit.getName()); + } + + @Test + void testGetUserByUsername() { + Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG); + + List users = dhis2.getUsers(Query.instance().addFilter(Filter.eq("username", "admin"))); + + assertNotNull(users); + assertEquals(1, users.size()); + + User user = users.get(0); + + assertNotNull(user); + assertEquals("xE7jOejl9FI", user.getId()); + } + + @Test + void testGetUsers() { + Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG); + + List users = dhis2.getUsers(Query.instance()); + + assertNotNull(users); + assertFalse(users.isEmpty()); + } +}