Skip to content

Commit

Permalink
avniproject/integration-service#96 - lastModified is not mandatory
Browse files Browse the repository at this point in the history
  • Loading branch information
petmongrels committed Oct 31, 2023
1 parent fe98a3c commit fb31d5c
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package org.avni.server.dao;

import org.avni.server.application.projections.WebSearchResultProjection;
import org.avni.server.domain.AddressLevel;
import org.avni.server.domain.Concept;
import org.avni.server.domain.Individual;
import org.avni.server.domain.SubjectType;
import org.avni.server.domain.*;
import org.avni.server.framework.security.UserContextHolder;
import org.avni.server.projection.IndividualWebProjection;
import org.avni.server.util.S;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.springframework.data.domain.Page;
Expand Down Expand Up @@ -152,10 +150,10 @@ default Specification<Individual> getFilterSpecForAddress(String locationName) {
@Query(value = "select firstname,lastname,fullname,id,uuid,title_lineage,subject_type_name,gender_name,date_of_birth,enrolments,total_elements from web_search_function(:jsonSearch, :dbUser)", nativeQuery = true)
List<WebSearchResultProjection> getWebSearchResults(String jsonSearch, String dbUser);

default Specification<Individual> findBySubjectTypeSpec(String subjectType) {
default Specification<Individual> findBySubjectTypeSpec(String subjectTypeName) {
Specification<Individual> spec = (Root<Individual> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
Join<Individual, SubjectType> subjectTypeJoin = root.join("subjectType", JoinType.LEFT);
return cb.and(cb.equal(subjectTypeJoin.get("name"), subjectType));
return cb.and(cb.equal(subjectTypeJoin.get("name"), subjectTypeName));
};
return spec;
}
Expand All @@ -165,17 +163,15 @@ default Specification<Individual> findInLocationSpec(List<Long> addressIds) {
addressIds.isEmpty() ? null : root.get("addressLevel").get("id").in(addressIds);
}

default Page<Individual> findByConcepts(Date lastModifiedDateTime, Date now, Map<Concept, String> concepts, List<Long> addressIds, Pageable pageable) {
return findAll(lastModifiedBetween(lastModifiedDateTime, now)
.and(withConceptValues(concepts, "observations"))
.and(findInLocationSpec(addressIds)), pageable);
}

default Page<Individual> findByConceptsAndSubjectType(Date lastModifiedDateTime, Date now, Map<Concept, String> concepts, String subjectType, List<Long> addressIds, Pageable pageable) {
return findAll(lastModifiedBetween(lastModifiedDateTime, now)
.and(withConceptValues(concepts, "observations"))
.and(findBySubjectTypeSpec(subjectType))
.and(findInLocationSpec(addressIds)), pageable);
default Page<Individual> findSubjects(IndividualSearchParams individualSearchParams, Pageable pageable) {
Specification specification = withConceptValues(individualSearchParams.getObservations(), "observations");
if (individualSearchParams.getLastModifiedDateTime() != null)
specification = specification.and(lastModifiedBetween(CHSEntity.toDate(individualSearchParams.getLastModifiedDateTime()), CHSEntity.toDate(individualSearchParams.getNow())));
if (!individualSearchParams.getAllLocationIds().isEmpty())
specification = specification.and(findInLocationSpec(individualSearchParams.getAllLocationIds()));
if (!S.isEmpty((individualSearchParams.getSubjectTypeName())))
specification = specification.and(findBySubjectTypeSpec(individualSearchParams.getSubjectTypeName()));
return findAll(specification, pageable);
}

List<Individual> findAllByAddressLevelAndSubjectTypeAndIsVoidedFalse(AddressLevel addressLevel, SubjectType subjectType);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.avni.server.dao;

import org.avni.server.domain.Concept;
import org.joda.time.DateTime;

import java.util.List;
import java.util.Map;

public class IndividualSearchParams {
private final DateTime lastModifiedDateTime;
private final DateTime now;
private final String subjectTypeName;
private final Map<Concept, String> observations;
private final List<Long> allLocationIds;

public IndividualSearchParams(DateTime lastModifiedDateTime, DateTime now, String subjectTypeName, Map<Concept, String> observations, List<Long> allLocationIds) {
this.lastModifiedDateTime = lastModifiedDateTime;
this.now = now;
this.subjectTypeName = subjectTypeName;
this.observations = observations;
this.allLocationIds = allLocationIds;
}

public DateTime getLastModifiedDateTime() {
return lastModifiedDateTime;
}

public DateTime getNow() {
return now;
}

public String getSubjectTypeName() {
return subjectTypeName;
}

public Map<Concept, String> getObservations() {
return observations;
}

public List<Long> getAllLocationIds() {
return allLocationIds;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ public Map<Concept, String> readConceptsFromJsonObject(String jsonObject) {
}
return jsonMap;
} catch (IOException e) {
logger.error("Bad Request", e);
throw new BadRequestError("Bad Request: concepts parameter is not a valid json object");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,24 +71,20 @@ public SubjectApiController(ConceptService conceptService, IndividualRepository

@RequestMapping(value = "/api/subjects", method = RequestMethod.GET)
@PreAuthorize(value = "hasAnyAuthority('user')")
public ResponsePage getSubjects(@RequestParam("lastModifiedDateTime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) DateTime lastModifiedDateTime,
@RequestParam("now") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) DateTime now,
@RequestParam(value = "subjectType", required = false) String subjectType,
public ResponsePage getSubjects(@RequestParam(value = "lastModifiedDateTime", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) DateTime lastModifiedDateTime,
@RequestParam(value = "now", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) DateTime now,
@RequestParam(value = "subjectTypeName", required = false) String subjectTypeName,
@RequestParam(value = "concepts", required = false) String concepts,
@RequestParam(value = "locationIds", required = false) List<String> locationUUIDs,
Pageable pageable) {
Page<Individual> subjects;
boolean subjectTypeRequested = S.isEmpty(subjectType);
List<Long> allLocationIds = locationService.getAllWithChildrenForUUIDs(locationUUIDs);
Map<Concept, String> conceptsMap = conceptService.readConceptsFromJsonObject(concepts);
subjects = subjectTypeRequested ?
individualRepository.findByConcepts(CHSEntity.toDate(lastModifiedDateTime), CHSEntity.toDate(now), conceptsMap, allLocationIds, pageable) :
individualRepository.findByConceptsAndSubjectType(CHSEntity.toDate(lastModifiedDateTime), CHSEntity.toDate(now), conceptsMap, subjectType, allLocationIds, pageable);
Map<Concept, String> observations = conceptService.readConceptsFromJsonObject(concepts);
IndividualSearchParams individualSearchParams = new IndividualSearchParams(lastModifiedDateTime, now, subjectTypeName, observations, allLocationIds);
subjects = individualRepository.findSubjects(individualSearchParams, pageable);
List<GroupSubject> groupsOfAllMemberSubjects = groupSubjectRepository.findAllByMemberSubjectIn(subjects.getContent());
ArrayList<SubjectResponse> subjectResponses = new ArrayList<>();
subjects.forEach(subject -> {
subjectResponses.add(SubjectResponse.fromSubject(subject, subjectTypeRequested, conceptRepository, conceptService, findGroupAffiliation(subject, groupsOfAllMemberSubjects), s3Service));
});
subjects.forEach(subject -> subjectResponses.add(SubjectResponse.fromSubject(subject, !S.isEmpty(subjectTypeName), conceptRepository, conceptService, findGroupAffiliation(subject, groupsOfAllMemberSubjects), s3Service)));
accessControlService.checkSubjectPrivileges(PrivilegeType.ViewSubject, subjects.getContent());
return new ResponsePage(subjectResponses, subjects.getNumberOfElements(), subjects.getTotalPages(), subjects.getSize());
}
Expand Down
2 changes: 1 addition & 1 deletion avni-server-api/src/main/resources/api/external-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ paths:
explode: true
schema:
type: string
- name: subjectType
- name: subjectTypeName
in: query
description: subject type
required: false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.avni.server.web.api;

import org.avni.server.common.AbstractControllerIntegrationTest;
import org.avni.server.domain.Concept;
import org.avni.server.domain.Individual;
import org.avni.server.domain.ObservationCollection;
import org.avni.server.domain.SubjectType;
import org.avni.server.domain.factory.txData.ObservationCollectionBuilder;
import org.avni.server.domain.factory.txn.SubjectBuilder;
import org.avni.server.domain.metadata.SubjectTypeBuilder;
import org.avni.server.service.builder.*;
import org.avni.server.web.response.ResponsePage;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.jdbc.Sql;

import java.util.Collections;

import static junit.framework.TestCase.assertEquals;

@Sql(value = {"/tear-down.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(value = {"/tear-down.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public class SubjectApiControllerIntegrationTest extends AbstractControllerIntegrationTest {
@Autowired
private TestDataSetupService testDataSetupService;
@Autowired
private TestSubjectTypeService testSubjectTypeService;
@Autowired
private TestConceptService testConceptService;
@Autowired
private TestGroupService testGroupService;
@Autowired
private TestSubjectService testSubjectService;
@Autowired
private SubjectApiController subjectApiController;
private SubjectType subjectType;
private TestDataSetupService.TestCatchmentData catchmentData;
private Concept concept;

@Before
public void setUp() throws Exception {
TestDataSetupService.TestOrganisationData organisationData = testDataSetupService.setupOrganisation();
catchmentData = testDataSetupService.setupACatchment();
subjectType = testSubjectTypeService.createWithDefaults(
new SubjectTypeBuilder()
.setMandatoryFieldsForNewEntity()
.setUuid("subjectType1")
.setName("subjectType1")
.build());
concept = testConceptService.createCodedConcept("Concept Name 1", "Answer 1", "Answer 2");
ObservationCollection observationCollection = new ObservationCollectionBuilder()
.addObservation(concept, concept.getAnswerConcept("Answer 1"))
.build();
testGroupService.giveViewSubjectPrivilegeTo(organisationData.getGroup(), subjectType);
Individual subject = new SubjectBuilder()
.withMandatoryFieldsForNewEntity()
.withSubjectType(subjectType)
.withLocation(catchmentData.getAddressLevel1())
.withObservations(observationCollection)
.build();
testSubjectService.save(subject);
}

@Test
public void getSubjectsWithAllParams() {
ResponsePage subjects = subjectApiController.getSubjects(DateTime.now().minusDays(1),
DateTime.now().plusDays(1),
subjectType.getName(),
String.format("{\"%s\": \"%s\"}", concept.getName(), concept.getAnswerConcept("Answer 1").getUuid()),
Collections.singletonList(catchmentData.getAddressLevel1().getUuid()),
PageRequest.of(0, 10));
assertEquals(1, subjects.getContent().size());
}

@Test
public void getSubjectWithoutLastModifiedDateTime() {
ResponsePage subjects = subjectApiController.getSubjects(null, null,
subjectType.getName(),
String.format("{\"%s\": \"%s\"}", concept.getName(), concept.getAnswerConcept("Answer 1").getUuid()),
Collections.singletonList(catchmentData.getAddressLevel1().getUuid()),
PageRequest.of(0, 10));
assertEquals(1, subjects.getContent().size());
}
}

0 comments on commit fb31d5c

Please sign in to comment.