Skip to content

Commit

Permalink
chore: join code paths for serving one vs many TEs
Browse files Browse the repository at this point in the history
  • Loading branch information
teleivo committed Jan 23, 2025
1 parent d4c3f8b commit 6b647e2
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<TrackedEntity> 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<String> 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<Enrollment> 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<TrackedEntityProgramOwner> 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<TrackedEntityAttributeValue> 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<String> 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
Expand Down Expand Up @@ -368,6 +276,7 @@ private List<TrackedEntity> getTrackedEntities(
operationParams.getTrackedEntityParams(),
queryParams,
queryParams.getOrgUnitMode());
// TODO(ivo) clean this up
setRelationshipItems(
trackedEntities,
operationParams.getTrackedEntityParams(),
Expand All @@ -384,6 +293,43 @@ private List<TrackedEntity> getTrackedEntities(
return trackedEntities;
}

private static Set<TrackedEntityProgramOwner> 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<TrackedEntityAttributeValue> 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<String> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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());
}

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 6b647e2

Please sign in to comment.