Skip to content

Commit 883fe67

Browse files
Merge pull request #198 from CodeForPhilly/update_check_crud_operations
Updated endpoints for eligibility checks to include working and published data model
2 parents f050e51 + 0c373b6 commit 883fe67

26 files changed

+713
-495
lines changed

builder-api/.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,10 @@ nb-configuration.xml
4444
# TLS Certificates
4545
.certs/
4646

47-
config/
47+
config/
48+
49+
# Service account crdentials
50+
src/main/resources/benefit-decision-toolkit*.json
51+
52+
# Temp
53+
src/main/resources/properties-test.txt
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package org.acme.auth;
22

33
import io.quarkus.security.identity.SecurityIdentity;
4+
import org.eclipse.microprofile.jwt.JsonWebToken;
45

56
public class AuthUtils {
67
public static String getUserId(SecurityIdentity identity){
7-
return identity.getPrincipal().getName();
8+
return ((JsonWebToken)identity.getPrincipal()).getClaim("user_id");
89
}
910
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.acme.constants;
2+
3+
import org.apache.hc.client5.http.impl.auth.AuthCacheKeeper;
4+
5+
public enum CheckStatus {
6+
WORKING('W'),
7+
PUBLISHED('P');
8+
9+
private final char code;
10+
11+
CheckStatus(char code) {
12+
this.code = code;
13+
}
14+
15+
public char getCode() {
16+
return code;
17+
}
18+
}
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package org.acme.constants;
22

33
public class CollectionNames {
4-
public static final String SCREENER_COLLECTION = "screener";
5-
public static final String DMN_MODEL_COLLECTION = "dmn_model";
6-
public static final String ELIGIBILITY_CHECK_COLLECTION = "eligibilityCheck";
4+
public static final String WORKING_SCREENER_COLLECTION = "workingScreener";
5+
public static final String PUBLISHED_SCREENER_COLLECTION = "publishedScreener";
6+
public static final String WORKING_CUSTOM_CHECK_COLLECTION = "workingCustomCheck";
7+
public static final String PUBLISHED_CUSTOM_CHECK_COLLECTION = "publishedCustomCheck";
78
public static final String BENEFIT_COLLECTION = "benefit";
9+
public static final String PUBLIC_BENEFIT_COLLECTION = "publicBenefit";
10+
public static final String PUBLIC_CHECK_COLLECTION = "publicCheck";
811
}

builder-api/src/main/java/org/acme/controller/DecisionResource.java

Lines changed: 54 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import org.acme.auth.AuthUtils;
1212
import org.acme.enums.OptionalBoolean;
1313
import org.acme.model.domain.Benefit;
14-
import org.acme.model.domain.CheckConfig;
1514
import org.acme.model.domain.EligibilityCheck;
1615
import org.acme.model.domain.Screener;
1716
import org.acme.persistence.BenefitRepository;
@@ -55,7 +54,7 @@ public Response evaluateScreener(
5554
return Response.status(Response.Status.UNAUTHORIZED).build();
5655
}
5756

58-
Optional<Screener> screenerOpt = screenerRepository.getScreener(screenerId);
57+
Optional<Screener> screenerOpt = screenerRepository.getWorkingScreener(screenerId);
5958
if (screenerOpt.isEmpty()){
6059
return Response.status(Response.Status.NOT_FOUND).build();
6160
}
@@ -130,53 +129,64 @@ public Response evaluateBenefit(
130129
private Map<String, Object> evaluateBenefitDmn(Benefit benefit, Map<String, Object> inputData) throws Exception {
131130
List<EligibilityCheck> checks = eligibilityCheckRepository.getChecksInBenefit(benefit);
132131

133-
List<OptionalBoolean> checkResultsList = new ArrayList<>();
134-
Map<String, Object> checkResults = new HashMap<>();
135-
for (EligibilityCheck check : checks) {
136-
Optional<CheckConfig> matchingCheckConfig = benefit.getChecks().stream().filter(
137-
checkConfig -> checkConfig.getCheckId().equals(check.getId())
138-
).findFirst();
139-
if (matchingCheckConfig.isEmpty()) {
140-
throw new Exception("Could not find CheckConfig for check " + check.getId());
141-
}
142-
143-
String dmnFilepath = storageService.getCheckDmnModelPath(
144-
check.getModule(), check.getId(), check.getVersion()
145-
);
146-
String dmnModelName = check.getId();
147-
148-
OptionalBoolean result = dmnService.evaluateSimpleDmn(
149-
dmnFilepath, dmnModelName, inputData, matchingCheckConfig.get().getParameters()
150-
);
151-
checkResultsList.add(result);
152-
checkResults.put(check.getId(), Map.of("name", check.getName(),"result", result));
153-
}
154-
155-
// Determine overall Benefit result
156-
Boolean allChecksTrue = checkResultsList.stream().allMatch(result -> result == OptionalBoolean.TRUE);
157-
Boolean anyChecksFalse = checkResultsList.stream().anyMatch(result -> result == OptionalBoolean.FALSE);
158-
Log.info("All True: " + allChecksTrue + " Any False: " + anyChecksFalse);
159-
160-
OptionalBoolean benefitResult;
161-
if (allChecksTrue) {
162-
benefitResult = OptionalBoolean.TRUE;
163-
} else if (anyChecksFalse) {
164-
benefitResult = OptionalBoolean.FALSE;
132+
if (benefit.getPublic()){
133+
// Public benefit, call the Library API to evaluate
134+
Map<String, Object> result = new HashMap<>();
135+
return result;
165136
} else {
166-
benefitResult = OptionalBoolean.UNABLE_TO_DETERMINE;
137+
// Custom benefit, evaluate here in the web app api (as opposed to calling the library api for evaluation)
138+
List<OptionalBoolean> checkResultsList = new ArrayList<>();
139+
Map<String, Object> checkResults = new HashMap<>();
140+
141+
Map<String, Object> result = new HashMap<>();
142+
return result;
143+
//TODO: update implementation here
144+
// for (EligibilityCheck check : checks) {
145+
// Optional<CheckConfig> matchingCheckConfig = benefit.getChecks().stream().filter(
146+
// checkConfig -> checkConfig.getCheckId().equals(check.getId())
147+
// ).findFirst();
148+
// if (matchingCheckConfig.isEmpty()) {
149+
// throw new Exception("Could not find CheckConfig for check " + check.getId());
150+
// }
151+
//
152+
// String dmnFilepath = storageService.getCheckDmnModelPath(
153+
// check.getModule(), check.getId(), check.getVersion()
154+
// );
155+
// String dmnModelName = check.getId();
156+
//
157+
// OptionalBoolean result = dmnService.evaluateSimpleDmn(
158+
// dmnFilepath, dmnModelName, inputData, matchingCheckConfig.get().getParameters()
159+
// );
160+
// checkResultsList.add(result);
161+
// checkResults.put(check.getId(), Map.of("name", check.getName(), "result", result));
162+
// }
163+
//
164+
// // Determine overall Benefit result
165+
// Boolean allChecksTrue = checkResultsList.stream().allMatch(result -> result == OptionalBoolean.TRUE);
166+
// Boolean anyChecksFalse = checkResultsList.stream().anyMatch(result -> result == OptionalBoolean.FALSE);
167+
// Log.info("All True: " + allChecksTrue + " Any False: " + anyChecksFalse);
168+
//
169+
// OptionalBoolean benefitResult;
170+
// if (allChecksTrue) {
171+
// benefitResult = OptionalBoolean.TRUE;
172+
// } else if (anyChecksFalse) {
173+
// benefitResult = OptionalBoolean.FALSE;
174+
// } else {
175+
// benefitResult = OptionalBoolean.UNABLE_TO_DETERMINE;
176+
// }
177+
//
178+
// return new HashMap<String, Object>(
179+
// Map.of(
180+
// "name", benefit.getName(),
181+
// "result", benefitResult,
182+
// "check_results", checkResults
183+
// )
184+
// );
167185
}
168-
169-
return new HashMap<String, Object>(
170-
Map.of(
171-
"name", benefit.getName(),
172-
"result", benefitResult,
173-
"check_results", checkResults
174-
)
175-
);
176186
}
177187

178188
private boolean isUserAuthorizedToAccessScreenerByScreenerId(String userId, String screenerId) {
179-
Optional<Screener> screenerOpt = screenerRepository.getScreenerMetaDataOnly(screenerId);
189+
Optional<Screener> screenerOpt = screenerRepository.getWorkingScreenerMetaDataOnly(screenerId);
180190
if (screenerOpt.isEmpty()){
181191
return false;
182192
}

builder-api/src/main/java/org/acme/controller/EligibilityCheckResource.java

Lines changed: 117 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
import jakarta.ws.rs.core.MediaType;
99
import jakarta.ws.rs.core.Response;
1010
import org.acme.auth.AuthUtils;
11+
import org.acme.constants.CheckStatus;
1112
import org.acme.model.domain.EligibilityCheck;
1213
import org.acme.model.dto.SaveDmnRequest;
1314
import org.acme.persistence.EligibilityCheckRepository;
1415
import org.acme.persistence.StorageService;
1516

17+
import java.util.ArrayList;
1618
import java.util.List;
1719
import java.util.Map;
1820
import java.util.Optional;
@@ -27,27 +29,27 @@ public class EligibilityCheckResource {
2729
StorageService storageService;
2830

2931
@GET
30-
@Path("/check")
31-
public Response getAllChecks(@Context SecurityIdentity identity) {
32+
@Path("/checks")
33+
public Response getPublicChecks(@Context SecurityIdentity identity) {
3234
String userId = AuthUtils.getUserId(identity);
3335
if (userId == null) {
3436
return Response.status(Response.Status.UNAUTHORIZED).build();
3537
}
3638
Log.info("Fetching all eligibility checks. User: " + userId);
37-
List<EligibilityCheck> checks = eligibilityCheckRepository.getAllPublicChecks();
39+
List<EligibilityCheck> checks = eligibilityCheckRepository.getPublicChecks();
3840

3941
return Response.ok(checks, MediaType.APPLICATION_JSON).build();
4042
}
4143

4244
@GET
43-
@Path("/check/{checkId}")
44-
public Response getCheck(@Context SecurityIdentity identity, @PathParam("checkId") String checkId) {
45+
@Path("/checks/{checkId}")
46+
public Response getPublicCheck(@Context SecurityIdentity identity, @PathParam("checkId") String checkId) {
4547
String userId = AuthUtils.getUserId(identity);
4648
if (userId == null){
4749
return Response.status(Response.Status.UNAUTHORIZED).build();
4850
}
4951
Log.info("Fetching all eligibility checks. User: " + userId);
50-
Optional<EligibilityCheck> checkOpt = eligibilityCheckRepository.getCheck(checkId);
52+
Optional<EligibilityCheck> checkOpt = eligibilityCheckRepository.getPublicCheck(checkId);
5153

5254
if (checkOpt.isEmpty()){
5355
return Response.status(Response.Status.NOT_FOUND).build();
@@ -61,21 +63,19 @@ public Response getCheck(@Context SecurityIdentity identity, @PathParam("checkId
6163
return Response.ok(check, MediaType.APPLICATION_JSON).build();
6264
}
6365

64-
// Utility endpoint to create an Eligibility check
65-
// In the future seperate endpoints will need to be created for publishing public checks and creating private checks
66+
// Utility endpoint, public checks come from the Library API schema and shouldn't usually be created through the app
6667
@POST
67-
@Path("/check")
68-
public Response createCheck(@Context SecurityIdentity identity,
68+
@Path("/checks")
69+
public Response createPublicCheck(@Context SecurityIdentity identity,
6970
EligibilityCheck newCheck) {
7071
String userId = AuthUtils.getUserId(identity);
7172

7273
//TODO: Add validations for user provided data
7374
newCheck.setOwnerId(userId);
74-
newCheck.setPublic(true); // By default all created checks are public
75-
newCheck.setVersion("1");
75+
newCheck.setPublic(true);
76+
newCheck.setVersion(1);
7677
try {
77-
String checkId = eligibilityCheckRepository.saveNewCheck(newCheck);
78-
newCheck.setId(checkId);
78+
String checkId = eligibilityCheckRepository.savePublicCheck(newCheck);
7979
return Response.ok(newCheck, MediaType.APPLICATION_JSON).build();
8080
} catch (Exception e){
8181
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
@@ -84,15 +84,17 @@ public Response createCheck(@Context SecurityIdentity identity,
8484
}
8585
}
8686

87+
// Utility endpoint, public checks are static and come from the library api schema
88+
// and usually should not be updated through the app
8789
@PUT
88-
@Path("/check")
89-
public Response updateCheck(@Context SecurityIdentity identity,
90+
@Path("/checks")
91+
public Response updatePublicCheck(@Context SecurityIdentity identity,
9092
EligibilityCheck updateCheck){
9193
String userId = AuthUtils.getUserId(identity);
9294

9395
// TODO: Add authorization to update check
9496
try {
95-
eligibilityCheckRepository.updateCheck(updateCheck);
97+
eligibilityCheckRepository.savePublicCheck(updateCheck);
9698
return Response.ok().entity(updateCheck).build();
9799
} catch (Exception e){
98100
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
@@ -114,7 +116,7 @@ public Response updateCheckDmn(@Context SecurityIdentity identity, SaveDmnReques
114116
}
115117

116118
String userId = AuthUtils.getUserId(identity);
117-
Optional<EligibilityCheck> checkOpt = eligibilityCheckRepository.getCheck(checkId);
119+
Optional<EligibilityCheck> checkOpt = eligibilityCheckRepository.getWorkingCustomCheck(userId, checkId);
118120
if (checkOpt.isEmpty()){
119121
return Response.status(Response.Status.NOT_FOUND).build();
120122
}
@@ -132,7 +134,7 @@ public Response updateCheckDmn(@Context SecurityIdentity identity, SaveDmnReques
132134
.build();
133135
}
134136
try {
135-
String filePath = storageService.getCheckDmnModelPath(check.getModule(), check.getId(), check.getVersion());
137+
String filePath = storageService.getCheckDmnModelPath(userId, checkId);
136138
storageService.writeStringToStorage(filePath, dmnModel, "application/xml");
137139
Log.info("Saved DMN model of check " + checkId + " to storage");
138140

@@ -145,4 +147,100 @@ public Response updateCheckDmn(@Context SecurityIdentity identity, SaveDmnReques
145147
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
146148
}
147149
}
150+
151+
// By default, returns the most recent versions of all published checks owned by the calling user
152+
// If the query parameter 'working' is set to true,
153+
// then all the working check objects owned by the user are returned
154+
@GET
155+
@Path("/custom-checks")
156+
public Response getCustomChecks(@Context SecurityIdentity identity, @QueryParam("working") Boolean working) {
157+
String userId = AuthUtils.getUserId(identity);
158+
if (userId == null) {
159+
return Response.status(Response.Status.UNAUTHORIZED).build();
160+
}
161+
162+
List<EligibilityCheck> checks;
163+
164+
if (working != null && working){
165+
Log.info("Fetching all working custom checks. User: " + userId);
166+
checks = eligibilityCheckRepository.getWorkingCustomChecks(userId);
167+
} else {
168+
Log.info("Fetching all published custom checks. User: " + userId);
169+
checks = eligibilityCheckRepository.getPublishedCustomChecks(userId);
170+
}
171+
172+
return Response.ok(checks, MediaType.APPLICATION_JSON).build();
173+
}
174+
175+
@GET
176+
@Path("/custom-checks/{checkId}")
177+
public Response getCustomCheck(@Context SecurityIdentity identity, @PathParam("checkId") String checkId) {
178+
String userId = AuthUtils.getUserId(identity);
179+
if (userId == null) {
180+
return Response.status(Response.Status.UNAUTHORIZED).build();
181+
}
182+
183+
char statusIndicator = (checkId != null && !checkId.isEmpty())
184+
? checkId.charAt(0)
185+
: '\0';
186+
187+
Optional<EligibilityCheck> checkOpt;
188+
189+
if (statusIndicator == CheckStatus.WORKING.getCode()){
190+
Log.info("Fetching working custom check: " + checkId + " User: " + userId);
191+
checkOpt = eligibilityCheckRepository.getWorkingCustomCheck(userId, checkId);
192+
} else {
193+
Log.info("Fetching published custom check: " + checkId + " User: " + userId);
194+
checkOpt = eligibilityCheckRepository.getPublishedCustomCheck(userId, checkId);
195+
}
196+
197+
if (checkOpt.isEmpty()){
198+
return Response.status(Response.Status.NOT_FOUND).build();
199+
}
200+
201+
EligibilityCheck check = checkOpt.get();
202+
203+
if (!check.getPublic() && !check.getOwnerId().equals(userId)){
204+
return Response.status(Response.Status.UNAUTHORIZED).build();
205+
}
206+
return Response.ok(check, MediaType.APPLICATION_JSON).build();
207+
}
208+
209+
@POST
210+
@Path("/custom-checks")
211+
public Response createCustomCheck(@Context SecurityIdentity identity,
212+
EligibilityCheck newCheck) {
213+
String userId = AuthUtils.getUserId(identity);
214+
215+
//TODO: Add validations for user provided data
216+
newCheck.setOwnerId(userId);
217+
newCheck.setPublic(false);
218+
newCheck.setVersion(1);
219+
newCheck.setPublished(false);
220+
try {
221+
eligibilityCheckRepository.saveWorkingCustomCheck(newCheck);
222+
return Response.ok(newCheck, MediaType.APPLICATION_JSON).build();
223+
} catch (Exception e){
224+
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
225+
.entity(Map.of("error", "Could not save Check"))
226+
.build();
227+
}
228+
}
229+
230+
@PUT
231+
@Path("/custom-checks")
232+
public Response updateCustomCheck(@Context SecurityIdentity identity,
233+
EligibilityCheck updateCheck){
234+
String userId = AuthUtils.getUserId(identity);
235+
236+
// TODO: Add authorization to update check
237+
try {
238+
eligibilityCheckRepository.updateWorkingCustomCheck(updateCheck);
239+
return Response.ok().entity(updateCheck).build();
240+
} catch (Exception e){
241+
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
242+
.entity(Map.of("error", "could not update Check"))
243+
.build();
244+
}
245+
}
148246
}

0 commit comments

Comments
 (0)