Skip to content

Commit 72ca050

Browse files
committed
Merge branch '7.3' into 8.0
2 parents b3dea35 + ab2fc77 commit 72ca050

File tree

7 files changed

+263
-7
lines changed

7 files changed

+263
-7
lines changed

avni-server-api/src/main/java/org/avni/server/dao/EntityApprovalStatusRepository.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package org.avni.server.dao;
22

33
import org.avni.server.dao.sync.SyncEntityName;
4-
import org.avni.server.domain.AddressLevel;
5-
import org.avni.server.domain.EntityApprovalStatus;
4+
import org.avni.server.domain.*;
65
import org.avni.server.framework.security.UserContextHolder;
6+
import org.joda.time.DateTime;
77
import org.springframework.data.domain.Page;
88
import org.springframework.data.domain.Pageable;
99
import org.springframework.data.jpa.domain.Specification;
@@ -16,10 +16,7 @@
1616
import org.springframework.security.access.prepost.PreAuthorize;
1717
import org.springframework.stereotype.Repository;
1818

19-
import javax.persistence.criteria.CriteriaBuilder;
20-
import javax.persistence.criteria.CriteriaQuery;
21-
import javax.persistence.criteria.Predicate;
22-
import javax.persistence.criteria.Root;
19+
import javax.persistence.criteria.*;
2320
import java.util.ArrayList;
2421
import java.util.Date;
2522
import java.util.List;
@@ -36,6 +33,35 @@ Page<EntityApprovalStatus> findByLastModifiedDateTimeIsBetweenOrderByLastModifie
3633
@Param("now") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Date now,
3734
Pageable pageable);
3835

36+
default Page<EntityApprovalStatus> findEntityApprovalStatuses(EntityApprovalStatusSearchParams searchParams, Pageable pageable) {
37+
38+
Specification specification = lastModifiedBetween(
39+
CHSEntity.toDate(searchParams.getLastModifiedDateTime() != null ? searchParams.getLastModifiedDateTime() : new DateTime("1900-01-01T00:00:00.000Z")),
40+
CHSEntity.toDate(searchParams.getNow() != null ? searchParams.getNow() : new DateTime()));
41+
42+
if (searchParams.getEntityType() != null) {
43+
specification = specification.and(findByEntityTypeSpec(searchParams.getEntityType()));
44+
}
45+
if (searchParams.getEntityTypeUuid() != null) {
46+
specification = specification.and(findByEntityTypeUuidSpec(searchParams.getEntityTypeUuid()));
47+
}
48+
49+
return findAll(specification, pageable);
50+
}
51+
52+
default Specification<EntityApprovalStatus> findByEntityTypeSpec(EntityApprovalStatus.EntityType entityType) {
53+
Specification<EntityApprovalStatus> spec = (Root<EntityApprovalStatus> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
54+
return cb.and(cb.equal(root.get("entityType"), entityType));
55+
};
56+
return spec;
57+
}
58+
59+
default Specification<EntityApprovalStatus> findByEntityTypeUuidSpec(String entityTypeUuid) {
60+
Specification<EntityApprovalStatus> spec = (Root<EntityApprovalStatus> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
61+
return cb.and(cb.equal(root.get("entityTypeUuid"), entityTypeUuid));
62+
};
63+
return spec;
64+
}
3965
List<EntityApprovalStatus> findByEntityIdAndEntityTypeAndIsVoidedFalse(Long entityId, EntityApprovalStatus.EntityType entityType);
4066
EntityApprovalStatus findFirstByEntityIdAndEntityTypeAndIsVoidedFalseOrderByStatusDateTimeDesc(Long entityId, EntityApprovalStatus.EntityType entityType);
4167

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.avni.server.dao;
2+
3+
import org.avni.server.domain.EntityApprovalStatus;
4+
import org.joda.time.DateTime;
5+
6+
7+
public class EntityApprovalStatusSearchParams {
8+
private final DateTime lastModifiedDateTime;
9+
private final DateTime now;
10+
private final EntityApprovalStatus.EntityType entityType;
11+
private final String entityTypeUuid;
12+
13+
public EntityApprovalStatusSearchParams(DateTime lastModifiedDateTime, DateTime now, EntityApprovalStatus.EntityType entityType, String entityTypeUuid) {
14+
this.lastModifiedDateTime = lastModifiedDateTime;
15+
this.now = now;
16+
this.entityType = entityType;
17+
this.entityTypeUuid = entityTypeUuid;
18+
}
19+
20+
public DateTime getLastModifiedDateTime() {
21+
return lastModifiedDateTime;
22+
}
23+
24+
public DateTime getNow() {
25+
return now;
26+
}
27+
28+
public EntityApprovalStatus.EntityType getEntityType() {
29+
return entityType;
30+
}
31+
32+
public String getEntityTypeUuid() {
33+
return entityTypeUuid;
34+
}
35+
}

