From 74f858498784390ac3ef5331bcc0e0a6698048a0 Mon Sep 17 00:00:00 2001 From: lucaCambi77 Date: Wed, 29 May 2024 09:54:52 +0200 Subject: [PATCH] fix: /tracker/trackedEntites?order=enrolledAt returns wrong order [2.39] (#17612) * fix: join condition * fix: compile --- .../HibernateTrackedEntityInstanceStore.java | 19 +++-- .../OrderAndPaginationExporterTest.java | 77 ++++++++++++++++++- .../tracker/event_and_enrollment.json | 23 ++++++ 3 files changed, 111 insertions(+), 8 deletions(-) diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/trackedentity/hibernate/HibernateTrackedEntityInstanceStore.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/trackedentity/hibernate/HibernateTrackedEntityInstanceStore.java index ffaa08fc5c2e..fb4be523b25d 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/trackedentity/hibernate/HibernateTrackedEntityInstanceStore.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/trackedentity/hibernate/HibernateTrackedEntityInstanceStore.java @@ -890,7 +890,8 @@ private String getOwnerOrgUnit(TrackedEntityInstanceQueryParams params) { /** * Generates an INNER JOIN for program instances. If the param we need to order by is enrolledAt, - * we need to join the program instance table to be able to select and order by this value + * we need to join the program instance table to be able to select and order by this value. We + * restrict the join condition to a specific program if specified in the request. * * @param params * @return a SQL INNER JOIN for program instances @@ -898,12 +899,16 @@ private String getOwnerOrgUnit(TrackedEntityInstanceQueryParams params) { private String getFromSubQueryJoinProgramInstanceConditions( TrackedEntityInstanceQueryParams params) { if (params.getOrders().stream().anyMatch(p -> ENROLLED_AT.isPropertyEqualTo(p.getField()))) { - return new StringBuilder(" INNER JOIN programinstance ") - .append(PROGRAM_INSTANCE_ALIAS) - .append(" ON ") - .append(PROGRAM_INSTANCE_ALIAS + "." + "trackedentityinstanceid") - .append("= TEI.trackedentityinstanceid ") - .toString(); + + String join = + "INNER JOIN programinstance %1$s ON %1$s.trackedentityinstanceid = TEI.trackedentityinstanceid"; + + return !params.hasProgram() + ? String.format(join, PROGRAM_INSTANCE_ALIAS) + : String.format( + join + " AND %1$s.programid = %2$s", + PROGRAM_INSTANCE_ALIAS, + params.getProgram().getId()); } return ""; diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/OrderAndPaginationExporterTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/OrderAndPaginationExporterTest.java index 8a62d728df00..620f4be390c1 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/OrderAndPaginationExporterTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/OrderAndPaginationExporterTest.java @@ -53,14 +53,19 @@ import org.hisp.dhis.common.QueryItem; import org.hisp.dhis.common.SlimPager; import org.hisp.dhis.common.ValueType; +import org.hisp.dhis.dxf2.events.TrackedEntityInstanceParams; import org.hisp.dhis.dxf2.events.event.Event; import org.hisp.dhis.dxf2.events.event.EventQueryParams; import org.hisp.dhis.dxf2.events.event.EventService; import org.hisp.dhis.dxf2.events.event.Events; +import org.hisp.dhis.dxf2.events.trackedentity.TrackedEntityInstance; +import org.hisp.dhis.dxf2.events.trackedentity.TrackedEntityInstanceService; import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.program.Program; import org.hisp.dhis.program.ProgramStageInstance; import org.hisp.dhis.trackedentity.TrackedEntityAttribute; +import org.hisp.dhis.trackedentity.TrackedEntityInstanceQueryParams; +import org.hisp.dhis.trackedentity.TrackedEntityType; import org.hisp.dhis.user.User; import org.hisp.dhis.webapi.controller.event.mapper.OrderParam; import org.hisp.dhis.webapi.controller.event.mapper.OrderParam.SortDirection; @@ -75,12 +80,20 @@ class OrderAndPaginationExporterTest extends TrackerTest { @Autowired private EventService eventService; + @Autowired private TrackedEntityInstanceService trackedEntityService; + @Autowired private TrackerImportService trackerImportService; @Autowired private IdentifiableObjectManager manager; private OrganisationUnit orgUnit; + private TrackedEntityType trackedEntityType; + + private User importUser; + + private Program program; + final Function> eventsFunction = (params) -> eventService.getEvents(params).getEvents().stream() @@ -94,7 +107,7 @@ class OrderAndPaginationExporterTest extends TrackerTest { @Override protected void initTest() throws IOException { setUpMetadata("tracker/simple_metadata.json"); - User importUser = userService.getUser("M5zQapPyTZI"); + importUser = userService.getUser("M5zQapPyTZI"); assertNoErrors( trackerImportService.importTracker( fromJson("tracker/event_and_enrollment.json", importUser.getUid()))); @@ -102,6 +115,8 @@ protected void initTest() throws IOException { pTzf9KYMk72 = get(ProgramStageInstance.class, "pTzf9KYMk72"); D9PbzJY8bJM = get(ProgramStageInstance.class, "D9PbzJY8bJM"); + trackedEntityType = get(TrackedEntityType.class, "ja8NY4PW7Xm"); + program = get(Program.class, "BFcipDERJnf"); // to test that events are only returned if the user has read access to ALL COs of an events COC CategoryOption categoryOption = get(CategoryOption.class, "yMj2MnmNI8L"); @@ -692,6 +707,66 @@ void shouldOrderEventsByLastUpdatedInAscendingOrderWhenLastUpdatedAscSupplied() } } + @Test + void shouldOrderTrackedEntitiesByEnrolledAtAsc() { + TrackedEntityInstanceQueryParams params = new TrackedEntityInstanceQueryParams(); + + params.addOrganisationUnit(orgUnit); + params.setOrganisationUnitMode(SELECTED); + params.setTrackedEntityInstanceUids(Set.of("QS6w44flWAf", "dUE514NMOlo")); + params.setTrackedEntityType(trackedEntityType); + params.setUser(importUser); + params.setOrders(List.of(new OrderParam("enrolledAt", SortDirection.ASC))); + + List trackedEntities = getTrackedEntities(params); + + assertEquals(List.of("QS6w44flWAf", "dUE514NMOlo"), trackedEntities); + } + + @Test + void shouldOrderTrackedEntitiesByEnrolledAtDescWithNoProgramInParams() { + TrackedEntityInstanceQueryParams params = new TrackedEntityInstanceQueryParams(); + + params.addOrganisationUnit(orgUnit); + params.setOrganisationUnitMode(SELECTED); + params.setTrackedEntityInstanceUids(Set.of("QS6w44flWAf", "dUE514NMOlo")); + params.setTrackedEntityType(trackedEntityType); + params.setUser(importUser); + params.setOrders(List.of(new OrderParam("enrolledAt", SortDirection.DESC))); + + List trackedEntities = getTrackedEntities(params); + + assertEquals( + List.of("QS6w44flWAf", "dUE514NMOlo"), + trackedEntities); // QS6w44flWAf has 2 enrollments, one of which has an enrollment with + // enrolled date greater than the enrollment in dUE514NMOlo + } + + @Test + void shouldOrderTrackedEntitiesByEnrolledAtDescWithProgramInParams() { + + TrackedEntityInstanceQueryParams params = new TrackedEntityInstanceQueryParams(); + + params.setProgram(program); + params.addOrganisationUnit(orgUnit); + params.setOrganisationUnitMode(SELECTED); + params.setTrackedEntityInstanceUids(Set.of("QS6w44flWAf", "dUE514NMOlo")); + params.setUser(importUser); + params.setOrders(List.of(new OrderParam("enrolledAt", SortDirection.DESC))); + + List trackedEntities = getTrackedEntities(params); + + assertEquals(List.of("dUE514NMOlo", "QS6w44flWAf"), trackedEntities); + } + + private List getTrackedEntities(TrackedEntityInstanceQueryParams params) { + return trackedEntityService + .getTrackedEntityInstances(params, TrackedEntityInstanceParams.FALSE, false, false) + .stream() + .map(TrackedEntityInstance::getTrackedEntityInstance) + .collect(Collectors.toList()); + } + private static QueryItem queryItem(TrackedEntityAttribute tea) { return new QueryItem( tea, diff --git a/dhis-2/dhis-test-integration/src/test/resources/tracker/event_and_enrollment.json b/dhis-2/dhis-test-integration/src/test/resources/tracker/event_and_enrollment.json index 95220bebe025..6e9d601fa01e 100644 --- a/dhis-2/dhis-test-integration/src/test/resources/tracker/event_and_enrollment.json +++ b/dhis-2/dhis-test-integration/src/test/resources/tracker/event_and_enrollment.json @@ -279,6 +279,29 @@ "attributes": [], "notes": [] }, + { + "enrollment": "nxP8UnKhomJ", + "createdAtClient": "2017-01-26T13:48:13.363", + "trackedEntity": "QS6w44flWAf", + "program": { + "idScheme": "UID", + "identifier": "shPjYNifvMK" + }, + "status": "ACTIVE", + "orgUnit": { + "idScheme": "UID", + "identifier": "uoNW0E3xXUy" + }, + "enrolledAt": "2021-04-28T12:05:00.000", + "occurredAt": "2021-04-28T12:05:00.000", + "scheduledAt": "2021-04-28T12:05:00.000", + "followUp": false, + "deleted": false, + "events": [], + "relationships": [], + "attributes": [], + "notes": [] + }, { "enrollment": "TvctPPhpD8z", "createdAtClient": "2017-01-26T13:48:13.363",