From 59991d7fb49ee89b520c9889215f358a4cf8c362 Mon Sep 17 00:00:00 2001 From: teleivo Date: Fri, 8 Nov 2024 08:14:52 +0100 Subject: [PATCH] feat: export /tracker/event using different idSchemes DHIS2-14968 (#19039) * feat: export /tracker/event using different idSchemes * feat: use original change log metadata [DHIS2-14968] --------- Co-authored-by: Marc --- .../dhis/tracker/TrackerIdSchemeParam.java | 16 + .../export/event/EventOperationParams.java | 3 - .../event/EventOperationParamsMapper.java | 1 - .../export/event/EventQueryParams.java | 16 +- .../tracker/export/event/JdbcEventStore.java | 231 ++- .../org/hisp/dhis/test/utils/Assertions.java | 16 +- .../resources/tracker/simple_metadata.json | 48 +- .../export/event/EventExporterTest.java | 130 -- .../webapi/controller/tracker/JsonEvent.java | 8 + .../export/IdSchemeExportControllerTest.java | 267 ++++ ...dEntitiesExportControllerPostgresTest.java | 34 +- .../tracker/event_and_enrollment.json | 1259 +++++++++++++++++ .../tracker/simple_metadata_changelog.json | 216 +++ .../tracker/export/MetadataMapper.java | 55 + .../export/enrollment/EnrollmentMapper.java | 5 +- .../EnrollmentsExportController.java | 11 +- .../tracker/export/event/EventMapper.java | 21 +- .../export/event/EventRequestParams.java | 3 - .../event/EventRequestParamsMapper.java | 4 +- .../export/event/EventsExportController.java | 50 +- .../TrackedEntitiesExportController.java | 19 +- .../trackedentity/TrackedEntityMapper.java | 5 +- 22 files changed, 2057 insertions(+), 361 deletions(-) create mode 100644 dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/IdSchemeExportControllerTest.java create mode 100644 dhis-2/dhis-test-web-api/src/test/resources/tracker/event_and_enrollment.json create mode 100644 dhis-2/dhis-test-web-api/src/test/resources/tracker/simple_metadata_changelog.json create mode 100644 dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/MetadataMapper.java diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/TrackerIdSchemeParam.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/TrackerIdSchemeParam.java index 2d657b69cc07..a19e70c9f218 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/TrackerIdSchemeParam.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/TrackerIdSchemeParam.java @@ -113,4 +113,20 @@ public MetadataIdentifier toMetadataIdentifier(String identifier) { } return MetadataIdentifier.of(this.idScheme, identifier, null); } + + /** + * Returns this {@link TrackerIdSchemeParam} as expected in a request parameter which makes it + * usable in tests. It is also a more readable format than what lombok produces. + */ + @Override + public String toString() { + if (this.idScheme == null) { + return ""; + } + if (this.idScheme == TrackerIdScheme.ATTRIBUTE) { + return this.idScheme.name() + ":" + this.attributeUid; + } + + return this.idScheme.name(); + } } diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventOperationParams.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventOperationParams.java index c51e10b1cdd6..0d24750cb317 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventOperationParams.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventOperationParams.java @@ -41,7 +41,6 @@ import lombok.Getter; import org.hisp.dhis.category.CategoryOptionCombo; import org.hisp.dhis.common.AssignedUserSelectionMode; -import org.hisp.dhis.common.IdSchemes; import org.hisp.dhis.common.OrganisationUnitSelectionMode; import org.hisp.dhis.common.QueryFilter; import org.hisp.dhis.common.SortDirection; @@ -110,8 +109,6 @@ public class EventOperationParams { private CategoryOptionCombo categoryOptionCombo; - @Builder.Default private IdSchemes idSchemes = new IdSchemes(); - private boolean includeRelationships; /** diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventOperationParamsMapper.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventOperationParamsMapper.java index a16d1f028544..2aa01ab24c18 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventOperationParamsMapper.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventOperationParamsMapper.java @@ -136,7 +136,6 @@ public EventQueryParams map( .setEnrollmentOccurredAfter(operationParams.getEnrollmentOccurredAfter()) .setEventStatus(operationParams.getEventStatus()) .setCategoryOptionCombo(attributeOptionCombo) - .setIdSchemes(operationParams.getIdSchemes()) .setIncludeAttributes(false) .setIncludeAllDataElements(false) .setEvents(operationParams.getEvents()) diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventQueryParams.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventQueryParams.java index b1d60b2bf982..2817ba1e9632 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventQueryParams.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/EventQueryParams.java @@ -40,7 +40,6 @@ import org.apache.commons.collections4.SetUtils; import org.hisp.dhis.category.CategoryOptionCombo; import org.hisp.dhis.common.AssignedUserQueryParam; -import org.hisp.dhis.common.IdSchemes; import org.hisp.dhis.common.OrganisationUnitSelectionMode; import org.hisp.dhis.common.QueryFilter; import org.hisp.dhis.common.SortDirection; @@ -103,8 +102,6 @@ class EventQueryParams { private CategoryOptionCombo categoryOptionCombo; - private IdSchemes idSchemes = new IdSchemes(); - private boolean includeRelationships; /** @@ -376,15 +373,6 @@ public EventQueryParams setEnrollmentOccurredAfter(Date enrollmentOccurredAfter) return this; } - public IdSchemes getIdSchemes() { - return idSchemes; - } - - public EventQueryParams setIdSchemes(IdSchemes idSchemes) { - this.idSchemes = idSchemes; - return this; - } - public boolean isIncludeAttributes() { return includeAttributes; } @@ -560,7 +548,7 @@ public boolean isOrganisationUnitMode(OrganisationUnitSelectionMode mode) { public boolean isPathOrganisationUnitMode() { return orgUnitMode != null - && (orgUnitMode.equals(OrganisationUnitSelectionMode.DESCENDANTS) - || orgUnitMode.equals(OrganisationUnitSelectionMode.CHILDREN)); + && (OrganisationUnitSelectionMode.DESCENDANTS.equals(orgUnitMode) + || OrganisationUnitSelectionMode.CHILDREN.equals(orgUnitMode)); } } diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/JdbcEventStore.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/JdbcEventStore.java index ed598db887f2..af893a197e50 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/JdbcEventStore.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/event/JdbcEventStore.java @@ -61,11 +61,10 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.hisp.dhis.attribute.AttributeValues; import org.hisp.dhis.category.CategoryOption; import org.hisp.dhis.category.CategoryOptionCombo; import org.hisp.dhis.common.AssignedUserSelectionMode; -import org.hisp.dhis.common.IdScheme; -import org.hisp.dhis.common.IdSchemes; import org.hisp.dhis.common.IdentifiableObjectManager; import org.hisp.dhis.common.QueryFilter; import org.hisp.dhis.common.QueryOperator; @@ -124,23 +123,24 @@ class JdbcEventStore implements EventStore { + " relationshipitem ri group by ri_ev_id) as fgh on fgh.ri_ev_id=event.ev_id "; private static final String EVENT_NOTE_QUERY = - "select evn.eventid as evn_id," - + " n.noteid as note_id," - + " n.notetext as note_text," - + " n.created as note_created," - + " n.creator as note_creator," - + " n.uid as note_uid," - + " n.lastupdated as note_lastupdated," - + " userinfo.userinfoid as note_user_id," - + " userinfo.code as note_user_code," - + " userinfo.uid as note_user_uid," - + " userinfo.username as note_user_username," - + " userinfo.firstname as note_user_firstname," - + " userinfo.surname as note_user_surname" - + " from event_notes evn" - + " inner join note n" - + " on evn.noteid = n.noteid" - + " left join userinfo on n.lastupdatedby = userinfo.userinfoid "; + """ + select evn.eventid as evn_id,\ + n.noteid as note_id,\ + n.notetext as note_text,\ + n.created as note_created,\ + n.creator as note_creator,\ + n.uid as note_uid,\ + n.lastupdated as note_lastupdated,\ + userinfo.userinfoid as note_user_id,\ + userinfo.code as note_user_code,\ + userinfo.uid as note_user_uid,\ + userinfo.username as note_user_username,\ + userinfo.firstname as note_user_firstname,\ + userinfo.surname as note_user_surname\ + from event_notes evn\ + inner join note n\ + on evn.noteid = n.noteid\ + left join userinfo on n.lastupdatedby = userinfo.userinfoid\s"""; private static final String EVENT_STATUS_EQ = " ev.status = "; @@ -157,13 +157,20 @@ class JdbcEventStore implements EventStore { private static final String COLUMN_EVENT_ID = "ev_id"; private static final String COLUMN_EVENT_UID = "ev_uid"; private static final String COLUMN_PROGRAM_UID = "p_uid"; + private static final String COLUMN_PROGRAM_CODE = "p_code"; + private static final String COLUMN_PROGRAM_NAME = "p_name"; + private static final String COLUMN_PROGRAM_ATTRIBUTE_VALUES = "p_attribute_values"; private static final String COLUMN_PROGRAM_STAGE_UID = "ps_uid"; + private static final String COLUMN_PROGRAM_STAGE_CODE = "ps_code"; private static final String COLUMN_PROGRAM_STAGE_NAME = "ps_name"; + private static final String COLUMN_PROGRAM_STAGE_ATTRIBUTE_VALUES = "ps_attribute_values"; private static final String COLUMN_ENROLLMENT_UID = "en_uid"; private static final String COLUMN_ENROLLMENT_STATUS = "en_status"; private static final String COLUMN_ENROLLMENT_DATE = "en_enrollmentdate"; private static final String COLUMN_ORG_UNIT_UID = "orgunit_uid"; private static final String COLUMN_ORG_UNIT_CODE = "orgunit_code"; + private static final String COLUMN_ORG_UNIT_NAME = "orgunit_name"; + private static final String COLUMN_ORG_UNIT_ATTRIBUTE_VALUES = "orgunit_attribute_values"; private static final String COLUMN_TRACKEDENTITY_UID = "te_uid"; private static final String COLUMN_EVENT_OCCURRED_DATE = "ev_occurreddate"; private static final String COLUMN_ENROLLMENT_FOLLOWUP = "en_followup"; @@ -282,8 +289,6 @@ private List fetchEvents(EventQueryParams queryParams, PageParams pagePar String eventUid = resultSet.getString(COLUMN_EVENT_UID); - validateIdentifiersPresence(resultSet, queryParams.getIdSchemes()); - Event event; if (eventsByUid.containsKey(eventUid)) { event = eventsByUid.get(eventUid); @@ -296,20 +301,35 @@ private List fetchEvents(EventQueryParams queryParams, PageParams pagePar TrackedEntity te = new TrackedEntity(); te.setUid(resultSet.getString(COLUMN_TRACKEDENTITY_UID)); event.setStatus(EventStatus.valueOf(resultSet.getString(COLUMN_EVENT_STATUS))); + ProgramType programType = ProgramType.fromValue(resultSet.getString("p_type")); Program program = new Program(); - program.setUid(resultSet.getString("p_identifier")); + program.setUid(resultSet.getString(COLUMN_PROGRAM_UID)); + program.setCode(resultSet.getString(COLUMN_PROGRAM_CODE)); + program.setName(resultSet.getString(COLUMN_PROGRAM_NAME)); + program.setAttributeValues( + AttributeValues.of(resultSet.getString(COLUMN_PROGRAM_ATTRIBUTE_VALUES))); program.setProgramType(programType); + Enrollment enrollment = new Enrollment(); enrollment.setUid(resultSet.getString(COLUMN_ENROLLMENT_UID)); enrollment.setProgram(program); enrollment.setTrackedEntity(te); - OrganisationUnit ou = new OrganisationUnit(); - ou.setUid(resultSet.getString(COLUMN_ORG_UNIT_UID)); - ou.setCode(resultSet.getString(COLUMN_ORG_UNIT_CODE)); + + OrganisationUnit orgUnit = new OrganisationUnit(); + orgUnit.setUid(resultSet.getString(COLUMN_ORG_UNIT_UID)); + orgUnit.setCode(resultSet.getString(COLUMN_ORG_UNIT_CODE)); + orgUnit.setName(resultSet.getString(COLUMN_ORG_UNIT_NAME)); + orgUnit.setAttributeValues( + AttributeValues.of(resultSet.getString(COLUMN_ORG_UNIT_ATTRIBUTE_VALUES))); + event.setOrganisationUnit(orgUnit); + ProgramStage ps = new ProgramStage(); - ps.setUid(resultSet.getString("ps_identifier")); + ps.setUid(resultSet.getString(COLUMN_PROGRAM_STAGE_UID)); + ps.setCode(resultSet.getString(COLUMN_PROGRAM_STAGE_CODE)); ps.setName(resultSet.getString(COLUMN_PROGRAM_STAGE_NAME)); + ps.setAttributeValues( + AttributeValues.of(resultSet.getString(COLUMN_PROGRAM_STAGE_ATTRIBUTE_VALUES))); event.setDeleted(resultSet.getBoolean(COLUMN_EVENT_DELETED)); enrollment.setStatus( @@ -317,10 +337,9 @@ private List fetchEvents(EventQueryParams queryParams, PageParams pagePar enrollment.setFollowup(resultSet.getBoolean(COLUMN_ENROLLMENT_FOLLOWUP)); event.setEnrollment(enrollment); event.setProgramStage(ps); - event.setOrganisationUnit(ou); CategoryOptionCombo coc = new CategoryOptionCombo(); - coc.setUid(resultSet.getString("coc_identifier")); + coc.setUid(resultSet.getString(COLUMN_EVENT_ATTRIBUTE_OPTION_COMBO_UID)); Set options = Arrays.stream(resultSet.getString("co_uids").split(TextUtils.COMMA)) .map( @@ -452,89 +471,6 @@ public Set getOrderableFields() { return ORDERABLE_FIELDS.keySet(); } - private String getIdSqlBasedOnIdScheme( - IdScheme idScheme, String uidSql, String attributeSql, String codeSql) { - if (idScheme == IdScheme.ID || idScheme == IdScheme.UID) { - return uidSql; - } else if (idScheme.isAttribute()) { - return String.format(attributeSql, idScheme.getAttribute()); - } else { - return codeSql; - } - } - - private void validateIdentifiersPresence(ResultSet rowSet, IdSchemes idSchemes) - throws SQLException { - - if (StringUtils.isEmpty(rowSet.getString("p_identifier"))) { - throw new IllegalStateException( - String.format( - "Program %s does not have a value assigned for idScheme %s", - rowSet.getString(COLUMN_PROGRAM_UID), idSchemes.getProgramIdScheme().name())); - } - - if (StringUtils.isEmpty(rowSet.getString("ps_identifier"))) { - throw new IllegalStateException( - String.format( - "ProgramStage %s does not have a value assigned for idScheme %s", - rowSet.getString(COLUMN_PROGRAM_STAGE_UID), - idSchemes.getProgramStageIdScheme().name())); - } - - if (StringUtils.isEmpty(rowSet.getString("ou_identifier"))) { - throw new IllegalStateException( - String.format( - "OrgUnit %s does not have a value assigned for idScheme %s", - rowSet.getString(COLUMN_ORG_UNIT_UID), idSchemes.getOrgUnitIdScheme().name())); - } - - if (StringUtils.isEmpty(rowSet.getString("coc_identifier"))) { - throw new IllegalStateException( - String.format( - "CategoryOptionCombo %s does not have a value assigned for idScheme %s", - rowSet.getString(COLUMN_EVENT_ATTRIBUTE_OPTION_COMBO_UID), - idSchemes.getCategoryOptionComboIdScheme().name())); - } - } - - private String getEventSelectIdentifiersByIdScheme(EventQueryParams params) { - IdSchemes idSchemes = params.getIdSchemes(); - - StringBuilder sqlBuilder = new StringBuilder(); - - String ouTableName = getOuTableName(params); - - sqlBuilder.append( - getIdSqlBasedOnIdScheme( - idSchemes.getOrgUnitIdScheme(), - ouTableName + ".uid as ou_identifier, ", - ouTableName + ".attributevalues #>> '{%s, value}' as ou_identifier, ", - ouTableName + ".code as ou_identifier, ")); - - sqlBuilder.append( - getIdSqlBasedOnIdScheme( - idSchemes.getProgramIdScheme(), - "p.uid as p_identifier, ", - "p.attributevalues #>> '{%s, value}' as p_identifier, ", - "p.code as p_identifier, ")); - - sqlBuilder.append( - getIdSqlBasedOnIdScheme( - idSchemes.getProgramStageIdScheme(), - "ps.uid as ps_identifier, ", - "ps.attributevalues #>> '{%s, value}' as ps_identifier, ", - "ps.code as ps_identifier, ")); - - sqlBuilder.append( - getIdSqlBasedOnIdScheme( - idSchemes.getCategoryOptionComboIdScheme(), - "coc_agg.uid as coc_identifier, ", - "coc_agg.attributevalues #>> '{%s, value}' as coc_identifier, ", - "coc_agg.code as coc_identifier, ")); - - return sqlBuilder.toString(); - } - private long getEventCount(EventQueryParams params) { User currentUser = userService.getUserByUsername(CurrentUserUtil.getCurrentUsername()); setAccessiblePrograms(currentUser, params); @@ -755,41 +691,47 @@ private String getEventSelectQuery( StringBuilder selectBuilder = new StringBuilder() - .append("select ") - .append(getEventSelectIdentifiersByIdScheme(params)) - .append(" ev.uid as ") + .append("select ev.uid as ") .append(COLUMN_EVENT_UID) - .append(", ") - .append("ou.uid as ") + .append(", ou.uid as ") .append(COLUMN_ORG_UNIT_UID) - .append(", ") - .append("ou.code as ") + .append(", ou.code as ") .append(COLUMN_ORG_UNIT_CODE) + .append(", ou.name as ") + .append(COLUMN_ORG_UNIT_NAME) + .append(", ou.attributevalues as ") + .append(COLUMN_ORG_UNIT_ATTRIBUTE_VALUES) .append(", p.uid as ") .append(COLUMN_PROGRAM_UID) + .append(", p.code as ") + .append(COLUMN_PROGRAM_CODE) + .append(", p.name as ") + .append(COLUMN_PROGRAM_NAME) + .append(", p.attributevalues as ") + .append(COLUMN_PROGRAM_ATTRIBUTE_VALUES) .append(", ps.uid as ") .append(COLUMN_PROGRAM_STAGE_UID) + .append(", ps.code as ") + .append(COLUMN_PROGRAM_STAGE_CODE) .append(", ps.name as ") .append(COLUMN_PROGRAM_STAGE_NAME) - .append(", ") - .append("ev.eventid as ") + .append(", ps.attributevalues as ") + .append(COLUMN_PROGRAM_STAGE_ATTRIBUTE_VALUES) + .append(", ev.eventid as ") .append(COLUMN_EVENT_ID) .append(", ev.status as ") .append(COLUMN_EVENT_STATUS) .append(", ev.occurreddate as ") .append(COLUMN_EVENT_OCCURRED_DATE) - .append(", ") - .append("ev.eventdatavalues as ev_eventdatavalues, ev.scheduleddate as ") + .append(", ev.eventdatavalues as ev_eventdatavalues, ev.scheduleddate as ") .append(COLUMN_EVENT_SCHEDULED_DATE) .append(", ev.completedby as ") .append(COLUMN_EVENT_COMPLETED_BY) .append(", ev.storedby as ") .append(COLUMN_EVENT_STORED_BY) - .append(", ") - .append("ev.created as ") + .append(", ev.created as ") .append(COLUMN_EVENT_CREATED) - .append(", ") - .append("ev.createdatclient as ") + .append(", ev.createdatclient as ") .append(COLUMN_EVENT_CREATED_AT_CLIENT) .append(", ev.createdbyuserinfo as ") .append(COLUMN_EVENT_CREATED_BY) @@ -799,15 +741,13 @@ private String getEventSelectQuery( .append(COLUMN_EVENT_LAST_UPDATED_AT_CLIENT) .append(", ev.lastupdatedbyuserinfo as ") .append(COLUMN_EVENT_LAST_UPDATED_BY) - .append(", ") - .append("ev.completeddate as ") + .append(", ev.completeddate as ") .append(COLUMN_EVENT_COMPLETED_DATE) .append(", ev.deleted as ") .append(COLUMN_EVENT_DELETED) - .append(", ") .append( - "ST_AsText( ev.geometry ) as ev_geometry, au.uid as user_assigned, (au.firstName ||" - + " ' ' || au.surName) as ") + ", ST_AsText( ev.geometry ) as ev_geometry, au.uid as user_assigned, (au.firstName" + + " || ' ' || au.surName) as ") .append(COLUMN_EVENT_ASSIGNED_USER_DISPLAY_NAME) .append(",") .append( @@ -866,10 +806,6 @@ private boolean checkForOwnership(EventQueryParams params) { .isPresent(); } - private String getOuTableName(EventQueryParams params) { - return checkForOwnership(params) ? " evou" : " ou"; - } - private StringBuilder getFromWhereClause( EventQueryParams params, MapSqlParameterSource mapSqlParameterSource, @@ -1510,12 +1446,14 @@ private String addLastUpdatedFilters( */ private String getCategoryOptionComboQuery(User user) { String joinCondition = - " inner join (select coc.uid, coc.attributevalues, coc.code, coc.categoryoptioncomboid as" - + " id, string_agg(co.uid, ',') as co_uids, count(co.categoryoptionid) as co_count from" - + " categoryoptioncombo coc inner join categoryoptioncombos_categoryoptions cocco on" - + " coc.categoryoptioncomboid = cocco.categoryoptioncomboid inner join categoryoption" - + " co on cocco.categoryoptionid = co.categoryoptionid group by" - + " coc.categoryoptioncomboid "; + """ + inner join (select coc.uid, coc.attributevalues, coc.code, coc.categoryoptioncomboid as\ + id, string_agg(co.uid, ',') as co_uids, count(co.categoryoptionid) as co_count from\ + categoryoptioncombo coc inner join categoryoptioncombos_categoryoptions cocco on\ + coc.categoryoptioncomboid = cocco.categoryoptioncomboid inner join categoryoption\ + co on cocco.categoryoptionid = co.categoryoptionid group by\ + coc.categoryoptioncomboid \ + """; if (!isSuper(user)) { joinCondition = @@ -1571,11 +1509,12 @@ private String getOrderQuery(EventQueryParams params) { } private String getAttributeValueQuery() { - return "select pav.trackedentityid as pav_id, pav.created as pav_created, pav.lastupdated as" - + " pav_lastupdated, pav.value as pav_value, ta.uid as ta_uid, ta.name as ta_name," - + " ta.valuetype as ta_valuetype from trackedentityattributevalue pav inner join" - + " trackedentityattribute ta on" - + " pav.trackedentityattributeid=ta.trackedentityattributeid "; + return """ + select pav.trackedentityid as pav_id, pav.created as pav_created, pav.lastupdated as\ + pav_lastupdated, pav.value as pav_value, ta.uid as ta_uid, ta.name as ta_name,\ + ta.valuetype as ta_valuetype from trackedentityattributevalue pav inner join\ + trackedentityattribute ta on\ + pav.trackedentityattributeid=ta.trackedentityattributeid\s"""; } private boolean isSuper(User user) { diff --git a/dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/test/utils/Assertions.java b/dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/test/utils/Assertions.java index 725c8b262f38..a636d0fbd66a 100644 --- a/dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/test/utils/Assertions.java +++ b/dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/test/utils/Assertions.java @@ -173,7 +173,8 @@ public static void assertStartsWith(String expected, String actual) { } /** - * Asserts that the given string neither null, a length of zero nor whitespace only. + * Asserts that the given string is not null, has a non-zero length, and contains non-whitespace + * characters. * * @param actual the string. */ @@ -183,7 +184,7 @@ public static void assertNotBlank(String actual) { } /** - * Asserts that the given string neither null or a length of zero. + * Asserts that the given string is not null and has a non-zero length. * * @param actual the string. */ @@ -192,6 +193,17 @@ public static void assertNotEmpty(String actual) { assertTrue(!actual.isEmpty()); } + /** + * Asserts that the given string is not null and has a non-zero length. + * + * @param actual the string. + * @param message fails with this message + */ + public static void assertNotEmpty(String actual, String message) { + assertNotNull(actual, message); + assertTrue(!actual.isEmpty(), message); + } + /** * Asserts that the given character sequence is contained within the actual string. * diff --git a/dhis-2/dhis-support/dhis-support-test/src/main/resources/tracker/simple_metadata.json b/dhis-2/dhis-support/dhis-support-test/src/main/resources/tracker/simple_metadata.json index c116089c0965..be855cb062ec 100644 --- a/dhis-2/dhis-support/dhis-support-test/src/main/resources/tracker/simple_metadata.json +++ b/dhis-2/dhis-support/dhis-support-test/src/main/resources/tracker/simple_metadata.json @@ -539,18 +539,26 @@ "organisationUnits": [ { "id": "h4w96yEMlzO", - "code": "test-orgunit-code", + "attributeValues": [ + { + "attribute": { + "id": "j45AR9cBQKc" + }, + "value": "orgUnit h4w96yEMlzO" + } + ], + "code": "h4w96yEMlzO code", "created": "2020-05-31T08:56:15.922", - "description": "test-orgunit", + "description": "h4w96yEMlzO description", "lastUpdated": "2020-05-31T11:41:22.384", "lastUpdatedBy": { "id": "tTgjgobT1oS" }, "level": 1, - "name": "test-orgunit", + "name": "h4w96yEMlzO name", "openingDate": "2020-05-31T00:00:00.000", "path": "/h4w96yEMlzO", - "shortName": "test-orgunit", + "shortName": "h4w96yEMlzO short name", "user": { "id": "tTgjgobT1oS" } @@ -715,7 +723,16 @@ "programStages": [ { "id": "NpsdDv6kKSO", + "attributeValues": [ + { + "attribute": { + "id": "j45AR9cBQKc" + }, + "value": "programStage NpsdDv6kKSO" + } + ], "autoGenerateEvent": true, + "code": "NpsdDv6kKSO code", "created": "2020-05-31T09:02:52.687", "description": "test-program-stage", "displayGenerateEventBox": true, @@ -974,8 +991,7 @@ "programStage": { "id": "NpsdDv6kKSg" }, - "sharing": { - }, + "sharing": {}, "sortOrder": 1 } ], @@ -1024,8 +1040,7 @@ "programStage": { "id": "qLZC0lvvxQH" }, - "sharing": { - }, + "sharing": {}, "sortOrder": 1 } ], @@ -1225,10 +1240,19 @@ { "id": "BFcipDERJnf", "accessLevel": "OPEN", + "attributeValues": [ + { + "attribute": { + "id": "j45AR9cBQKc" + }, + "value": "programStage NpsdDv6kKSO" + } + ], "categoryCombo": { "id": "bjDvmb4bfuf" }, "completeEventsExpiryDays": 0, + "code": "BFcipDERJnf code", "created": "2020-05-31T09:02:52.718", "displayIncidentDate": true, "expiryDays": 0, @@ -1238,7 +1262,7 @@ }, "maxTeiCountToReturn": 0, "minAttributesRequiredToSearch": 1, - "name": "test-program", + "name": "BFcipDERJnf name", "organisationUnits": [ { "id": "h4w96yEMlzO" @@ -1273,8 +1297,7 @@ "id": "BFcipDERJnf" }, "searchable": true, - "sharing": { - }, + "sharing": {}, "sortOrder": 1, "trackedEntityAttribute": { "id": "dIVt4l5vIOa" @@ -1301,8 +1324,7 @@ "id": "BFcipDERJnf" }, "searchable": true, - "sharing": { - }, + "sharing": {}, "sortOrder": 2, "trackedEntityAttribute": { "id": "fRGt4l6yIRb" diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/event/EventExporterTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/event/EventExporterTest.java index 3ed1a326bb53..fc0a251367ff 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/event/EventExporterTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/event/EventExporterTest.java @@ -29,17 +29,14 @@ import static org.hisp.dhis.common.OrganisationUnitSelectionMode.ACCESSIBLE; import static org.hisp.dhis.common.OrganisationUnitSelectionMode.SELECTED; -import static org.hisp.dhis.test.utils.Assertions.assertContains; import static org.hisp.dhis.test.utils.Assertions.assertContainsOnly; import static org.hisp.dhis.test.utils.Assertions.assertIsEmpty; -import static org.hisp.dhis.test.utils.Assertions.assertStartsWith; import static org.hisp.dhis.tracker.Assertions.assertHasTimeStamp; import static org.hisp.dhis.tracker.Assertions.assertNoErrors; import static org.hisp.dhis.util.DateUtils.parseDate; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.time.ZoneId; @@ -52,7 +49,6 @@ import java.util.stream.Collectors; import org.hisp.dhis.category.CategoryOption; import org.hisp.dhis.common.BaseIdentifiableObject; -import org.hisp.dhis.common.IdSchemes; import org.hisp.dhis.common.IdentifiableObject; import org.hisp.dhis.common.IdentifiableObjectManager; import org.hisp.dhis.common.QueryFilter; @@ -497,132 +493,6 @@ void shouldReturnEventsGivenCategoryOptionCombo() throws ForbiddenException, Bad assertAll("all events should have the same category option combo and options", executables); } - @Test - void shouldFailIfCategoryOptionComboOfGivenEventDoesNotHaveAValueForGivenIdScheme() { - IdSchemes idSchemes = new IdSchemes(); - idSchemes.setCategoryOptionComboIdScheme("ATTRIBUTE:GOLswS44mh8"); - EventOperationParams params = - operationParamsBuilder - .orgUnit(UID.of("DiszpKrYNg8")) - .orgUnitMode(SELECTED) - .idSchemes(idSchemes) - .events(Set.of(UID.of("kWjSezkXHVp"))) - .build(); - - IllegalStateException ex = - assertThrows(IllegalStateException.class, () -> eventService.getEvents(params)); - assertStartsWith("CategoryOptionCombo", ex.getMessage()); - assertContains("not have a value assigned for idScheme ATTRIBUTE:GOLswS44mh8", ex.getMessage()); - } - - @Test - void shouldReturnEventsGivenIdSchemeCode() throws ForbiddenException, BadRequestException { - IdSchemes idSchemes = new IdSchemes(); - idSchemes.setProgramIdScheme("code"); - idSchemes.setProgramStageIdScheme("code"); - idSchemes.setOrgUnitIdScheme("code"); - idSchemes.setCategoryOptionComboIdScheme("code"); - - EventOperationParams params = - operationParamsBuilder - .orgUnit(UID.of("DiszpKrYNg8")) - .orgUnitMode(SELECTED) - .idSchemes(idSchemes) - .attributeCategoryCombo(UID.of("O4VaNks6tta")) - .attributeCategoryOptions(UID.of("xwZ2u3WyQR0", "M58XdOfhiJ7")) - .build(); - - List events = eventService.getEvents(params); - - assertContainsOnly(List.of("kWjSezkXHVp", "OTmjvJDn0Fu"), uids(events)); - List executables = - events.stream() - .map( - e -> - (Executable) - () -> - assertAll( - "event " + e.getUid(), - () -> - assertEquals( - "multi-program", e.getEnrollment().getProgram().getUid()), - () -> assertEquals("multi-stage", e.getProgramStage().getUid()), - () -> - assertEquals( - "DiszpKrYNg8", - e.getOrganisationUnit() - .getUid()), // TODO(DHIS2-14968): this might be a bug - // caused by - // https://github.com/dhis2/dhis2-core/pull/12518 - () -> - assertEquals( - "COC_1153452", e.getAttributeOptionCombo().getUid()), - () -> - assertContainsOnly( - Set.of("xwZ2u3WyQR0", "M58XdOfhiJ7"), - e.getAttributeOptionCombo().getCategoryOptions().stream() - .map(CategoryOption::getUid) - .collect(Collectors.toSet())))) - .toList(); - assertAll("all events should have the same category option combo and options", executables); - } - - @Test - void shouldReturnEventsGivenIdSchemeAttribute() throws ForbiddenException, BadRequestException { - IdSchemes idSchemes = new IdSchemes(); - idSchemes.setProgramIdScheme("ATTRIBUTE:j45AR9cBQKc"); - idSchemes.setProgramStageIdScheme("ATTRIBUTE:j45AR9cBQKc"); - idSchemes.setOrgUnitIdScheme("ATTRIBUTE:j45AR9cBQKc"); - idSchemes.setCategoryOptionComboIdScheme("ATTRIBUTE:j45AR9cBQKc"); - EventOperationParams params = - operationParamsBuilder - .orgUnit(UID.of("DiszpKrYNg8")) - .orgUnitMode(SELECTED) - .idSchemes(idSchemes) - .attributeCategoryCombo(UID.of("O4VaNks6tta")) - .attributeCategoryOptions(UID.of("xwZ2u3WyQR0", "M58XdOfhiJ7")) - .build(); - - List events = eventService.getEvents(params); - - assertContainsOnly(List.of("kWjSezkXHVp", "OTmjvJDn0Fu"), uids(events)); - List executables = - events.stream() - .map( - e -> - (Executable) - () -> - assertAll( - "event " + e.getUid(), - () -> - assertEquals( - "multi-program-attribute", - e.getEnrollment().getProgram().getUid()), - () -> - assertEquals( - "multi-program-stage-attribute", - e.getProgramStage().getUid()), - () -> - assertEquals( - "DiszpKrYNg8", - e.getOrganisationUnit() - .getUid()), // TODO(DHIS2-14968): this might be a bug - // caused by - // https://github.com/dhis2/dhis2-core/pull/12518 - () -> - assertEquals( - "COC_1153452-attribute", - e.getAttributeOptionCombo().getUid()), - () -> - assertContainsOnly( - Set.of("xwZ2u3WyQR0", "M58XdOfhiJ7"), - e.getAttributeOptionCombo().getCategoryOptions().stream() - .map(CategoryOption::getUid) - .collect(Collectors.toSet())))) - .toList(); - assertAll("all events should have the same category option combo and options", executables); - } - @Test void testExportEventsWhenFilteringByDataElementsWithCategoryOptionNotSuperUser() throws ForbiddenException, BadRequestException { diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/JsonEvent.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/JsonEvent.java index 38a9541b7459..c5e68ab77f64 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/JsonEvent.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/JsonEvent.java @@ -60,6 +60,14 @@ default String getOrgUnit() { return getString("orgUnit").string(); } + default String attributeOptionCombo() { + return getString("attributeOptionCombo").string(); + } + + default String attributeCategoryOptions() { + return getString("attributeCategoryOptions").string(); + } + default Boolean getDeleted() { return getBoolean("deleted").bool(); } diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/IdSchemeExportControllerTest.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/IdSchemeExportControllerTest.java new file mode 100644 index 000000000000..3933c871e30a --- /dev/null +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/IdSchemeExportControllerTest.java @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2004-2022, 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.webapi.controller.tracker.export; + +import static org.hisp.dhis.test.utils.Assertions.assertNotEmpty; +import static org.junit.jupiter.api.Assertions.assertAll; +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 static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.hisp.dhis.attribute.Attribute; +import org.hisp.dhis.common.IdentifiableObject; +import org.hisp.dhis.common.IdentifiableObjectManager; +import org.hisp.dhis.dxf2.metadata.objectbundle.ObjectBundle; +import org.hisp.dhis.dxf2.metadata.objectbundle.ObjectBundleMode; +import org.hisp.dhis.dxf2.metadata.objectbundle.ObjectBundleParams; +import org.hisp.dhis.dxf2.metadata.objectbundle.ObjectBundleService; +import org.hisp.dhis.dxf2.metadata.objectbundle.ObjectBundleValidationService; +import org.hisp.dhis.dxf2.metadata.objectbundle.feedback.ObjectBundleValidationReport; +import org.hisp.dhis.http.HttpStatus; +import org.hisp.dhis.importexport.ImportStrategy; +import org.hisp.dhis.jsontree.JsonObject; +import org.hisp.dhis.organisationunit.OrganisationUnit; +import org.hisp.dhis.program.Event; +import org.hisp.dhis.program.Program; +import org.hisp.dhis.program.ProgramStage; +import org.hisp.dhis.render.RenderFormat; +import org.hisp.dhis.render.RenderService; +import org.hisp.dhis.test.webapi.PostgresControllerIntegrationTestBase; +import org.hisp.dhis.tracker.TrackerIdSchemeParam; +import org.hisp.dhis.tracker.imports.TrackerImportParams; +import org.hisp.dhis.tracker.imports.TrackerImportService; +import org.hisp.dhis.tracker.imports.domain.TrackerObjects; +import org.hisp.dhis.tracker.imports.report.ImportReport; +import org.hisp.dhis.tracker.imports.report.Status; +import org.hisp.dhis.tracker.imports.report.ValidationReport; +import org.hisp.dhis.user.User; +import org.hisp.dhis.webapi.controller.tracker.JsonEvent; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; +import org.springframework.transaction.annotation.Transactional; + +/** Tests tracker exporter idScheme support. */ +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class IdSchemeExportControllerTest extends PostgresControllerIntegrationTestBase { + + private static final String METADATA_ATTRIBUTE = "j45AR9cBQKc"; + + @Autowired private RenderService renderService; + + @Autowired private ObjectBundleService objectBundleService; + + @Autowired private ObjectBundleValidationService objectBundleValidationService; + + @Autowired private TrackerImportService trackerImportService; + + @Autowired private IdentifiableObjectManager manager; + + private OrganisationUnit orgUnit; + private ProgramStage programStage; + private Program program; + + private User importUser; + + protected ObjectBundle setUpMetadata(String path) throws IOException { + Map, List> metadata = + renderService.fromMetadata(new ClassPathResource(path).getInputStream(), RenderFormat.JSON); + ObjectBundleParams params = new ObjectBundleParams(); + params.setObjectBundleMode(ObjectBundleMode.COMMIT); + params.setImportStrategy(ImportStrategy.CREATE); + params.setObjects(metadata); + ObjectBundle bundle = objectBundleService.create(params); + assertNoErrors(objectBundleValidationService.validate(bundle)); + objectBundleService.commit(bundle); + return bundle; + } + + protected TrackerObjects fromJson(String path) throws IOException { + return renderService.fromJson( + new ClassPathResource(path).getInputStream(), TrackerObjects.class); + } + + @BeforeAll + void setUp() throws IOException { + setUpMetadata("tracker/simple_metadata.json"); + + importUser = userService.getUser("tTgjgobT1oS"); + injectSecurityContextUser(importUser); + + TrackerImportParams params = TrackerImportParams.builder().build(); + assertNoErrors( + trackerImportService.importTracker(params, fromJson("tracker/event_and_enrollment.json"))); + get(Attribute.class, METADATA_ATTRIBUTE); // ensure this is created in setup + orgUnit = get(OrganisationUnit.class, "h4w96yEMlzO"); + program = get(Program.class, "BFcipDERJnf"); + programStage = get(ProgramStage.class, "NpsdDv6kKSO"); + + manager.flush(); + manager.clear(); + } + + @ParameterizedTest + @MethodSource(value = "shouldExportMetadataUsingGivenIdSchemeProvider") + void shouldExportMetadataUsingGivenIdScheme(TrackerIdSchemeParam idSchemeParam) { + switchContextToUser(importUser); + + // maps JSON fields to metadata + Map metadata = + Map.of("orgUnit", orgUnit, "program", program, "programStage", programStage); + String fields = metadata.keySet().stream().collect(Collectors.joining(",")); + String idSchemes = + metadata.keySet().stream() + .map(m -> m + "IdScheme=" + idSchemeParam) + .collect(Collectors.joining("&")); + Event d9PbzJY8bJM = get(Event.class, "D9PbzJY8bJM"); + + JsonEvent actual = + GET( + "/tracker/events/{id}?fields={fields}&{idSchemes}", + d9PbzJY8bJM.getUid(), + fields, + idSchemes) + .content(HttpStatus.OK) + .as(JsonEvent.class); + + assertMetadataIdScheme(metadata, actual, idSchemeParam, "event"); + } + + public static Stream shouldExportMetadataUsingGivenIdSchemeProvider() { + return Stream.of( + TrackerIdSchemeParam.UID, + TrackerIdSchemeParam.CODE, + TrackerIdSchemeParam.NAME, + TrackerIdSchemeParam.ofAttribute(METADATA_ATTRIBUTE)); + } + + /** + * Asserts that every metadata key from {@code expected} is a field in the {@code actual} JSON and + * that its string value matches the requested {@code idSchemeParam}. + */ + private void assertMetadataIdScheme( + Map expected, + JsonObject actual, + TrackerIdSchemeParam idSchemeParam, + String objectName) { + List assertions = + expected.entrySet().stream() + .map( + e -> + (Executable) + () -> { + assertIdScheme(e.getValue(), actual, idSchemeParam, e.getKey()); + }) + .toList(); + assertAll(objectName + " metadata assertions", assertions); + } + + private void assertIdScheme( + IdentifiableObject expected, + JsonObject actual, + TrackerIdSchemeParam idSchemeParam, + String field) { + assertNotEmpty( + idSchemeParam.getIdentifier(expected), + String.format( + "metadata %s(%s) has no value in test data for idScheme '%s'", + expected.getClass().getSimpleName(), expected.getUid(), idSchemeParam)); + assertTrue( + actual.has(field), + () -> + String.format( + "field \"%s\" is not in response %s for idScheme '%s'", + field, actual, idSchemeParam)); + assertEquals( + idSchemeParam.getIdentifier(expected), + actual.getString(field).string(), + () -> + String.format( + "field \"%s\" does not have required idScheme '%s' in response", + field, idSchemeParam)); + } + + private T get(Class type, String uid) { + T t = manager.get(type, uid); + assertNotNull( + t, + () -> + String.format( + "'%s' with uid '%s' should have been created", type.getSimpleName(), uid)); + return t; + } + + public static void assertNoErrors(ImportReport report) { + assertNotNull(report); + assertEquals( + Status.OK, + report.getStatus(), + errorMessage( + "Expected import with status OK, instead got:%n", report.getValidationReport())); + } + + private static Supplier errorMessage(String errorTitle, ValidationReport report) { + return () -> { + StringBuilder msg = new StringBuilder(errorTitle); + report + .getErrors() + .forEach( + e -> { + msg.append(e.getErrorCode()); + msg.append(": "); + msg.append(e.getMessage()); + msg.append('\n'); + }); + return msg.toString(); + }; + } + + public static void assertNoErrors(ObjectBundleValidationReport report) { + assertNotNull(report); + List errors = new ArrayList<>(); + report.forEachErrorReport( + err -> { + errors.add(err.toString()); + }); + assertFalse( + report.hasErrorReports(), String.format("Expected no errors, instead got: %s%n", errors)); + } +} diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntitiesExportControllerPostgresTest.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntitiesExportControllerPostgresTest.java index 11cf64fe4d8e..c6e83aa1c9fe 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntitiesExportControllerPostgresTest.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntitiesExportControllerPostgresTest.java @@ -96,7 +96,7 @@ class TrackedEntitiesExportControllerPostgresTest extends PostgresControllerInte @BeforeEach void setUp() throws IOException { this.renderService = _renderService; - setUpMetadata("tracker/simple_metadata.json"); + setUpMetadata("tracker/simple_metadata_changelog.json"); User importUser = userService.getUser("tTgjgobT1oS"); injectSecurityContextUser(importUser); @@ -320,22 +320,22 @@ private Supplier errorMessage(String errorTitle, ValidationReport report private String createJsonPayload(int value) { return """ - { - "trackedEntities": [ - { - "attributes": [ - { - "attribute": "numericAttr", - "value": %d - } - ], - "trackedEntity": "IOR1AXXl24H", - "trackedEntityType": "ja8NY4PW7Xm", - "orgUnit": "h4w96yEMlzO" - } - ] - } - """ + { + "trackedEntities": [ + { + "attributes": [ + { + "attribute": "numericAttr", + "value": %d + } + ], + "trackedEntity": "IOR1AXXl24H", + "trackedEntityType": "ja8NY4PW7Xm", + "orgUnit": "h4w96yEMlzO" + } + ] + } + """ .formatted(value); } diff --git a/dhis-2/dhis-test-web-api/src/test/resources/tracker/event_and_enrollment.json b/dhis-2/dhis-test-web-api/src/test/resources/tracker/event_and_enrollment.json new file mode 100644 index 000000000000..65e456734b90 --- /dev/null +++ b/dhis-2/dhis-test-web-api/src/test/resources/tracker/event_and_enrollment.json @@ -0,0 +1,1259 @@ +{ + "importMode": "COMMIT", + "idSchemes": { + "dataElementIdScheme": { + "idScheme": "UID" + }, + "orgUnitIdScheme": { + "idScheme": "UID" + }, + "programIdScheme": { + "idScheme": "UID" + }, + "programStageIdScheme": { + "idScheme": "UID" + }, + "idScheme": { + "idScheme": "UID" + }, + "categoryOptionComboIdScheme": { + "idScheme": "UID" + }, + "categoryOptionIdScheme": { + "idScheme": "UID" + } + }, + "importStrategy": "CREATE", + "atomicMode": "ALL", + "flushMode": "AUTO", + "validationMode": "FULL", + "skipPatternValidation": false, + "skipSideEffects": false, + "skipRuleEngine": false, + "trackedEntities": [ + { + "trackedEntity": "QS6w44flWAf", + "trackedEntityType": { + "idScheme": "UID", + "identifier": "ja8NY4PW7Xm" + }, + "orgUnit": { + "idScheme": "UID", + "identifier": "h4w96yEMlzO" + }, + "inactive": true, + "deleted": false, + "potentialDuplicate": false, + "createdAtClient": "2018-10-01T12:17:30.163", + "relationships": [], + "attributes": [ + { + "valueType": "TEXT", + "attribute": { + "idScheme": "UID", + "identifier": "toDelete000" + }, + "value": "just day" + }, + { + "valueType": "TEXT", + "attribute": { + "idScheme": "UID", + "identifier": "toUpdate000" + }, + "value": "summer day" + }, + { + "valueType": "INTEGER", + "attribute": { + "idScheme": "UID", + "identifier": "numericAttr" + }, + "value": "88" + } + ], + "enrollments": [] + }, + { + "trackedEntity": "dUE514NMOlo", + "trackedEntityType": { + "idScheme": "UID", + "identifier": "ja8NY4PW7Xm" + }, + "orgUnit": { + "idScheme": "UID", + "identifier": "h4w96yEMlzO" + }, + "inactive": false, + "deleted": false, + "potentialDuplicate": false, + "createdAtClient": "2018-11-01T12:17:30.163", + "relationships": [], + "attributes": [ + { + "valueType": "TEXT", + "attribute": { + "idScheme": "UID", + "identifier": "toDelete000" + }, + "value": "just day" + }, + { + "valueType": "TEXT", + "attribute": { + "idScheme": "UID", + "identifier": "toUpdate000" + }, + "value": "rainy day" + }, + { + "valueType": "TEXT", + "attribute": { + "idScheme": "UID", + "identifier": "notUpdated0" + }, + "value": "winter day" + }, + { + "valueType": "INTEGER", + "attribute": { + "idScheme": "UID", + "identifier": "numericAttr" + }, + "value": "70" + } + ], + "enrollments": [] + }, + { + "trackedEntity": "mHWCacsGYYn", + "trackedEntityType": { + "idScheme": "UID", + "identifier": "ja8NY4PW7Xm" + }, + "orgUnit": { + "idScheme": "UID", + "identifier": "h4w96yEMlzO" + }, + "inactive": false, + "deleted": false, + "potentialDuplicate": false, + "relationships": [], + "attributes": [ + { + "valueType": "TEXT", + "attribute": { + "idScheme": "UID", + "identifier": "toDelete000" + }, + "value": "just day" + }, + { + "valueType": "TEXT", + "attribute": { + "idScheme": "UID", + "identifier": "toUpdate000" + }, + "value": "summer day" + }, + { + "valueType": "INTEGER", + "attribute": { + "idScheme": "UID", + "identifier": "numericAttr" + }, + "value": "72" + } + ], + "enrollments": [] + }, + { + "trackedEntity": "QesgJkTyTCk", + "trackedEntityType": { + "idScheme": "UID", + "identifier": "ja8NY4PW7Xm" + }, + "orgUnit": { + "idScheme": "UID", + "identifier": "h4w96yEMlzO" + }, + "inactive": false, + "deleted": false, + "potentialDuplicate": false, + "relationships": [], + "attributes": [ + { + "valueType": "TEXT", + "attribute": { + "idScheme": "UID", + "identifier": "toDelete000" + }, + "value": "just day" + }, + { + "valueType": "TEXT", + "attribute": { + "idScheme": "UID", + "identifier": "toUpdate000" + }, + "value": "summer day" + }, + { + "valueType": "INTEGER", + "attribute": { + "idScheme": "UID", + "identifier": "numericAttr" + }, + "value": "89" + } + ], + "enrollments": [] + }, + { + "trackedEntity": "guVNoAerxWo", + "trackedEntityType": { + "idScheme": "UID", + "identifier": "ja8NY4PW7Xm" + }, + "orgUnit": { + "idScheme": "UID", + "identifier": "tSsGrtfRzjY" + }, + "inactive": false, + "deleted": false, + "potentialDuplicate": false, + "relationships": [], + "attributes": [ + { + "valueType": "TEXT", + "attribute": { + "idScheme": "UID", + "identifier": "toDelete000" + }, + "value": "just day" + }, + { + "valueType": "TEXT", + "attribute": { + "idScheme": "UID", + "identifier": "toUpdate000" + }, + "value": "summer day" + }, + { + "valueType": "INTEGER", + "attribute": { + "idScheme": "UID", + "identifier": "numericAttr" + }, + "value": "91" + } + ], + "enrollments": [] + }, + { + "trackedEntity": "woitxQbWYNq", + "trackedEntityType": { + "idScheme": "UID", + "identifier": "ja8NY4PW7Xm" + }, + "orgUnit": { + "idScheme": "UID", + "identifier": "RojfDTBhoGC" + }, + "inactive": false, + "deleted": false, + "potentialDuplicate": false, + "relationships": [], + "attributes": [ + { + "valueType": "TEXT", + "attribute": { + "idScheme": "UID", + "identifier": "toDelete000" + }, + "value": "just day" + }, + { + "valueType": "TEXT", + "attribute": { + "idScheme": "UID", + "identifier": "toUpdate000" + }, + "value": "summer day" + }, + { + "valueType": "INTEGER", + "attribute": { + "idScheme": "UID", + "identifier": "numericAttr" + }, + "value": "90" + } + ], + "enrollments": [] + }, + { + "trackedEntity": "XUitxQbWYNq", + "trackedEntityType": { + "idScheme": "UID", + "identifier": "Ip8NY4PW7Xm" + }, + "orgUnit": { + "idScheme": "UID", + "identifier": "DiszpKrYNg8" + }, + "inactive": false, + "deleted": false, + "potentialDuplicate": false, + "relationships": [], + "enrollments": [] + } + ], + "enrollments": [ + { + "enrollment": "nxP7UnKhomJ", + "createdAtClient": "2017-01-26T13:48:13.363", + "trackedEntity": "QS6w44flWAf", + "program": { + "idScheme": "UID", + "identifier": "BFcipDERJnf" + }, + "status": "ACTIVE", + "orgUnit": { + "idScheme": "UID", + "identifier": "h4w96yEMlzO" + }, + "orgUnitName": "Mbokie CHP", + "enrolledAt": "2021-02-28T12:05:00.000", + "occurredAt": "2021-02-28T12:05:00.000", + "scheduledAt": "2021-02-28T12:05:00.000", + "followUp": false, + "deleted": false, + "events": [], + "relationships": [], + "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", + "trackedEntity": "dUE514NMOlo", + "program": { + "idScheme": "UID", + "identifier": "BFcipDERJnf" + }, + "status": "COMPLETED", + "orgUnit": { + "idScheme": "UID", + "identifier": "h4w96yEMlzO" + }, + "orgUnitName": "Mbokie CHP", + "enrolledAt": "2021-03-28T12:05:00.000", + "occurredAt": "2021-03-28T12:05:00.000", + "scheduledAt": "2021-02-28T12:05:00.000", + "followUp": false, + "deleted": false, + "events": [], + "relationships": [], + "attributes": [], + "notes": [] + }, + { + "enrollment": "JuioKiICQqI", + "createdAtClient": "2017-01-26T13:48:13.363", + "trackedEntity": "mHWCacsGYYn", + "program": { + "idScheme": "UID", + "identifier": "shPjYNifvMK" + }, + "status": "ACTIVE", + "orgUnit": { + "idScheme": "UID", + "identifier": "uoNW0E3xXUy" + }, + "orgUnitName": "test-orgunit-2", + "enrolledAt": "2021-02-28T12:05:00.000", + "occurredAt": "2021-02-28T12:05:00.000", + "scheduledAt": "2021-02-28T12:05:00.000", + "followUp": false, + "deleted": false, + "events": [], + "relationships": [], + "attributes": [], + "notes": [] + }, + { + "enrollment": "iHFHfPKTSYP", + "createdAtClient": "2017-01-26T13:48:13.363", + "trackedEntity": "QesgJkTyTCk", + "program": { + "idScheme": "UID", + "identifier": "pcxIanBWlSY" + }, + "status": "ACTIVE", + "orgUnit": { + "idScheme": "UID", + "identifier": "uoNW0E3xXUy" + }, + "orgUnitName": "test-orgunit-2", + "enrolledAt": "2021-02-28T12:05:00.000", + "occurredAt": "2021-02-28T12:05:00.000", + "scheduledAt": "2021-02-28T12:05:00.000", + "followUp": false, + "deleted": false, + "events": [], + "relationships": [], + "attributes": [], + "notes": [] + }, + { + "enrollment": "ipBifypAQTo", + "createdAtClient": "2017-01-26T13:48:13.363", + "trackedEntity": "guVNoAerxWo", + "program": { + "idScheme": "UID", + "identifier": "UWRnoyBjvqi" + }, + "status": "ACTIVE", + "orgUnit": { + "idScheme": "UID", + "identifier": "tSsGrtfRzjY" + }, + "orgUnitName": "test-orgunit-3", + "enrolledAt": "2021-02-28T12:05:00.000", + "occurredAt": "2021-02-28T12:05:00.000", + "scheduledAt": "2021-02-28T12:05:00.000", + "followUp": false, + "deleted": false, + "events": [], + "relationships": [], + "attributes": [], + "notes": [] + }, + { + "enrollment": "qxOSXoEZkOA", + "createdAtClient": "2018-01-26T13:48:13.363", + "trackedEntity": "woitxQbWYNq", + "program": { + "idScheme": "UID", + "identifier": "YlUmbgnKWkd" + }, + "status": "ACTIVE", + "orgUnit": { + "idScheme": "UID", + "identifier": "RojfDTBhoGC" + }, + "orgUnitName": "test-orgunit-3", + "enrolledAt": "2021-02-28T12:05:00.000", + "occurredAt": "2021-02-28T12:05:00.000", + "scheduledAt": "2021-02-28T12:05:00.000", + "followUp": false, + "deleted": false, + "events": [], + "relationships": [], + "attributes": [], + "notes": [] + }, + { + "enrollment": "HDWTYSYkICe", + "createdAtClient": "2017-01-26T13:48:13.363", + "trackedEntity": "woitxQbWYNq", + "program": { + "idScheme": "UID", + "identifier": "SeeUNWLQmZk" + }, + "status": "ACTIVE", + "orgUnit": { + "idScheme": "UID", + "identifier": "DiszpKrYNg8" + }, + "orgUnitName": "test-orgunit-3", + "enrolledAt": "2021-02-28T12:05:00.000", + "occurredAt": "2021-02-28T12:05:00.000", + "scheduledAt": "2021-02-28T12:05:00.000", + "followUp": false, + "deleted": false, + "events": [], + "relationships": [], + "attributes": [], + "notes": [] + }, + { + "enrollment": "FXWSSZunTLk", + "createdAtClient": "2017-01-26T13:48:13.363", + "trackedEntity": "woitxQbWYNq", + "program": { + "idScheme": "UID", + "identifier": "sLngICFQjvH" + }, + "status": "ACTIVE", + "orgUnit": { + "idScheme": "UID", + "identifier": "lbDXJBlvtZe" + }, + "orgUnitName": "test-orgunit-3", + "enrolledAt": "2021-02-28T12:05:00.000", + "occurredAt": "2021-02-28T12:05:00.000", + "scheduledAt": "2021-02-28T12:05:00.000", + "followUp": false, + "deleted": false, + "events": [], + "relationships": [], + "attributes": [], + "notes": [] + }, + { + "enrollment": "GYWSSZunTLk", + "createdAtClient": "2017-01-26T13:48:13.363", + "trackedEntity": "XUitxQbWYNq", + "program": { + "idScheme": "UID", + "identifier": "TsngICFQjvH" + }, + "status": "ACTIVE", + "orgUnit": { + "idScheme": "UID", + "identifier": "DiszpKrYNg8" + }, + "orgUnitName": "test-orgunit-3", + "enrolledAt": "2021-02-28T12:05:00.000", + "occurredAt": "2021-02-28T12:05:00.000", + "scheduledAt": "2021-02-28T12:05:00.000", + "followUp": false, + "deleted": false, + "events": [], + "relationships": [], + "attributes": [], + "notes": [] + } + ], + "events": [ + { + "event": "pTzf9KYMk72", + "status": "COMPLETED", + "program": { + "idScheme": "UID", + "identifier": "BFcipDERJnf" + }, + "programStage": { + "idScheme": "UID", + "identifier": "NpsdDv6kKSO" + }, + "enrollment": "nxP7UnKhomJ", + "orgUnit": { + "idScheme": "UID", + "identifier": "h4w96yEMlzO" + }, + "relationships": [], + "occurredAt": "2019-01-25T12:10:38.100", + "scheduledAt": "2019-01-28T12:32:38.100", + "createdAtClient": "2020-01-25T12:10:38.100", + "updatedAtClient": "2020-01-26T12:10:38.100", + "storedBy": "admin", + "followUp": true, + "deleted": false, + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "HllvX50cXC0" + }, + "attributeCategoryOptions": [ + { + "idScheme": "UID", + "identifier": "xYerKDKCefk" + } + ], + "completedAt": "2020-01-28T00:00:00.000", + "dataValues": [ + { + "dataElement": { + "idScheme": "UID", + "identifier": "DATAEL00001" + }, + "value": "value00001", + "createdAt": "2021-07-01T12:05:00", + "storedBy": null, + "providedElsewhere": false + }, + { + "dataElement": { + "idScheme": "UID", + "identifier": "DATAEL00005" + }, + "value": "option1", + "createdAt": "2021-07-01T12:05:00", + "storedBy": null, + "providedElsewhere": false + }, + { + "dataElement": { + "idScheme": "UID", + "identifier": "DATAEL00006" + }, + "value": "88", + "createdAt": "2021-07-01T12:05:00", + "storedBy": null, + "providedElsewhere": false + } + ], + "notes": [ + { + "note": "SGuCABkhpgn", + "value": "comment value", + "storedBy": "admin" + }, + { + "note": "DRKO4xUVrpr", + "value": "comment value", + "storedBy": "admin" + } + ] + }, + { + "event": "D9PbzJY8bJM", + "status": "COMPLETED", + "program": { + "idScheme": "UID", + "identifier": "BFcipDERJnf" + }, + "programStage": { + "idScheme": "UID", + "identifier": "NpsdDv6kKSO" + }, + "enrollment": "TvctPPhpD8z", + "orgUnit": { + "idScheme": "UID", + "identifier": "h4w96yEMlzO" + }, + "relationships": [], + "occurredAt": "2020-01-28T00:00:00.000", + "scheduledAt": "2019-01-28T12:10:38.100", + "createdAtClient": "2019-01-25T12:10:38.100", + "updatedAtClient": "2021-01-26T12:10:38.100", + "storedBy": "admin", + "followUp": true, + "deleted": false, + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "HllvX50cXC0" + }, + "attributeCategoryOptions": [ + { + "idScheme": "UID", + "identifier": "xYerKDKCefk" + } + ], + "dataValues": [ + { + "dataElement": { + "idScheme": "UID", + "identifier": "DATAEL00001" + }, + "value": "value00002", + "created": "2021-07-01T12:05:00", + "storedBy": null, + "providedElsewhere": false + }, + { + "dataElement": { + "idScheme": "UID", + "identifier": "DATAEL00002" + }, + "value": "value00002", + "created": "2021-07-01T12:05:00", + "storedBy": null, + "providedElsewhere": false + }, + { + "dataElement": { + "idScheme": "UID", + "identifier": "DATAEL00005" + }, + "value": "option2", + "created": "2021-07-01T12:05:00", + "storedBy": null, + "providedElsewhere": false + }, + { + "dataElement": { + "idScheme": "UID", + "identifier": "DATAEL00006" + }, + "value": "70", + "created": "2021-07-01T12:05:00", + "storedBy": null, + "providedElsewhere": false + }, + { + "dataElement": { + "idScheme": "UID", + "identifier": "DATAEL00007" + }, + "value": "70", + "created": "2021-07-01T12:05:00", + "storedBy": null, + "providedElsewhere": false + } + ], + "notes": [], + "assignedUser": { + "uid": "xE7jOejl9FI", + "firstName": "John", + "surname": "Traore", + "username": "admin" + } + }, + { + "event": "jxgFyJEMUPf", + "status": "COMPLETED", + "program": { + "idScheme": "UID", + "identifier": "shPjYNifvMK" + }, + "programStage": { + "idScheme": "UID", + "identifier": "SKNvpoLioON" + }, + "enrollment": "JuioKiICQqI", + "orgUnit": { + "idScheme": "UID", + "identifier": "uoNW0E3xXUy" + }, + "relationships": [], + "occurredAt": "2019-01-28T00:00:00.000", + "scheduledAt": "2019-01-28T12:10:38.100", + "storedBy": "admin", + "followUp": true, + "deleted": false, + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "HllvX50cXC0" + }, + "attributeCategoryOptions": [ + { + "idScheme": "UID", + "identifier": "xYerKDKCefk" + } + ], + "notes": [] + }, + { + "event": "JaRDIvcEcEx", + "status": "COMPLETED", + "program": { + "idScheme": "UID", + "identifier": "pcxIanBWlSY" + }, + "programStage": { + "idScheme": "UID", + "identifier": "ebGXHEqqEMF" + }, + "enrollment": "iHFHfPKTSYP", + "orgUnit": { + "idScheme": "UID", + "identifier": "uoNW0E3xXUy" + }, + "relationships": [], + "occurredAt": "2019-01-28T00:00:00.000", + "scheduledAt": "2019-01-28T12:10:38.100", + "storedBy": "admin", + "followUp": true, + "deleted": false, + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "HllvX50cXC0" + }, + "attributeCategoryOptions": [ + { + "idScheme": "UID", + "identifier": "xYerKDKCefk" + } + ], + "notes": [] + }, + { + "event": "QRYjLTiJTrA", + "occurredAt": "2022-04-20T06:00:38.343", + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "SeWJkpLAyLt" + }, + "attributeCategoryOptions": [ + { + "idScheme": "UID", + "identifier": "xwZ2u3WyQR0" + }, + { + "idScheme": "UID", + "identifier": "i4Nbp8S2G6A" + } + ], + "storedBy": "admin", + "scheduledAt": "2022-04-22T06:00:38.343", + "program": { + "idScheme": "UID", + "identifier": "iS7eutanDry" + }, + "programStage": { + "idScheme": "UID", + "identifier": "qLZC0lvvxQH" + }, + "programType": "WITHOUT_REGISTRATION", + "orgUnit": { + "idScheme": "UID", + "identifier": "DiszpKrYNg8" + }, + "status": "ACTIVE", + "deleted": false, + "dataValues": [ + { + "created": "2022-04-22T06:00:38.339", + "dataElement": { + "idScheme": "UID", + "identifier": "GieVkTxp4HH" + }, + "value": "15", + "providedElsewhere": false + } + ], + "notes": [], + "relationships": [] + }, + { + "event": "kWjSezkXHVp", + "occurredAt": "2022-04-22T06:00:38.343", + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "cr89ebDZrac" + }, + "attributeCategoryOptions": [ + { + "idScheme": "UID", + "identifier": "xwZ2u3WyQR0" + }, + { + "idScheme": "UID", + "identifier": "M58XdOfhiJ7" + } + ], + "storedBy": "admin", + "scheduledAt": "2022-04-26T06:00:34.323", + "program": { + "idScheme": "UID", + "identifier": "iS7eutanDry" + }, + "programStage": { + "idScheme": "UID", + "identifier": "qLZC0lvvxQH" + }, + "programType": "WITHOUT_REGISTRATION", + "orgUnit": { + "idScheme": "UID", + "identifier": "DiszpKrYNg8" + }, + "status": "ACTIVE", + "deleted": false, + "dataValues": [ + { + "created": "2022-04-22T06:00:34.319", + "dataElement": { + "idScheme": "UID", + "identifier": "GieVkTxp4HH" + }, + "value": "14", + "providedElsewhere": false + } + ], + "notes": [], + "relationships": [] + }, + { + "event": "OTmjvJDn0Fu", + "occurredAt": "2022-04-23T06:00:38.343", + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "cr89ebDZrac" + }, + "attributeCategoryOptions": [ + { + "idScheme": "UID", + "identifier": "xwZ2u3WyQR0" + }, + { + "idScheme": "UID", + "identifier": "M58XdOfhiJ7" + } + ], + "storedBy": "admin", + "scheduledAt": "2022-04-22T06:00:30.562", + "program": { + "idScheme": "UID", + "identifier": "iS7eutanDry" + }, + "programStage": { + "idScheme": "UID", + "identifier": "qLZC0lvvxQH" + }, + "programType": "WITHOUT_REGISTRATION", + "orgUnit": { + "idScheme": "UID", + "identifier": "DiszpKrYNg8" + }, + "status": "ACTIVE", + "deleted": false, + "dataValues": [ + { + "created": "2022-04-22T06:00:30.559", + "dataElement": { + "idScheme": "UID", + "identifier": "GieVkTxp4HH" + }, + "value": "13", + "providedElsewhere": false + } + ], + "notes": [], + "relationships": [] + }, + { + "event": "ck7DzdxqLqA", + "occurredAt": "2022-04-24T06:00:38.343", + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "tzsPhPtE94U" + }, + "attributeCategoryOptions": [ + { + "idScheme": "UID", + "identifier": "xEunk8LPzkb" + }, + { + "idScheme": "UID", + "identifier": "M58XdOfhiJ7" + } + ], + "storedBy": "admin", + "scheduledAt": "2022-04-22T06:00:14.228", + "program": { + "idScheme": "UID", + "identifier": "iS7eutanDry" + }, + "programStage": { + "idScheme": "UID", + "identifier": "qLZC0lvvxQH" + }, + "programType": "WITHOUT_REGISTRATION", + "orgUnit": { + "idScheme": "UID", + "identifier": "DiszpKrYNg8" + }, + "status": "ACTIVE", + "deleted": false, + "dataValues": [ + { + "created": "2022-04-22T06:00:14.224", + "dataElement": { + "idScheme": "UID", + "identifier": "GieVkTxp4HH" + }, + "value": "12", + "providedElsewhere": false + } + ], + "notes": [], + "relationships": [] + }, + { + "event": "lumVtWwwy0O", + "occurredAt": "2022-04-21T06:00:38.343", + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "AeOORUC0ISH" + }, + "storedBy": "admin", + "scheduledAt": "2022-04-22T06:00:14.228", + "program": { + "idScheme": "UID", + "identifier": "iS7eutanDry" + }, + "programStage": { + "idScheme": "UID", + "identifier": "qLZC0lvvxQH" + }, + "programType": "WITHOUT_REGISTRATION", + "orgUnit": { + "idScheme": "UID", + "identifier": "DiszpKrYNg8" + }, + "status": "ACTIVE", + "dataValues": [ + { + "created": "2022-04-22T06:00:14.224", + "dataElement": { + "idScheme": "UID", + "identifier": "GieVkTxp4HH" + }, + "value": "12" + } + ] + }, + { + "event": "cadc5eGj0j7", + "occurredAt": "2022-04-20T03:00:38.343", + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "V2gbpszLQJm" + }, + "storedBy": "admin", + "scheduledAt": "2022-04-22T06:00:14.228", + "program": { + "idScheme": "UID", + "identifier": "iS7eutanDry" + }, + "programStage": { + "idScheme": "UID", + "identifier": "qLZC0lvvxQH" + }, + "programType": "WITHOUT_REGISTRATION", + "orgUnit": { + "idScheme": "UID", + "identifier": "DiszpKrYNg8" + }, + "status": "ACTIVE", + "dataValues": [ + { + "created": "2022-04-22T06:00:14.224", + "dataElement": { + "idScheme": "UID", + "identifier": "GieVkTxp4HH" + }, + "value": "12" + } + ] + }, + { + "event": "gvULMgNiAfM", + "status": "COMPLETED", + "program": { + "idScheme": "UID", + "identifier": "UWRnoyBjvqi" + }, + "programStage": { + "idScheme": "UID", + "identifier": "ZfzckZBvDkA" + }, + "enrollment": "ipBifypAQTo", + "orgUnit": { + "idScheme": "UID", + "identifier": "tSsGrtfRzjY" + }, + "relationships": [], + "occurredAt": "2019-01-28T00:00:00.000", + "scheduledAt": "2019-01-28T12:10:38.100", + "storedBy": "admin", + "followUp": true, + "deleted": false, + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "HllvX50cXC0" + }, + "notes": [] + }, + { + "event": "SbUJzkxKYAG", + "status": "ACTIVE", + "program": { + "idScheme": "UID", + "identifier": "YlUmbgnKWkd" + }, + "programStage": { + "idScheme": "UID", + "identifier": "uLQthmAPTPq" + }, + "enrollment": "qxOSXoEZkOA", + "orgUnit": { + "idScheme": "UID", + "identifier": "RojfDTBhoGC" + }, + "relationships": [], + "occurredAt": "2019-01-28T00:00:00.000", + "scheduledAt": "2019-01-28T11:10:38.100", + "storedBy": "tracker", + "followUp": true, + "deleted": false, + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "HllvX50cXC0" + } + }, + { + "event": "LCSfHnurnNB", + "status": "COMPLETED", + "program": { + "idScheme": "UID", + "identifier": "SeeUNWLQmZk" + }, + "programStage": { + "idScheme": "UID", + "identifier": "GmxBvezOlGA" + }, + "enrollment": "HDWTYSYkICe", + "orgUnit": { + "idScheme": "UID", + "identifier": "DiszpKrYNg8" + }, + "relationships": [], + "occurredAt": "2019-01-28T00:00:00.000", + "scheduledAt": "2019-01-28T12:10:38.100", + "storedBy": "admin", + "followUp": true, + "deleted": false, + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "HllvX50cXC0" + }, + "notes": [] + }, + { + "event": "YKmfzHdjUDL", + "status": "COMPLETED", + "program": { + "idScheme": "UID", + "identifier": "sLngICFQjvH" + }, + "programStage": { + "idScheme": "UID", + "identifier": "zydGjigJcJb" + }, + "enrollment": "FXWSSZunTLk", + "orgUnit": { + "idScheme": "UID", + "identifier": "lbDXJBlvtZe" + }, + "relationships": [], + "occurredAt": "2019-01-28T00:00:00.000", + "scheduledAt": "2019-01-28T12:10:38.100", + "storedBy": "admin", + "followUp": true, + "deleted": false, + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "HllvX50cXC0" + }, + "notes": [] + }, + { + "event": "G9PbzJY8bJG", + "status": "COMPLETED", + "program": { + "idScheme": "UID", + "identifier": "BFcipDERJng" + }, + "programStage": { + "idScheme": "UID", + "identifier": "NpsdDv6kKSg" + }, + "orgUnit": { + "idScheme": "UID", + "identifier": "g4w96yEMlzO" + }, + "relationships": [], + "occurredAt": "2019-01-28T00:00:00.000", + "scheduledAt": "2019-01-28T12:10:38.100", + "storedBy": "admin", + "followUp": true, + "deleted": false, + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "HllvX50cXC0" + }, + "notes": [] + }, + { + "event": "H0PbzJY8bJG", + "status": "COMPLETED", + "program": { + "idScheme": "UID", + "identifier": "TsngICFQjvH" + }, + "programStage": { + "idScheme": "UID", + "identifier": "aZdGjigJcJb" + }, + "enrollment": "GYWSSZunTLk", + "orgUnit": { + "idScheme": "UID", + "identifier": "DiszpKrYNg8" + }, + "relationships": [], + "occurredAt": "2019-01-28T00:00:00.000", + "scheduledAt": "2019-01-28T12:10:38.100", + "storedBy": "admin", + "followUp": true, + "deleted": false, + "attributeOptionCombo": { + "idScheme": "UID", + "identifier": "HllvX50cXC0" + }, + "notes": [] + } + ], + "relationships": [ + { + "relationship": "oLT07jKRu9e", + "relationshipType": { + "idScheme": "UID", + "identifier": "TV9oB9LT3sh" + }, + "createdAtClient": "2018-10-01T12:17:30.163", + "bidirectional": false, + "deleted": false, + "from": { + "trackedEntity": "QS6w44flWAf" + }, + "to": { + "event": "pTzf9KYMk72" + } + }, + { + "relationship": "yZxjxJli9mO", + "relationshipType": { + "idScheme": "UID", + "identifier": "TV9oB9LT3sh" + }, + "createdAtClient": "2018-11-01T13:24:37.118", + "bidirectional": false, + "deleted": false, + "from": { + "trackedEntity": "dUE514NMOlo" + }, + "to": { + "event": "pTzf9KYMk72" + } + } + ], + "username": "system-process" +} diff --git a/dhis-2/dhis-test-web-api/src/test/resources/tracker/simple_metadata_changelog.json b/dhis-2/dhis-test-web-api/src/test/resources/tracker/simple_metadata_changelog.json new file mode 100644 index 000000000000..1fe668a7a40c --- /dev/null +++ b/dhis-2/dhis-test-web-api/src/test/resources/tracker/simple_metadata_changelog.json @@ -0,0 +1,216 @@ +{ + "attributes": [ + { + "id": "j45AR9cBQKc", + "name": "some attribute", + "sharing": { + "owner": "PQD6wXJ2r5j", + "public": "rw------" + }, + "valueType": "TEXT", + "mandatory": false, + "unique": true, + "programAttribute": true, + "programStageAttribute": true, + "trackedEntityTypeAttribute": true, + "trackedEntityAttributeAttribute": true, + "organisationUnitAttribute": true, + "dataElementAttribute": true, + "relationshipTypeAttribute": true, + "categoryOptionAttribute": true, + "categoryOptionComboAttribute": true + } + ], + "trackedEntityTypes": [ + { + "name": "Person", + "id": "ja8NY4PW7Xm", + "publicAccess": "rw------", + "description": "person", + "maxTeiCountToReturn": 0, + "allowAuditLog": true, + "featureType": "NONE", + "minAttributesRequiredToSearch": 1, + "user": { + "id": "tTgjgobT1oS" + }, + "sharing": { + "external": false, + "public": "rwrw----" + }, + "trackedEntityTypeAttributes": [ + { + "displayInList": true, + "displayName": "Person Given name", + "displayShortName": "null Given name", + "externalAccess": false, + "favorite": false, + "favorites": [], + "id": "hKZ9AJpnVcG", + "name": "Person Given name", + "searchable": true, + "trackedEntityAttribute": { + "id": "numericAttr" + }, + "trackedEntityType": { + "id": "ja8NY4PW7Xm" + }, + "valueType": "TEXT" + } + ] + }, + { + "name": "Inaccessible person", + "id": "Ip8NY4PW7Xm", + "publicAccess": "rw------", + "description": "person", + "maxTeiCountToReturn": 0, + "allowAuditLog": false, + "featureType": "NONE", + "minAttributesRequiredToSearch": 1, + "user": { + "id": "tTgjgobT1oS" + }, + "sharing": { + "external": false, + "public": "--------" + } + } + ], + "users": [ + { + "code": "tracker admin", + "id": "tTgjgobT1oS", + "password": "Test123###...", + "surname": "tracker Surnameadmin", + "firstName": "tracker FirstNameadmin", + "name": "tracker admin", + "lastLogin": "2020-05-31T08:55:38.571", + "displayName": "tracker admin", + "externalAuth": false, + "externalAccess": false, + "disabled": false, + "twoFA": false, + "passwordLastUpdated": "2020-05-31T08:55:28.232", + "invitation": false, + "selfRegistered": false, + "favorite": false, + "username": "trackeradmin", + "access": { + "read": true, + "update": true, + "externalize": true, + "delete": true, + "write": true, + "manage": true + }, + "userRoles": [ + { + "id": "nJ4Ml8ads4M" + } + ], + "teiSearchOrganisationUnits": [ + { + "id": "h4w96yEMlzO" + } + ], + "organisationUnits": [ + { + "id": "h4w96yEMlzO" + } + ] + } + ], + "organisationUnits": [ + { + "id": "h4w96yEMlzO", + "name": "test-orgunit", + "code": "test-orgunit-code", + "level": 1, + "publicAccess": "rwrw----", + "shortName": "test-orgunit", + "description": "test-orgunit", + "path": "/h4w96yEMlzO", + "openingDate": "2020-05-31T00:00:00.000", + "user": { + "id": "tTgjgobT1oS" + } + } + ], + "programs": [ + { + "id": "BFcipDERJnf", + "name": "test-program", + "shortName": "test-program", + "publicAccess": "rwrw----", + "completeEventsExpiryDays": 0, + "ignoreOverdueEvents": false, + "skipOffline": false, + "minAttributesRequiredToSearch": 1, + "displayFrontPageList": false, + "onlyEnrollOnce": false, + "programType": "WITH_REGISTRATION", + "accessLevel": "OPEN", + "version": 2, + "maxTeiCountToReturn": 0, + "selectIncidentDatesInFuture": false, + "displayIncidentDate": true, + "selectEnrollmentDatesInFuture": false, + "expiryDays": 0, + "useFirstStageDuringRegistration": false, + "categoryCombo": { + "id": "bjDvmb4bfuf" + }, + "trackedEntityType": { + "id": "ja8NY4PW7Xm" + }, + "user": { + "id": "tTgjgobT1oS" + }, + "organisationUnits": [ + { + "id": "h4w96yEMlzO" + } + ] + } + ], + "userRoles": [ + { + "code": "tracker Superuser", + "name": "tracker Superuser", + "id": "nJ4Ml8ads4M", + "publicAccess": "--------", + "description": "tracker Superuser", + "user": { + "id": "tTgjgobT1oS" + }, + "authorities": [ + "F_TRACKED_ENTITY_INSTANCE_SEARCH_IN_ALL_ORGUNITS", + "ALL" + ] + } + ], + "trackedEntityAttributes": [ + { + "id": "numericAttr", + "name": "numeric-attribute", + "shortName": "numeric-attribute", + "aggregationType": "NONE", + "displayInListNoProgram": false, + "publicAccess": "rw------", + "pattern": "", + "skipSynchronization": false, + "generated": false, + "displayOnVisitSchedule": false, + "valueType": "INTEGER", + "formName": "numeric-attribute", + "orgunitScope": false, + "confidential": false, + "unique": false, + "inherit": false, + "user": { + "id": "tTgjgobT1oS" + } + } + ] +} \ No newline at end of file diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/MetadataMapper.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/MetadataMapper.java new file mode 100644 index 000000000000..8461f7bce2bf --- /dev/null +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/MetadataMapper.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2022, 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.webapi.controller.tracker.export; + +import org.hisp.dhis.organisationunit.OrganisationUnit; +import org.hisp.dhis.program.Program; +import org.hisp.dhis.program.ProgramStage; +import org.hisp.dhis.tracker.TrackerIdSchemeParams; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.Named; + +@Mapper +public interface MetadataMapper { + + @Named("programToString") + default String map(Program program, @Context TrackerIdSchemeParams idSchemeParams) { + return idSchemeParams.getProgramIdScheme().getIdentifier(program); + } + + @Named("programStageToString") + default String map(ProgramStage programStage, @Context TrackerIdSchemeParams idSchemeParams) { + return idSchemeParams.getProgramStageIdScheme().getIdentifier(programStage); + } + + @Named("organisationUnitToString") + default String map(OrganisationUnit orgUnit, @Context TrackerIdSchemeParams idSchemeParams) { + return idSchemeParams.getOrgUnitIdScheme().getIdentifier(orgUnit); + } +} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/enrollment/EnrollmentMapper.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/enrollment/EnrollmentMapper.java index 2f133f2e6fc7..a9eda46d5b4a 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/enrollment/EnrollmentMapper.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/enrollment/EnrollmentMapper.java @@ -31,6 +31,7 @@ import java.util.Map; import org.hisp.dhis.program.Enrollment; +import org.hisp.dhis.tracker.TrackerIdSchemeParams; import org.hisp.dhis.webapi.controller.tracker.export.AttributeMapper; import org.hisp.dhis.webapi.controller.tracker.export.NoteMapper; import org.hisp.dhis.webapi.controller.tracker.export.UserMapper; @@ -38,6 +39,7 @@ import org.hisp.dhis.webapi.controller.tracker.export.relationship.RelationshipMapper; import org.hisp.dhis.webapi.controller.tracker.view.InstantMapper; import org.hisp.dhis.webapi.controller.tracker.view.UIDMapper; +import org.mapstruct.Context; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @@ -82,5 +84,6 @@ public interface EnrollmentMapper { @Mapping(target = "relationships", source = "relationshipItems") @Mapping(target = "attributes", source = "trackedEntity.trackedEntityAttributeValues") @Mapping(target = "notes", source = "notes") - org.hisp.dhis.webapi.controller.tracker.view.Enrollment map(Enrollment enrollment); + org.hisp.dhis.webapi.controller.tracker.view.Enrollment map( + Enrollment enrollment, @Context TrackerIdSchemeParams idSchemeParams); } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/enrollment/EnrollmentsExportController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/enrollment/EnrollmentsExportController.java index 9a98dab15b27..a3b4aac3fece 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/enrollment/EnrollmentsExportController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/enrollment/EnrollmentsExportController.java @@ -43,6 +43,7 @@ import org.hisp.dhis.feedback.NotFoundException; import org.hisp.dhis.fieldfiltering.FieldFilterService; import org.hisp.dhis.fieldfiltering.FieldPath; +import org.hisp.dhis.tracker.TrackerIdSchemeParams; import org.hisp.dhis.tracker.export.PageParams; import org.hisp.dhis.tracker.export.enrollment.EnrollmentOperationParams; import org.hisp.dhis.tracker.export.enrollment.EnrollmentParams; @@ -114,7 +115,9 @@ ResponseEntity> getEnrollments(EnrollmentRequestParams requestP org.hisp.dhis.tracker.export.Page enrollmentsPage = enrollmentService.getEnrollments(operationParams, pageParams); List enrollments = - enrollmentsPage.getItems().stream().map(ENROLLMENT_MAPPER::map).toList(); + enrollmentsPage.getItems().stream() + .map(en -> ENROLLMENT_MAPPER.map(en, TrackerIdSchemeParams.builder().build())) + .toList(); List objectNodes = fieldFilterService.toObjectNodes(enrollments, requestParams.getFields()); @@ -125,7 +128,7 @@ ResponseEntity> getEnrollments(EnrollmentRequestParams requestP List enrollments = enrollmentService.getEnrollments(operationParams).stream() - .map(ENROLLMENT_MAPPER::map) + .map(en -> ENROLLMENT_MAPPER.map(en, TrackerIdSchemeParams.builder().build())) .toList(); List objectNodes = fieldFilterService.toObjectNodes(enrollments, requestParams.getFields()); @@ -144,7 +147,9 @@ public ResponseEntity getEnrollmentByUid( throws NotFoundException, ForbiddenException { EnrollmentParams enrollmentParams = fieldsMapper.map(fields); Enrollment enrollment = - ENROLLMENT_MAPPER.map(enrollmentService.getEnrollment(uid, enrollmentParams, false)); + ENROLLMENT_MAPPER.map( + enrollmentService.getEnrollment(uid, enrollmentParams, false), + TrackerIdSchemeParams.builder().build()); return ResponseEntity.ok(fieldFilterService.toObjectNode(enrollment, fields)); } } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventMapper.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventMapper.java index ca93ddc82d72..3cbdf289e6c7 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventMapper.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventMapper.java @@ -31,12 +31,15 @@ import java.util.Map; import org.hisp.dhis.program.Event; +import org.hisp.dhis.tracker.TrackerIdSchemeParams; import org.hisp.dhis.webapi.controller.tracker.export.DataValueMapper; +import org.hisp.dhis.webapi.controller.tracker.export.MetadataMapper; import org.hisp.dhis.webapi.controller.tracker.export.NoteMapper; import org.hisp.dhis.webapi.controller.tracker.export.UserMapper; import org.hisp.dhis.webapi.controller.tracker.export.relationship.RelationshipMapper; import org.hisp.dhis.webapi.controller.tracker.view.InstantMapper; import org.hisp.dhis.webapi.controller.tracker.view.UIDMapper; +import org.mapstruct.Context; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @@ -48,7 +51,8 @@ UIDMapper.class, NoteMapper.class, RelationshipMapper.class, - UserMapper.class + UserMapper.class, + MetadataMapper.class }) public interface EventMapper { @@ -89,11 +93,17 @@ public interface EventMapper { entry("updatedBy", "lastUpdatedBy")); @Mapping(target = "event", source = "uid") - @Mapping(target = "program", source = "enrollment.program.uid") - @Mapping(target = "programStage", source = "programStage.uid") + @Mapping(target = "program", source = "enrollment.program", qualifiedByName = "programToString") + @Mapping( + target = "programStage", + source = "programStage", + qualifiedByName = "programStageToString") @Mapping(target = "enrollment", source = "enrollment.uid") @Mapping(target = "trackedEntity", source = "enrollment.trackedEntity.uid") - @Mapping(target = "orgUnit", source = "organisationUnit.uid") + @Mapping( + target = "orgUnit", + source = "organisationUnit", + qualifiedByName = "organisationUnitToString") @Mapping(target = "occurredAt", source = "occurredDate") @Mapping(target = "scheduledAt", source = "scheduledDate") @Mapping( @@ -114,5 +124,6 @@ public interface EventMapper { @Mapping(target = "dataValues", source = "eventDataValues") @Mapping(target = "relationships", source = "relationshipItems") @Mapping(target = "notes", source = "notes") - org.hisp.dhis.webapi.controller.tracker.view.Event map(Event event); + org.hisp.dhis.webapi.controller.tracker.view.Event map( + Event event, @Context TrackerIdSchemeParams idSchemeParams); } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventRequestParams.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventRequestParams.java index 0b32ad0e3c4b..9b1a822bc50d 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventRequestParams.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventRequestParams.java @@ -36,7 +36,6 @@ import org.hisp.dhis.category.CategoryCombo; import org.hisp.dhis.category.CategoryOption; import org.hisp.dhis.common.AssignedUserSelectionMode; -import org.hisp.dhis.common.IdSchemes; import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.common.OrganisationUnitSelectionMode; import org.hisp.dhis.common.UID; @@ -208,8 +207,6 @@ public class EventRequestParams implements PageRequestParams { @OpenApi.Property({UID[].class, Enrollment.class}) private Set enrollments = new HashSet<>(); - @OpenApi.Ignore private IdSchemes idSchemes = new IdSchemes(); - @OpenApi.Property(value = String[].class) private List fields = FieldFilterParser.parse(DEFAULT_FIELDS_PARAM); } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventRequestParamsMapper.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventRequestParamsMapper.java index 2a8343d37916..0130ca287fa8 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventRequestParamsMapper.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventRequestParamsMapper.java @@ -161,7 +161,6 @@ public EventOperationParams map(EventRequestParams eventRequestParams) .eventStatus(eventRequestParams.getStatus()) .attributeCategoryCombo(attributeCategoryCombo) .attributeCategoryOptions(attributeCategoryOptions) - .idSchemes(eventRequestParams.getIdSchemes()) .includeAttributes(false) .includeAllDataElements(false) .dataElementFilters(dataElementFilters) @@ -202,7 +201,8 @@ private void validateUpdateDurationParams(EventRequestParams eventRequestParams) && (eventRequestParams.getUpdatedAfter() != null || eventRequestParams.getUpdatedBefore() != null)) { throw new BadRequestException( - "Last updated from and/or to and last updated duration cannot be specified simultaneously"); + "Last updated from and/or to and last updated duration cannot be specified" + + " simultaneously"); } if (eventRequestParams.getUpdatedWithin() != null diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventsExportController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventsExportController.java index e7f011f2a01f..d2f2be65096f 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventsExportController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/event/EventsExportController.java @@ -60,6 +60,7 @@ import org.hisp.dhis.fieldfiltering.FieldPath; import org.hisp.dhis.fileresource.ImageFileDimension; import org.hisp.dhis.program.Event; +import org.hisp.dhis.tracker.TrackerIdSchemeParams; import org.hisp.dhis.tracker.export.PageParams; import org.hisp.dhis.tracker.export.event.EventChangeLog; import org.hisp.dhis.tracker.export.event.EventChangeLogOperationParams; @@ -151,7 +152,8 @@ public EventsExportController( // use the text/html Accept header to default to a Json response when a generic request comes // from a browser ) - ResponseEntity> getEvents(EventRequestParams requestParams) + ResponseEntity> getEvents( + EventRequestParams requestParams, TrackerIdSchemeParams idSchemeParams) throws BadRequestException, ForbiddenException, NotFoundException { validatePaginationParameters(requestParams); EventOperationParams eventOperationParams = eventParamsMapper.map(requestParams); @@ -164,7 +166,7 @@ ResponseEntity> getEvents(EventRequestParams requestParams) org.hisp.dhis.tracker.export.Page eventsPage = eventService.getEvents(eventOperationParams, pageParams); List events = - eventsPage.getItems().stream().map(EVENTS_MAPPER::map).toList(); + eventsPage.getItems().stream().map(ev -> EVENTS_MAPPER.map(ev, idSchemeParams)).toList(); List objectNodes = fieldFilterService.toObjectNodes(events, requestParams.getFields()); @@ -174,7 +176,9 @@ ResponseEntity> getEvents(EventRequestParams requestParams) } List events = - eventService.getEvents(eventOperationParams).stream().map(EVENTS_MAPPER::map).toList(); + eventService.getEvents(eventOperationParams).stream() + .map(ev -> EVENTS_MAPPER.map(ev, idSchemeParams)) + .toList(); List objectNodes = fieldFilterService.toObjectNodes(events, requestParams.getFields()); @@ -184,14 +188,19 @@ ResponseEntity> getEvents(EventRequestParams requestParams) } @GetMapping(produces = CONTENT_TYPE_JSON_GZIP) - void getEventsAsJsonGzip(EventRequestParams eventRequestParams, HttpServletResponse response) + void getEventsAsJsonGzip( + EventRequestParams eventRequestParams, + TrackerIdSchemeParams idSchemeParams, + HttpServletResponse response) throws BadRequestException, IOException, ForbiddenException, NotFoundException { validatePaginationParameters(eventRequestParams); EventOperationParams eventOperationParams = eventParamsMapper.map(eventRequestParams); List events = - eventService.getEvents(eventOperationParams).stream().map(EVENTS_MAPPER::map).toList(); + eventService.getEvents(eventOperationParams).stream() + .map(ev -> EVENTS_MAPPER.map(ev, idSchemeParams)) + .toList(); ResponseHeader.addContentDispositionAttachment(response, EVENT_JSON_FILE + GZIP_EXT); ResponseHeader.addContentTransferEncodingBinary(response); @@ -205,14 +214,19 @@ void getEventsAsJsonGzip(EventRequestParams eventRequestParams, HttpServletRespo } @GetMapping(produces = CONTENT_TYPE_JSON_ZIP) - void getEventsAsJsonZip(EventRequestParams eventRequestParams, HttpServletResponse response) + void getEventsAsJsonZip( + EventRequestParams eventRequestParams, + TrackerIdSchemeParams idSchemeParams, + HttpServletResponse response) throws BadRequestException, ForbiddenException, IOException, NotFoundException { validatePaginationParameters(eventRequestParams); EventOperationParams eventOperationParams = eventParamsMapper.map(eventRequestParams); List events = - eventService.getEvents(eventOperationParams).stream().map(EVENTS_MAPPER::map).toList(); + eventService.getEvents(eventOperationParams).stream() + .map(ev -> EVENTS_MAPPER.map(ev, idSchemeParams)) + .toList(); ResponseHeader.addContentDispositionAttachment(response, EVENT_JSON_FILE + ZIP_EXT); ResponseHeader.addContentTransferEncodingBinary(response); @@ -231,13 +245,16 @@ void getEventsAsJsonZip(EventRequestParams eventRequestParams, HttpServletRespon @GetMapping(produces = {CONTENT_TYPE_CSV, CONTENT_TYPE_TEXT_CSV}) void getEventsAsCsv( EventRequestParams eventRequestParams, + TrackerIdSchemeParams idSchemeParams, HttpServletResponse response, @RequestParam(required = false, defaultValue = "false") boolean skipHeader) throws IOException, BadRequestException, ForbiddenException, NotFoundException { EventOperationParams eventOperationParams = eventParamsMapper.map(eventRequestParams); List events = - eventService.getEvents(eventOperationParams).stream().map(EVENTS_MAPPER::map).toList(); + eventService.getEvents(eventOperationParams).stream() + .map(ev -> EVENTS_MAPPER.map(ev, idSchemeParams)) + .toList(); ResponseHeader.addContentDispositionAttachment(response, EVENT_CSV_FILE); response.setContentType(CONTENT_TYPE_CSV); @@ -248,13 +265,16 @@ void getEventsAsCsv( @GetMapping(produces = {CONTENT_TYPE_CSV_GZIP}) void getEventsAsCsvGZip( EventRequestParams eventRequestParams, + TrackerIdSchemeParams idSchemeParams, HttpServletResponse response, @RequestParam(required = false, defaultValue = "false") boolean skipHeader) throws IOException, BadRequestException, ForbiddenException, NotFoundException { EventOperationParams eventOperationParams = eventParamsMapper.map(eventRequestParams); List events = - eventService.getEvents(eventOperationParams).stream().map(EVENTS_MAPPER::map).toList(); + eventService.getEvents(eventOperationParams).stream() + .map(ev -> EVENTS_MAPPER.map(ev, idSchemeParams)) + .toList(); ResponseHeader.addContentDispositionAttachment(response, EVENT_CSV_FILE + GZIP_EXT); ResponseHeader.addContentTransferEncodingBinary(response); @@ -267,12 +287,15 @@ void getEventsAsCsvGZip( void getEventsAsCsvZip( EventRequestParams eventRequestParams, HttpServletResponse response, - @RequestParam(required = false, defaultValue = "false") boolean skipHeader) + @RequestParam(required = false, defaultValue = "false") boolean skipHeader, + TrackerIdSchemeParams idSchemeParams) throws IOException, BadRequestException, ForbiddenException, NotFoundException { EventOperationParams eventOperationParams = eventParamsMapper.map(eventRequestParams); List events = - eventService.getEvents(eventOperationParams).stream().map(EVENTS_MAPPER::map).toList(); + eventService.getEvents(eventOperationParams).stream() + .map(ev -> EVENTS_MAPPER.map(ev, idSchemeParams)) + .toList(); ResponseHeader.addContentDispositionAttachment(response, EVENT_CSV_FILE + ZIP_EXT); ResponseHeader.addContentTransferEncodingBinary(response); @@ -286,11 +309,12 @@ void getEventsAsCsvZip( ResponseEntity getEventByUid( @OpenApi.Param({UID.class, Event.class}) @PathVariable UID uid, @OpenApi.Param(value = String[].class) @RequestParam(defaultValue = DEFAULT_FIELDS_PARAM) - List fields) + List fields, + TrackerIdSchemeParams idSchemeParams) throws NotFoundException, ForbiddenException { EventParams eventParams = eventsMapper.map(fields); org.hisp.dhis.webapi.controller.tracker.view.Event event = - EVENTS_MAPPER.map(eventService.getEvent(uid, eventParams)); + EVENTS_MAPPER.map(eventService.getEvent(uid, eventParams), idSchemeParams); return ResponseEntity.ok(fieldFilterService.toObjectNode(event, fields)); } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntitiesExportController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntitiesExportController.java index 31ba8e940c1e..f7c3bb52a32d 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntitiesExportController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntitiesExportController.java @@ -57,6 +57,7 @@ import org.hisp.dhis.fieldfiltering.FieldPath; import org.hisp.dhis.fileresource.ImageFileDimension; import org.hisp.dhis.program.Program; +import org.hisp.dhis.tracker.TrackerIdSchemeParams; import org.hisp.dhis.tracker.export.PageParams; import org.hisp.dhis.tracker.export.trackedentity.TrackedEntityChangeLog; import org.hisp.dhis.tracker.export.trackedentity.TrackedEntityChangeLogOperationParams; @@ -176,7 +177,9 @@ ResponseEntity> getTrackedEntities( trackedEntitiesPage = trackedEntityService.getTrackedEntities(operationParams, pageParams); List trackedEntities = - trackedEntitiesPage.getItems().stream().map(TRACKED_ENTITY_MAPPER::map).toList(); + trackedEntitiesPage.getItems().stream() + .map(te -> TRACKED_ENTITY_MAPPER.map(te, TrackerIdSchemeParams.builder().build())) + .toList(); List objectNodes = fieldFilterService.toObjectNodes(trackedEntities, requestParams.getFields()); @@ -187,7 +190,7 @@ ResponseEntity> getTrackedEntities( List trackedEntities = trackedEntityService.getTrackedEntities(operationParams).stream() - .map(TRACKED_ENTITY_MAPPER::map) + .map(te -> TRACKED_ENTITY_MAPPER.map(te, TrackerIdSchemeParams.builder().build())) .toList(); List objectNodes = fieldFilterService.toObjectNodes(trackedEntities, requestParams.getFields()); @@ -209,7 +212,7 @@ void getTrackedEntitiesAsCsv( List trackedEntities = trackedEntityService.getTrackedEntities(operationParams).stream() - .map(TRACKED_ENTITY_MAPPER::map) + .map(te -> TRACKED_ENTITY_MAPPER.map(te, TrackerIdSchemeParams.builder().build())) .toList(); ResponseHeader.addContentDispositionAttachment(response, TE_CSV_FILE); @@ -231,7 +234,7 @@ void getTrackedEntitiesAsCsvZip( List trackedEntities = trackedEntityService.getTrackedEntities(operationParams).stream() - .map(TRACKED_ENTITY_MAPPER::map) + .map(te -> TRACKED_ENTITY_MAPPER.map(te, TrackerIdSchemeParams.builder().build())) .toList(); ResponseHeader.addContentDispositionAttachment(response, TE_CSV_FILE + ZIP_EXT); @@ -254,7 +257,7 @@ void getTrackedEntitiesAsCsvGZip( List trackedEntities = trackedEntityService.getTrackedEntities(operationParams).stream() - .map(TRACKED_ENTITY_MAPPER::map) + .map(te -> TRACKED_ENTITY_MAPPER.map(te, TrackerIdSchemeParams.builder().build())) .toList(); ResponseHeader.addContentDispositionAttachment(response, TE_CSV_FILE + GZIP_EXT); @@ -276,7 +279,8 @@ ResponseEntity getTrackedEntityByUid( TrackedEntityParams trackedEntityParams = fieldsMapper.map(fields); TrackedEntity trackedEntity = TRACKED_ENTITY_MAPPER.map( - trackedEntityService.getTrackedEntity(uid, program, trackedEntityParams)); + trackedEntityService.getTrackedEntity(uid, program, trackedEntityParams), + TrackerIdSchemeParams.builder().build()); return ResponseEntity.ok(fieldFilterService.toObjectNode(trackedEntity, fields)); } @@ -294,7 +298,8 @@ void getTrackedEntityByUidAsCsv( TrackedEntity trackedEntity = TRACKED_ENTITY_MAPPER.map( - trackedEntityService.getTrackedEntity(uid, program, trackedEntityParams)); + trackedEntityService.getTrackedEntity(uid, program, trackedEntityParams), + TrackerIdSchemeParams.builder().build()); OutputStream outputStream = response.getOutputStream(); response.setContentType(CONTENT_TYPE_CSV); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntityMapper.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntityMapper.java index 2dd8bcf427c6..606565de4125 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntityMapper.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntityMapper.java @@ -31,6 +31,7 @@ import java.util.Map; import org.hisp.dhis.trackedentity.TrackedEntity; +import org.hisp.dhis.tracker.TrackerIdSchemeParams; import org.hisp.dhis.webapi.controller.tracker.export.AttributeMapper; import org.hisp.dhis.webapi.controller.tracker.export.ProgramOwnerMapper; import org.hisp.dhis.webapi.controller.tracker.export.UserMapper; @@ -38,6 +39,7 @@ import org.hisp.dhis.webapi.controller.tracker.export.relationship.RelationshipMapper; import org.hisp.dhis.webapi.controller.tracker.view.InstantMapper; import org.hisp.dhis.webapi.controller.tracker.view.UIDMapper; +import org.mapstruct.Context; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @@ -78,5 +80,6 @@ interface TrackedEntityMapper { @Mapping(target = "updatedBy", source = "lastUpdatedByUserInfo") @Mapping(target = "relationships", source = "relationshipItems") @Mapping(target = "attributes", source = "trackedEntityAttributeValues") - org.hisp.dhis.webapi.controller.tracker.view.TrackedEntity map(TrackedEntity trackedEntity); + org.hisp.dhis.webapi.controller.tracker.view.TrackedEntity map( + TrackedEntity trackedEntity, @Context TrackerIdSchemeParams idSchemeParams); }