avni-server-api/src/main/java/org/avni/server/service/accessControl/AccessControlService.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import javax.validation.constraints.NotNull;
1313
import java.util.Arrays;
1414
import java.util.List;
15+
import java.util.Map;
1516
import java.util.Objects;
1617
import java.util.stream.Collectors;
1718

@@ -180,4 +181,29 @@ public void checkIsAdmin() {
180181
if (!user.isAdmin())
181182
throw AvniAccessException.createForNotAdmin(user);
182183
}
184+
185+
public void checkApprovePrivilegeOnEntityApprovalStatus(String entityType, String entityTypeUuid) {
186+
switch (EntityApprovalStatus.EntityType.valueOf(entityType)) {
187+
case Subject:
188+
this.checkSubjectPrivilege(PrivilegeType.ApproveSubject, entityTypeUuid);
189+
break;
190+
case Encounter:
191+
case ProgramEncounter:
192+
this.checkEncounterPrivilege(PrivilegeType.ApproveEncounter, entityTypeUuid);
193+
break;
194+
case ProgramEnrolment:
195+
this.checkProgramPrivilege(PrivilegeType.ApproveEnrolment, entityTypeUuid);
196+
break;
197+
// TODO: implement when ApproveChecklistitem privileges are enforced
198+
// case ChecklistItem:
199+
}
200+
}
201+
202+
public void checkApprovePrivilegeOnEntityApprovalStatuses(List<EntityApprovalStatus> entityApprovalStatuses) {
203+
Map<List<String>, Long> uniqueEASByTypeAndTypeUuid = entityApprovalStatuses
204+
.stream()
205+
.collect(Collectors.groupingBy(entityApprovalStatus -> Arrays.asList(String.valueOf(entityApprovalStatus.getEntityType()), entityApprovalStatus.getEntityTypeUuid()), Collectors.counting()));
206+
207+
uniqueEASByTypeAndTypeUuid.keySet().forEach(entity -> checkApprovePrivilegeOnEntityApprovalStatus(entity.get(0), entity.get(1)));
208+
}
183209
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package org.avni.server.web.api;
2+
3+
import org.avni.server.dao.EntityApprovalStatusRepository;
4+
import org.avni.server.dao.EntityApprovalStatusSearchParams;
5+
import org.avni.server.domain.EntityApprovalStatus;
6+
import org.avni.server.service.EntityApprovalStatusService;
7+
import org.avni.server.service.accessControl.AccessControlService;
8+
import org.avni.server.web.response.EntityApprovalStatusResponse;
9+
import org.avni.server.web.response.ResponsePage;
10+
import org.joda.time.DateTime;
11+
import org.springframework.beans.factory.annotation.Autowired;
12+
import org.springframework.data.domain.Page;
13+
import org.springframework.data.domain.Pageable;
14+
import org.springframework.format.annotation.DateTimeFormat;
15+
import org.springframework.security.access.prepost.PreAuthorize;
16+
import org.springframework.web.bind.annotation.RequestMapping;
17+
import org.springframework.web.bind.annotation.RequestMethod;
18+
import org.springframework.web.bind.annotation.RequestParam;
19+
import org.springframework.web.bind.annotation.RestController;
20+
21+
import java.util.ArrayList;
22+
23+
@RestController
24+
public class EntityApprovalStatusApiController {
25+
26+
private final EntityApprovalStatusRepository entityApprovalStatusRepository;
27+
private final EntityApprovalStatusService entityApprovalStatusService;
28+
private final AccessControlService accessControlService;
29+
30+
@Autowired
31+
public EntityApprovalStatusApiController(EntityApprovalStatusRepository entityApprovalStatusRepository, EntityApprovalStatusService entityApprovalStatusService, AccessControlService accessControlService) {
32+
this.entityApprovalStatusRepository = entityApprovalStatusRepository;
33+
this.entityApprovalStatusService = entityApprovalStatusService;
34+
this.accessControlService = accessControlService;
35+
}
36+
37+
@RequestMapping(value = "/api/approvalStatuses", method = RequestMethod.GET)
38+
@PreAuthorize(value = "hasAnyAuthority('user')")
39+
public ResponsePage getEntityApprovalStatuses(@RequestParam(value = "lastModifiedDateTime", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) DateTime lastModifiedDateTime,
40+
@RequestParam(value = "now", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) DateTime now,
41+
@RequestParam(value = "entityType", required = false) String entityType,
42+
@RequestParam(value = "entityTypeId", required = false) String entityTypeUuid,
43+
Pageable pageable) {
44+
45+
Page<EntityApprovalStatus> entityApprovalStatuses = entityApprovalStatusRepository.findEntityApprovalStatuses(new EntityApprovalStatusSearchParams(lastModifiedDateTime, now, EntityApprovalStatus.EntityType.valueOf(entityType), entityTypeUuid), pageable);
46+
accessControlService.checkApprovePrivilegeOnEntityApprovalStatuses(entityApprovalStatuses.getContent());
47+
ArrayList<EntityApprovalStatusResponse> entityApprovalStatusResponse = new ArrayList<>();
48+
entityApprovalStatuses.forEach(entityApprovalStatus -> entityApprovalStatusResponse.add(EntityApprovalStatusResponse.fromEntityApprovalStatus(entityApprovalStatus, entityApprovalStatusService.getEntityUuid(entityApprovalStatus))));
49+
return new ResponsePage(entityApprovalStatusResponse, entityApprovalStatuses.getNumberOfElements(), entityApprovalStatuses.getTotalPages(), entityApprovalStatuses.getSize());
50+
}
51+
}

