From 44feaf8d9333de477ec44efda83be4aba2551698 Mon Sep 17 00:00:00 2001 From: teleivo Date: Thu, 23 Jan 2025 09:26:02 +0100 Subject: [PATCH] chore: join code paths for serving one vs many TEs --- .../enrollment/DefaultEnrollmentService.java | 5 +- .../DefaultTrackedEntityService.java | 158 +++----- .../TrackedEntityServiceTest.java | 63 ++-- .../ExportControllerPaginationTest.java | 344 +++++++----------- .../TrackedEntitiesExportControllerTest.java | 2 +- 5 files changed, 213 insertions(+), 359 deletions(-) diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/enrollment/DefaultEnrollmentService.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/enrollment/DefaultEnrollmentService.java index 0ef7a2df408d..639629ba056b 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/enrollment/DefaultEnrollmentService.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/enrollment/DefaultEnrollmentService.java @@ -42,6 +42,7 @@ import org.hisp.dhis.feedback.BadRequestException; import org.hisp.dhis.feedback.ForbiddenException; import org.hisp.dhis.feedback.NotFoundException; +import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.program.Enrollment; import org.hisp.dhis.program.Event; import org.hisp.dhis.relationship.RelationshipItem; @@ -158,7 +159,9 @@ private Enrollment getEnrollment( trackedEntity.setUid(enrollment.getTrackedEntity().getUid()); result.setTrackedEntity(trackedEntity); } - result.setOrganisationUnit(enrollment.getOrganisationUnit()); + OrganisationUnit organisationUnit = new OrganisationUnit(); + organisationUnit.setUid(enrollment.getOrganisationUnit().getUid()); + result.setOrganisationUnit(organisationUnit); result.setGeometry(enrollment.getGeometry()); result.setCreated(enrollment.getCreated()); result.setCreatedAtClient(enrollment.getCreatedAtClient()); diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/trackedentity/DefaultTrackedEntityService.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/trackedentity/DefaultTrackedEntityService.java index e665670311ea..12336a5c35f1 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/trackedentity/DefaultTrackedEntityService.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/trackedentity/DefaultTrackedEntityService.java @@ -27,7 +27,6 @@ */ package org.hisp.dhis.tracker.export.trackedentity; -import static org.hisp.dhis.audit.AuditOperationType.READ; import static org.hisp.dhis.audit.AuditOperationType.SEARCH; import static org.hisp.dhis.user.CurrentUserUtil.getCurrentUserDetails; @@ -48,7 +47,6 @@ import org.hisp.dhis.fileresource.FileResource; import org.hisp.dhis.fileresource.FileResourceService; import org.hisp.dhis.fileresource.ImageFileDimension; -import org.hisp.dhis.program.Enrollment; import org.hisp.dhis.program.Program; import org.hisp.dhis.program.ProgramService; import org.hisp.dhis.relationship.Relationship; @@ -66,7 +64,6 @@ import org.hisp.dhis.tracker.export.FileResourceStream; import org.hisp.dhis.tracker.export.Page; import org.hisp.dhis.tracker.export.PageParams; -import org.hisp.dhis.tracker.export.enrollment.EnrollmentOperationParams; import org.hisp.dhis.tracker.export.enrollment.EnrollmentService; import org.hisp.dhis.tracker.export.event.EventParams; import org.hisp.dhis.tracker.export.event.EventService; @@ -222,111 +219,22 @@ public TrackedEntity getTrackedEntity( @CheckForNull UID programIdentifier, @Nonnull TrackedEntityParams params) throws NotFoundException, ForbiddenException, BadRequestException { - Program program = null; - if (programIdentifier != null) { - program = programService.getProgram(programIdentifier.getValue()); - if (program == null) { - throw new NotFoundException(Program.class, programIdentifier); - } - } - - return getTrackedEntity(trackedEntityUid, program, params, getCurrentUserDetails()); - } - - /** - * Gets a tracked entity based on the program and org unit ownership. - * - * @return the TE object if found and accessible by the current user - * @throws NotFoundException if uid does not exist - * @throws ForbiddenException if TE owner is not in user's scope or not enough sharing access - */ - private TrackedEntity getTrackedEntity( - UID uid, Program program, TrackedEntityParams params, UserDetails user) - throws NotFoundException, ForbiddenException, BadRequestException { - TrackedEntity trackedEntity = trackedEntityStore.getByUid(uid.getValue()); - if (trackedEntity == null) { - throw new NotFoundException(TrackedEntity.class, uid); + TrackedEntityOperationParams operationParams = + TrackedEntityOperationParams.builder() + .trackedEntities(Set.of(trackedEntityUid)) + .program(programIdentifier) + .trackedEntityParams(params) + .build(); + // TODO(ivo) allow setting the audit to READ instead of SEARCH + // trackedEntityAuditService.addTrackedEntityAudit(READ, user.getUsername(), trackedEntity); + Page trackedEntities = + getTrackedEntities(operationParams, new PageParams(1, 1, false)); + + if (trackedEntities.getItems().isEmpty()) { + throw new NotFoundException(TrackedEntity.class, trackedEntityUid); } - trackedEntityAuditService.addTrackedEntityAudit(READ, user.getUsername(), trackedEntity); - - if (program != null) { - List errors = - trackerAccessManager.canReadProgramAndTrackedEntityType(user, trackedEntity, program); - if (!errors.isEmpty()) { - throw new ForbiddenException(errors.toString()); - } - - String error = - trackerAccessManager.canAccessProgramOwner(user, trackedEntity, program, false); - if (error != null) { - throw new ForbiddenException(error); - } - } else { - if (!trackerAccessManager.canRead(user, trackedEntity).isEmpty()) { - throw new ForbiddenException(TrackedEntity.class, uid); - } - } - - if (params.isIncludeEnrollments()) { - EnrollmentOperationParams enrollmentOperationParams = - mapToEnrollmentParams(uid, program, params); - List enrollments = enrollmentService.getEnrollments(enrollmentOperationParams); - trackedEntity.setEnrollments(new HashSet<>(enrollments)); - } - setRelationshipItems(trackedEntity, trackedEntity, params, false); - if (params.isIncludeProgramOwners()) { - trackedEntity.setProgramOwners(getTrackedEntityProgramOwners(trackedEntity, program)); - } - trackedEntity.setTrackedEntityAttributeValues( - getTrackedEntityAttributeValues(trackedEntity, program)); - return trackedEntity; - } - - private EnrollmentOperationParams mapToEnrollmentParams( - UID trackedEntity, Program program, TrackedEntityParams params) { - return EnrollmentOperationParams.builder() - .trackedEntity(trackedEntity) - .program(program) - .enrollmentParams(params.getEnrollmentParams()) - .build(); - } - - private static Set getTrackedEntityProgramOwners( - TrackedEntity trackedEntity, Program program) { - if (program == null) { - return trackedEntity.getProgramOwners(); - } - - return trackedEntity.getProgramOwners().stream() - .filter(te -> te.getProgram().getUid().equals(program.getUid())) - .collect(Collectors.toSet()); - } - - private Set getTrackedEntityAttributeValues( - TrackedEntity trackedEntity, Program program) { - TrackedEntityType trackedEntityType = trackedEntity.getTrackedEntityType(); - if (CollectionUtils.isEmpty(trackedEntityType.getTrackedEntityTypeAttributes())) { - // the TrackedEntityAggregate does not fetch the TrackedEntityTypeAttributes at the moment - // TODO(DHIS2-18541) bypass ACL as our controller test as the user must have access to the TET - // if it has access to the TE. - trackedEntityType = - trackedEntityTypeStore.getByUidNoAcl(trackedEntity.getTrackedEntityType().getUid()); - } - - Set teas = // tracked entity type attributes - trackedEntityType.getTrackedEntityAttributes().stream() - .map(IdentifiableObject::getUid) - .collect(Collectors.toSet()); - if (program != null) { // add program tracked entity attributes - teas.addAll( - program.getTrackedEntityAttributes().stream() - .map(IdentifiableObject::getUid) - .collect(Collectors.toSet())); - } - return trackedEntity.getTrackedEntityAttributeValues().stream() - .filter(av -> teas.contains(av.getAttribute().getUid())) - .collect(Collectors.toSet()); + return trackedEntities.getItems().get(0); } @Nonnull @@ -368,6 +276,7 @@ private List getTrackedEntities( operationParams.getTrackedEntityParams(), queryParams, queryParams.getOrgUnitMode()); + // TODO(ivo) clean this up setRelationshipItems( trackedEntities, operationParams.getTrackedEntityParams(), @@ -384,6 +293,43 @@ private List getTrackedEntities( return trackedEntities; } + private static Set getTrackedEntityProgramOwners( + TrackedEntity trackedEntity, Program program) { + if (program == null) { + return trackedEntity.getProgramOwners(); + } + + return trackedEntity.getProgramOwners().stream() + .filter(te -> te.getProgram().getUid().equals(program.getUid())) + .collect(Collectors.toSet()); + } + + private Set getTrackedEntityAttributeValues( + TrackedEntity trackedEntity, Program program) { + TrackedEntityType trackedEntityType = trackedEntity.getTrackedEntityType(); + if (CollectionUtils.isEmpty(trackedEntityType.getTrackedEntityTypeAttributes())) { + // the TrackedEntityAggregate does not fetch the TrackedEntityTypeAttributes at the moment + // TODO(DHIS2-18541) bypass ACL as our controller test as the user must have access to the TET + // if it has access to the TE. + trackedEntityType = + trackedEntityTypeStore.getByUidNoAcl(trackedEntity.getTrackedEntityType().getUid()); + } + + Set teas = // tracked entity type attributes + trackedEntityType.getTrackedEntityAttributes().stream() + .map(IdentifiableObject::getUid) + .collect(Collectors.toSet()); + if (program != null) { // add program tracked entity attributes + teas.addAll( + program.getTrackedEntityAttributes().stream() + .map(IdentifiableObject::getUid) + .collect(Collectors.toSet())); + } + return trackedEntity.getTrackedEntityAttributeValues().stream() + .filter(av -> teas.contains(av.getAttribute().getUid())) + .collect(Collectors.toSet()); + } + /** * We need to return the full models for relationship items (i.e. trackedEntity, enrollment and * event) in our API. The aggregate stores currently do not support that, so we need to fetch the diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/trackedentity/TrackedEntityServiceTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/trackedentity/TrackedEntityServiceTest.java index 3697e0a2153e..745dad25cea0 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/trackedentity/TrackedEntityServiceTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/trackedentity/TrackedEntityServiceTest.java @@ -1832,16 +1832,11 @@ void shouldReturnAllEntitiesWhenSuperuserAndModeAll() @Test void shouldFailWhenRequestingSingleTEAndProvidedProgramDoesNotExist() { - String programUid = "madeUpPrUid"; - NotFoundException exception = - assertThrows( - NotFoundException.class, - () -> - trackedEntityService.getTrackedEntity( - UID.of(trackedEntityA), UID.of(programUid), TrackedEntityParams.TRUE)); - assertEquals( - String.format("Program with id %s could not be found.", programUid), - exception.getMessage()); + assertThrows( + BadRequestException.class, + () -> + trackedEntityService.getTrackedEntity( + UID.of(trackedEntityA), UID.of("madeUpPrUid"), TrackedEntityParams.TRUE)); } @Test @@ -1874,7 +1869,7 @@ void shouldFailWhenRequestingSingleTEAndNoDataAccessToProvidedProgram() { trackedEntityService.getTrackedEntity( UID.of(trackedEntityA), UID.of(inaccessibleProgram), TrackedEntityParams.TRUE)); assertContains( - String.format("User has no data read access to program: %s", inaccessibleProgram.getUid()), + String.format("User has no access to program: %s", inaccessibleProgram.getUid()), exception.getMessage()); } @@ -1887,17 +1882,11 @@ void shouldFailWhenRequestingSingleTEAndTETNotAccessible() { trackedEntity.setTrackedEntityType(inaccessibleTrackedEntityType); manager.save(trackedEntity); - ForbiddenException exception = - assertThrows( - ForbiddenException.class, - () -> - trackedEntityService.getTrackedEntity( - UID.of(trackedEntity), UID.of(programA), TrackedEntityParams.TRUE)); - assertContains( - String.format( - "User has no data read access to tracked entity type: %s", - inaccessibleTrackedEntityType.getUid()), - exception.getMessage()); + assertThrows( + NotFoundException.class, + () -> + trackedEntityService.getTrackedEntity( + UID.of(trackedEntity), UID.of(programA), TrackedEntityParams.TRUE)); } @Test @@ -1954,16 +1943,11 @@ void shouldFailWhenRequestingSingleTEAndTETDoesNotMatchAnyProgram() { manager.update(programC); injectSecurityContextUser(user); - ForbiddenException exception = - assertThrows( - ForbiddenException.class, - () -> - trackedEntityService.getTrackedEntity( - UID.of(trackedEntityA), null, TrackedEntityParams.TRUE)); - - assertContains( - String.format("User has no access to TrackedEntity:%s", trackedEntityA.getUid()), - exception.getMessage()); + assertThrows( + NotFoundException.class, + () -> + trackedEntityService.getTrackedEntity( + UID.of(trackedEntityA), null, TrackedEntityParams.TRUE)); } @Test @@ -1993,16 +1977,11 @@ void shouldFailWhenRequestingSingleTEAndNoAccessToTET() { manager.update(trackedEntityA); injectSecurityContextUser(user); - ForbiddenException exception = - assertThrows( - ForbiddenException.class, - () -> - trackedEntityService.getTrackedEntity( - UID.of(trackedEntityA), null, TrackedEntityParams.TRUE)); - - assertEquals( - String.format("User has no access to TrackedEntity:%s", trackedEntityA.getUid()), - exception.getMessage()); + assertThrows( + BadRequestException.class, + () -> + trackedEntityService.getTrackedEntity( + UID.of(trackedEntityA), null, TrackedEntityParams.TRUE)); } @Test diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/ExportControllerPaginationTest.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/ExportControllerPaginationTest.java index 05924435a9f8..98784e84ad99 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/ExportControllerPaginationTest.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/ExportControllerPaginationTest.java @@ -28,39 +28,50 @@ package org.hisp.dhis.webapi.controller.tracker.export; import static org.hisp.dhis.test.utils.Assertions.assertContainsOnly; +import static org.hisp.dhis.test.utils.Assertions.assertNotEmpty; import static org.hisp.dhis.webapi.controller.tracker.JsonAssertions.assertHasNoMember; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; -import java.util.Date; +import java.io.IOException; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; -import org.hisp.dhis.category.CategoryOptionCombo; -import org.hisp.dhis.category.CategoryService; -import org.hisp.dhis.common.CodeGenerator; +import java.util.function.Supplier; +import org.hisp.dhis.common.IdentifiableObject; import org.hisp.dhis.common.IdentifiableObjectManager; +import org.hisp.dhis.common.UidObject; +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.JsonList; -import org.hisp.dhis.organisationunit.OrganisationUnit; -import org.hisp.dhis.program.Enrollment; -import org.hisp.dhis.program.EnrollmentStatus; import org.hisp.dhis.program.Event; -import org.hisp.dhis.program.Program; -import org.hisp.dhis.program.ProgramStage; -import org.hisp.dhis.relationship.Relationship; -import org.hisp.dhis.relationship.RelationshipEntity; import org.hisp.dhis.relationship.RelationshipItem; -import org.hisp.dhis.relationship.RelationshipType; -import org.hisp.dhis.security.acl.AccessStringHelper; -import org.hisp.dhis.test.webapi.H2ControllerIntegrationTestBase; -import org.hisp.dhis.trackedentity.TrackedEntity; -import org.hisp.dhis.trackedentity.TrackedEntityType; +import org.hisp.dhis.render.RenderFormat; +import org.hisp.dhis.render.RenderService; +import org.hisp.dhis.test.webapi.PostgresControllerIntegrationTestBase; +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.user.sharing.UserAccess; import org.hisp.dhis.webapi.controller.tracker.JsonPage; import org.hisp.dhis.webapi.controller.tracker.JsonRelationship; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; import org.springframework.transaction.annotation.Transactional; /** @@ -70,75 +81,77 @@ * the export controllers. */ @Transactional -class ExportControllerPaginationTest extends H2ControllerIntegrationTestBase { +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class ExportControllerPaginationTest extends PostgresControllerIntegrationTestBase { + @Autowired private RenderService renderService; - @Autowired private IdentifiableObjectManager manager; - - @Autowired private CategoryService categoryService; - - private CategoryOptionCombo coc; - - private OrganisationUnit orgUnit; + @Autowired private ObjectBundleService objectBundleService; - private Program program; + @Autowired private ObjectBundleValidationService objectBundleValidationService; - private ProgramStage programStage; + @Autowired private TrackerImportService trackerImportService; - private User owner; - - private User user; - - private TrackedEntityType trackedEntityType; + @Autowired private IdentifiableObjectManager manager; - @BeforeEach - void setUp() { - owner = makeUser("o"); - manager.save(owner, false); + private User importUser; + + private Event event; + private Set relationshipItems; + + 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; + } - coc = categoryService.getDefaultCategoryOptionCombo(); + protected TrackerObjects fromJson(String path) throws IOException { + return renderService.fromJson( + new ClassPathResource(path).getInputStream(), TrackerObjects.class); + } - orgUnit = createOrganisationUnit('A'); - orgUnit.getSharing().setOwner(owner); - manager.save(orgUnit, false); + @BeforeAll + void setUp() throws IOException { + setUpMetadata("tracker/simple_metadata.json"); - OrganisationUnit anotherOrgUnit = createOrganisationUnit('B'); - anotherOrgUnit.getSharing().setOwner(owner); - manager.save(anotherOrgUnit, false); + importUser = userService.getUser("tTgjgobT1oS"); + injectSecurityContextUser(importUser); - user = createUserWithId("tester", CodeGenerator.generateUid()); - user.addOrganisationUnit(orgUnit); - user.setTeiSearchOrganisationUnits(Set.of(orgUnit)); - this.userService.updateUser(user); + TrackerImportParams params = TrackerImportParams.builder().build(); + assertNoErrors( + trackerImportService.importTracker(params, fromJson("tracker/event_and_enrollment.json"))); - program = createProgram('A'); - program.addOrganisationUnit(orgUnit); - program.getSharing().setOwner(owner); - program.getSharing().addUserAccess(userAccess()); - manager.save(program, false); + manager.flush(); + manager.clear(); - programStage = createProgramStage('A', program); - programStage.getSharing().setOwner(owner); - programStage.getSharing().addUserAccess(userAccess()); - manager.save(programStage, false); + event = get(Event.class, "pTzf9KYMk72"); + assertNotEmpty(event.getRelationshipItems(), "test expects an enrollment with one event"); + relationshipItems = event.getRelationshipItems(); + } - trackedEntityType = trackedEntityTypeAccessible(); + @BeforeEach + void setUpUser() { + switchContextToUser(importUser); } @Test void shouldGetPaginatedItemsWithDefaults() { - TrackedEntity to = trackedEntity(); - Event from1 = event(enrollment(to)); - Event from2 = event(enrollment(to)); - Relationship r1 = relationship(from1, to); - Relationship r2 = relationship(from2, to); - JsonPage page = - GET("/tracker/relationships?trackedEntity={uid}", to.getUid()) + GET("/tracker/relationships?event={uid}", event.getUid()) .content(HttpStatus.OK) .asA(JsonPage.class); assertContainsOnly( - List.of(r1.getUid(), r2.getUid()), + relationshipItems.stream() + .map(RelationshipItem::getRelationship) + .map(UidObject::getUid) + .toList(), page.getList("relationships", JsonRelationship.class) .toList(JsonRelationship::getRelationship)); assertEquals(1, page.getPager().getPage()); @@ -155,19 +168,16 @@ void shouldGetPaginatedItemsWithDefaults() { @Test void shouldGetPaginatedItemsWithPagingSetToTrue() { - TrackedEntity to = trackedEntity(); - Event from1 = event(enrollment(to)); - Event from2 = event(enrollment(to)); - Relationship r1 = relationship(from1, to); - Relationship r2 = relationship(from2, to); - JsonPage page = - GET("/tracker/relationships?trackedEntity={uid}&paging=true", to.getUid()) + GET("/tracker/relationships?event={uid}&paging=true", event.getUid()) .content(HttpStatus.OK) .asA(JsonPage.class); assertContainsOnly( - List.of(r1.getUid(), r2.getUid()), + relationshipItems.stream() + .map(RelationshipItem::getRelationship) + .map(UidObject::getUid) + .toList(), page.getList("relationships", JsonRelationship.class) .toList(JsonRelationship::getRelationship)); assertEquals(1, page.getPager().getPage()); @@ -184,19 +194,16 @@ void shouldGetPaginatedItemsWithPagingSetToTrue() { @Test void shouldGetPaginatedItemsWithDefaultsAndTotals() { - TrackedEntity to = trackedEntity(); - Event from1 = event(enrollment(to)); - Event from2 = event(enrollment(to)); - Relationship r1 = relationship(from1, to); - Relationship r2 = relationship(from2, to); - JsonPage page = - GET("/tracker/relationships?trackedEntity={uid}&totalPages=true", to.getUid()) + GET("/tracker/relationships?event={uid}&totalPages=true", event.getUid()) .content(HttpStatus.OK) .asA(JsonPage.class); assertContainsOnly( - List.of(r1.getUid(), r2.getUid()), + relationshipItems.stream() + .map(RelationshipItem::getRelationship) + .map(UidObject::getUid) + .toList(), page.getList("relationships", JsonRelationship.class) .toList(JsonRelationship::getRelationship)); assertEquals(1, page.getPager().getPage()); @@ -213,14 +220,8 @@ void shouldGetPaginatedItemsWithDefaultsAndTotals() { @Test void shouldGetPaginatedItemsWithNonDefaults() { - TrackedEntity to = trackedEntity(); - Event from1 = event(enrollment(to)); - Event from2 = event(enrollment(to)); - relationship(from1, to); - relationship(from2, to); - JsonPage page = - GET("/tracker/relationships?trackedEntity={uid}&page=2&pageSize=1", to.getUid()) + GET("/tracker/relationships?event={uid}&page=2&pageSize=1", event.getUid()) .content(HttpStatus.OK) .asA(JsonPage.class); @@ -245,16 +246,9 @@ void shouldGetPaginatedItemsWithNonDefaults() { @Test void shouldGetPaginatedItemsWithNonDefaultsAndTotals() { - TrackedEntity to = trackedEntity(); - Event from1 = event(enrollment(to)); - Event from2 = event(enrollment(to)); - relationship(from1, to); - relationship(from2, to); JsonPage page = - GET( - "/tracker/relationships?trackedEntity={uid}&page=2&pageSize=1&totalPages=true", - to.getUid()) + GET("/tracker/relationships?event={uid}&page=2&pageSize=1&totalPages=true", event.getUid()) .content(HttpStatus.OK) .asA(JsonPage.class); @@ -279,19 +273,17 @@ void shouldGetPaginatedItemsWithNonDefaultsAndTotals() { @Test void shouldGetNonPaginatedItemsWithSkipPaging() { - TrackedEntity to = trackedEntity(); - Event from1 = event(enrollment(to)); - Event from2 = event(enrollment(to)); - Relationship r1 = relationship(from1, to); - Relationship r2 = relationship(from2, to); JsonPage page = - GET("/tracker/relationships?trackedEntity={uid}&skipPaging=true", to.getUid()) + GET("/tracker/relationships?event={uid}&skipPaging=true", event.getUid()) .content(HttpStatus.OK) .asA(JsonPage.class); assertContainsOnly( - List.of(r1.getUid(), r2.getUid()), + relationshipItems.stream() + .map(RelationshipItem::getRelationship) + .map(UidObject::getUid) + .toList(), page.getList("relationships", JsonRelationship.class) .toList(JsonRelationship::getRelationship)); assertHasNoMember(page, "pager"); @@ -305,19 +297,17 @@ void shouldGetNonPaginatedItemsWithSkipPaging() { @Test void shouldGetNonPaginatedItemsWithPagingSetToFalse() { - TrackedEntity to = trackedEntity(); - Event from1 = event(enrollment(to)); - Event from2 = event(enrollment(to)); - Relationship r1 = relationship(from1, to); - Relationship r2 = relationship(from2, to); JsonPage page = - GET("/tracker/relationships?trackedEntity={uid}&paging=false", to.getUid()) + GET("/tracker/relationships?event={uid}&paging=false", event.getUid()) .content(HttpStatus.OK) .asA(JsonPage.class); assertContainsOnly( - List.of(r1.getUid(), r2.getUid()), + relationshipItems.stream() + .map(RelationshipItem::getRelationship) + .map(UidObject::getUid) + .toList(), page.getList("relationships", JsonRelationship.class) .toList(JsonRelationship::getRelationship)); assertHasNoMember(page, "pager"); @@ -329,110 +319,46 @@ void shouldGetNonPaginatedItemsWithPagingSetToFalse() { assertHasNoMember(page, "pageCount"); } - private TrackedEntityType trackedEntityTypeAccessible() { - TrackedEntityType type = trackedEntityType(); - type.getSharing().addUserAccess(userAccess()); - manager.save(type, false); - return type; - } - - private TrackedEntityType trackedEntityType() { - TrackedEntityType type = createTrackedEntityType('A'); - type.getSharing().setOwner(owner); - type.getSharing().setPublicAccess(AccessStringHelper.DEFAULT); - return type; - } - - private TrackedEntity trackedEntity() { - TrackedEntity te = trackedEntity(orgUnit); - manager.save(te, false); - return te; - } - - private TrackedEntity trackedEntity(OrganisationUnit orgUnit) { - TrackedEntity te = trackedEntity(orgUnit, trackedEntityType); - manager.save(te, false); - return te; - } - - private TrackedEntity trackedEntity( - OrganisationUnit orgUnit, TrackedEntityType trackedEntityType) { - TrackedEntity te = createTrackedEntity(orgUnit); - te.setTrackedEntityType(trackedEntityType); - te.getSharing().setPublicAccess(AccessStringHelper.DEFAULT); - te.getSharing().setOwner(owner); - return te; - } - - private Enrollment enrollment(TrackedEntity te) { - Enrollment enrollment = new Enrollment(program, te, orgUnit); - enrollment.setAutoFields(); - enrollment.setEnrollmentDate(new Date()); - enrollment.setOccurredDate(new Date()); - enrollment.setStatus(EnrollmentStatus.COMPLETED); - manager.save(enrollment, false); - te.setEnrollments(Set.of(enrollment)); - manager.save(te, false); - return enrollment; - } - - private Event event(Enrollment enrollment) { - Event event = new Event(enrollment, programStage, orgUnit, coc); - event.setAutoFields(); - manager.save(event, false); - enrollment.setEvents(Set.of(event)); - manager.save(enrollment, false); - return event; - } - - private UserAccess userAccess() { - UserAccess a = new UserAccess(); - a.setUser(user); - a.setAccess(AccessStringHelper.FULL); - return a; + 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; } - private RelationshipType relationshipTypeAccessible() { - RelationshipType type = relationshipType(); - type.getSharing().addUserAccess(userAccess()); - manager.save(type, false); - return type; + 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 RelationshipType relationshipType() { - RelationshipType type = createRelationshipType('A'); - type.getFromConstraint().setRelationshipEntity(RelationshipEntity.PROGRAM_STAGE_INSTANCE); - type.getToConstraint().setRelationshipEntity(RelationshipEntity.TRACKED_ENTITY_INSTANCE); - type.getSharing().setOwner(owner); - type.getSharing().setPublicAccess(AccessStringHelper.DEFAULT); - manager.save(type, false); - return type; + 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(); + }; } - private Relationship relationship(Event from, TrackedEntity to) { - Relationship r = new Relationship(); - - RelationshipItem fromItem = new RelationshipItem(); - fromItem.setEvent(from); - from.getRelationshipItems().add(fromItem); - r.setFrom(fromItem); - fromItem.setRelationship(r); - - RelationshipItem toItem = new RelationshipItem(); - toItem.setTrackedEntity(to); - to.getRelationshipItems().add(toItem); - r.setTo(toItem); - toItem.setRelationship(r); - - RelationshipType type = relationshipTypeAccessible(); - r.setRelationshipType(type); - r.setKey(type.getUid()); - r.setInvertedKey(type.getUid()); - - r.setAutoFields(); - r.getSharing().setOwner(owner); - r.setCreatedAtClient(new Date()); - manager.save(r, false); - return r; + 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/TrackedEntitiesExportControllerTest.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntitiesExportControllerTest.java index 89cea37bf476..22aa140d39f6 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntitiesExportControllerTest.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/trackedentity/TrackedEntitiesExportControllerTest.java @@ -406,7 +406,7 @@ void getTrackedEntityByIdyWithFieldsRelationshipsNoAccessToTrackedEntityType() { this.switchContextToUser(user); GET("/tracker/trackedEntities/{id}?fields=relationships", from.getUid()) - .error(HttpStatus.FORBIDDEN); + .error(HttpStatus.NOT_FOUND); } @Test