avni-server-api/src/main/java/org/avni/server/web/api/SubjectApiController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public ResponsePage getSubjects(@RequestParam(value = "lastModifiedDateTime", re
8686
subjects = individualRepository.findSubjects(individualSearchParams, pageable);
8787
List<GroupSubject> groupsOfAllMemberSubjects = groupSubjectRepository.findAllByMemberSubjectIn(subjects.getContent());
8888
ArrayList<SubjectResponse> subjectResponses = new ArrayList<>();
89-
subjects.forEach(subject -> subjectResponses.add(SubjectResponse.fromSubject(subject, !S.isEmpty(subjectTypeName), conceptRepository, conceptService, findGroupAffiliation(subject, groupsOfAllMemberSubjects), s3Service)));
89+
subjects.forEach(subject -> subjectResponses.add(SubjectResponse.fromSubject(subject, S.isEmpty(subjectTypeName), conceptRepository, conceptService, findGroupAffiliation(subject, groupsOfAllMemberSubjects), s3Service)));
9090
accessControlService.checkSubjectPrivileges(PrivilegeType.ViewSubject, subjects.getContent());
9191
return new ResponsePage(subjectResponses, subjects.getNumberOfElements(), subjects.getTotalPages(), subjects.getSize());
9292
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.avni.server.web.response;
2+
3+
import org.avni.server.domain.EntityApprovalStatus;
4+
5+
import java.util.LinkedHashMap;
6+
7+
public class EntityApprovalStatusResponse extends LinkedHashMap<String, Object> {
8+
public static EntityApprovalStatusResponse fromEntityApprovalStatus(EntityApprovalStatus entityApprovalStatus, String entityUuid) {
9+
EntityApprovalStatusResponse entityApprovalStatusResponse = new EntityApprovalStatusResponse();
10+
entityApprovalStatusResponse.put("Entity ID", entityUuid);
11+
entityApprovalStatusResponse.put("Entity type", entityApprovalStatus.getEntityType());
12+
entityApprovalStatusResponse.put("Entity type ID", entityApprovalStatus.getEntityTypeUuid());
13+
entityApprovalStatusResponse.put("Approval status", entityApprovalStatus.getApprovalStatus().getStatus());
14+
entityApprovalStatusResponse.put("Approval status comment", entityApprovalStatus.getApprovalStatusComment());
15+
entityApprovalStatusResponse.put("Status date time", entityApprovalStatus.getStatusDateTime());
16+
17+
Response.putAudit(entityApprovalStatus, entityApprovalStatusResponse);
18+
return entityApprovalStatusResponse;
19+
}
20+
}

avni-server-api/src/main/resources/api/external-api.yaml

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,73 @@ paths:
12531253
responses:
12541254
"200":
12551255
description: successful
1256+
/api/entityApprovalStatuses:
1257+
get:
1258+
tags:
1259+
- Entity Approval Status
1260+
summary: Get paged list of entity approval statues
1261+
description: |
1262+
By passing in the appropriate options, you can get the entity approval statuses in set of pages
1263+
operationId: entityApprovalStatuses
1264+
parameters:
1265+
- name: lastModifiedDateTime
1266+
in: query
1267+
description: "date-time in ISO datetime format. All the entity approval statuses which have been updated since this time will be returned. The value should be specified in the following format - yyyy-MM-dd'T'HH:mm:ss.SSSZ, e.g. \"2000-10-31T01:30:00.000Z\". Defaults to \"1900-01-01T00:00:00.000Z\"."
1268+
required: false
1269+
style: form
1270+
explode: true
1271+
schema:
1272+
type: string
1273+
- name: now
1274+
in: query
1275+
description: "date-time in ISO datetime format. All the entity approval statuses which have been updated till this time will be returned. The value should be specified in the following format - yyyy-MM-dd'T'HH:mm:ss.SSSZ, e.g. \"2000-10-31T01:30:00.000Z\". Defaults to current time."
1276+
required: false
1277+
style: form
1278+
explode: true
1279+
schema:
1280+
type: string
1281+
- name: entityType
1282+
in: query
1283+
description: "Allows filtering results by entity type name"
1284+
required: false
1285+
style: form
1286+
explode: true
1287+
schema:
1288+
type: string
1289+
- name: entityTypeId
1290+
in: query
1291+
description: "Allows filtering results by entity type id"
1292+
required: false
1293+
style: form
1294+
explode: true
1295+
schema:
1296+
type: string
1297+
example: "{\"Diagnosis\": \"Diabetes\", \"Blood Group\": \"B+\"}"
1298+
- name: auth-token
1299+
in: header
1300+
required: false
1301+
style: simple
1302+
explode: false
1303+
schema:
1304+
type: string
1305+
description: token provided by cognito/keycloak
1306+
- name: version
1307+
in: query
1308+
description: "Version of the API to be called"
1309+
required: false
1310+
explode: false
1311+
schema:
1312+
type: string
1313+
default: 1
1314+
responses:
1315+
"200":
1316+
description: "successful, a page of entity approval statuses"
1317+
content:
1318+
application/json:
1319+
schema:
1320+
$ref: '#/components/schemas/inline_response_200_eas'
1321+
"400":
1322+
description: bad input parameter
12561323
components:
12571324
schemas:
12581325
Subject:
@@ -1796,6 +1863,29 @@ components:
17961863
description: "id of locations"
17971864
items:
17981865
type: number
1866+
EntityApprovalStatusBody:
1867+
type: object
1868+
properties:
1869+
Entity ID:
1870+
type: string
1871+
description: ID of the entity for which the approval status is being returned
1872+
Entity type:
1873+
type: string
1874+
description: Type of the entity for which the approval status is being returned i.e. Subject / Encounter etc
1875+
Entity type ID:
1876+
type: string
1877+
description: ID of the entity type for which the approval status is being returned
1878+
Approval status:
1879+
type: string
1880+
description: Approval status for the entity
1881+
Approval status comment:
1882+
type: string
1883+
description: Comments on the approval status for the entity
1884+
Status date time:
1885+
type: string
1886+
description: Date/Time when the approval status was set
1887+
audit:
1888+
$ref: '#/components/schemas/Audit'
17991889
inline_response_200:
18001890
properties:
18011891
content:
@@ -1840,3 +1930,11 @@ components:
18401930
$ref: '#/components/schemas/Location'
18411931
allOf:
18421932
- $ref: '#/components/schemas/ResponsePage'
1933+
inline_response_200_eas:
1934+
properties:
1935+
content:
1936+
type: array
1937+
items:
1938+
$ref: '#/components/schemas/EntityApprovalStatusBody'
1939+
allOf:
1940+
- $ref: '#/components/schemas/ResponsePage'

0 commit comments

Comments
 (0)