Skip to content

Commit f5a8fa5

Browse files
authored
[JN-474] Various clean-ups and fixes based on integration testing with Pepper (#539)
1 parent a0f61d2 commit f5a8fa5

File tree

18 files changed

+262
-63
lines changed

18 files changed

+262
-63
lines changed

api-admin/src/main/java/bio/terra/pearl/api/admin/controller/kit/KitController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ public ResponseEntity<Object> requestKit(
5353
String kitType) {
5454
AdminUser adminUser = authUtilService.requireAdminUser(request);
5555
try {
56-
KitRequest sampleKit = kitExtService.requestKit(adminUser, enrolleeShortcode, kitType);
56+
KitRequest sampleKit =
57+
kitExtService.requestKit(adminUser, studyShortcode, enrolleeShortcode, kitType);
5758
return ResponseEntity.ok(sampleKit);
5859
} catch (PepperException e) {
5960
log.error("Error requesting sample kit from Pepper", e);

api-admin/src/main/java/bio/terra/pearl/api/admin/service/kit/KitExtService.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public List<KitRequest> requestKits(
3737
authUtilService.authUserToStudy(adminUser, portalShortcode, studyShortcode);
3838

3939
return enrolleeShortcodes.stream()
40-
.map(enrolleeShortcode -> requestKit(adminUser, enrolleeShortcode, kitType))
40+
.map(enrolleeShortcode -> requestKit(adminUser, studyShortcode, enrolleeShortcode, kitType))
4141
.toList();
4242
}
4343

@@ -54,9 +54,10 @@ public Collection<KitRequest> getKitRequestsByStudyEnvironment(
5454
return kitRequestService.getSampleKitsByStudyEnvironment(studyEnvironment);
5555
}
5656

57-
public KitRequest requestKit(AdminUser adminUser, String enrolleeShortcode, String kitTypeName) {
57+
public KitRequest requestKit(
58+
AdminUser adminUser, String studyShortcode, String enrolleeShortcode, String kitTypeName) {
5859
Enrollee enrollee = authUtilService.authAdminUserToEnrollee(adminUser, enrolleeShortcode);
59-
return kitRequestService.requestKit(adminUser, enrollee, kitTypeName);
60+
return kitRequestService.requestKit(adminUser, studyShortcode, enrollee, kitTypeName);
6061
}
6162

6263
public Collection<KitRequest> getKitRequests(AdminUser adminUser, String enrolleeShortcode) {

api-admin/src/main/resources/application.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ env:
2222
deploymentZone: ${DEPLOYMENT_ZONE:}
2323
tdrExportEnabled: ${TDR_EXPORT_ENABLED:false}
2424
dsm:
25-
basePath: ${DSM_ADDRESS:https://dsm-dev.datadonationplatform.org/ddp}
25+
basePath: ${DSM_ADDRESS:https://dsm-dev.datadonationplatform.org/dsm}
26+
issuerClaim: ${DSM_JWT_ISSUER:admin-d2p.ddp-dev.envs.broadinstitute.org}
2627
secret: ${DSM_JWT_SIGNING_SECRET:}
2728
populate:
2829
populate-from-classpath: true

core/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ dependencies {
2525
implementation group: 'org.jdbi', name: 'jdbi3-postgres', version: '3.34.0'
2626
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc:2.7.10'
2727
implementation 'org.springframework.boot:spring-boot-starter-webflux:2.7.10'
28+
implementation 'org.springframework.boot:spring-boot-starter-validation:2.7.14'
2829
implementation 'org.springframework.retry:spring-retry:1.3.4'
2930
implementation 'org.apache.commons:commons-text:1.10.0'
3031
implementation 'commons-beanutils:commons-beanutils:1.9.4'
@@ -48,6 +49,7 @@ dependencies {
4849
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.1'
4950
testImplementation 'org.springframework.boot:spring-boot-starter-test:2.7.10'
5051
testImplementation 'com.squareup.okhttp3:mockwebserver:4.11.0'
52+
testImplementation 'com.github.seregamorph:hamcrest-more-matchers:0.1'
5153

5254
testFixturesImplementation 'org.springframework.boot:spring-boot-starter-test:2.7.10'
5355
testFixturesImplementation 'org.apache.commons:commons-text:1.10.0'

core/src/main/java/bio/terra/pearl/core/service/kit/KitRequestService.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,15 @@ public KitRequestService(KitRequestDao dao,
7272
* transaction to help avoid that potential inconsistency.
7373
*/
7474
@Transactional
75-
public KitRequest requestKit(AdminUser adminUser, Enrollee enrollee, String kitTypeName) {
75+
public KitRequest requestKit(AdminUser adminUser, String studyShortcode, Enrollee enrollee, String kitTypeName) {
7676
// create and save kit request
7777
Profile profile = profileService.loadWithMailingAddress(enrollee.getProfileId())
7878
.orElseThrow(() -> new RuntimeException("Missing profile for enrollee: " + enrollee.getShortcode()));
7979
PepperKitAddress pepperKitAddress = makePepperKitAddress(profile);
8080
var kitRequest = createKitRequest(adminUser, enrollee, pepperKitAddress, kitTypeName);
8181

8282
// send kit request to DSM
83-
var result = pepperDSMClient.sendKitRequest(enrollee, kitRequest, pepperKitAddress);
83+
var result = pepperDSMClient.sendKitRequest(studyShortcode, enrollee, kitRequest, pepperKitAddress);
8484

8585
// save DSM response/status with Juniper KitRequest
8686
kitRequest.setDsmStatus(result);
@@ -163,7 +163,7 @@ public void syncAllKitStatusesFromPepper() {
163163
var pepperKitStatuses = pepperDSMClient.fetchKitStatusByStudy(study.getShortcode());
164164
var pepperStatusFetchedAt = Instant.now();
165165
var pepperKitStatusByKitId = pepperKitStatuses.stream().collect(
166-
Collectors.toMap(PepperKitStatus::getKitId, Function.identity()));
166+
Collectors.toMap(PepperKitStatus::getJuniperKitId, Function.identity()));
167167

168168
var studyEnvironments = studyEnvironmentService.findByStudy(study.getId());
169169
for (StudyEnvironment studyEnvironment : studyEnvironments) {

core/src/main/java/bio/terra/pearl/core/service/kit/LivePepperDSMClient.java

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,41 @@
1414
import org.springframework.web.reactive.function.client.WebClient;
1515
import reactor.core.publisher.Mono;
1616

17+
import javax.validation.Validator;
1718
import java.time.Instant;
1819
import java.util.Arrays;
1920
import java.util.Collection;
2021
import java.util.UUID;
2122
import java.util.function.Function;
23+
import java.util.stream.Collectors;
2224

2325
@Component
2426
public class LivePepperDSMClient implements PepperDSMClient {
2527
private final PepperDSMConfig pepperDSMConfig;
2628
private final WebClient webClient;
2729
private final ObjectMapper objectMapper;
30+
private final Validator validator;
2831

2932
public LivePepperDSMClient(PepperDSMConfig pepperDSMConfig,
3033
WebClient.Builder webClientBuilder,
31-
ObjectMapper objectMapper) {
34+
ObjectMapper objectMapper,
35+
Validator validator) {
3236
this.pepperDSMConfig = pepperDSMConfig;
3337
this.webClient = webClientBuilder.build();
3438
this.objectMapper = objectMapper;
39+
this.validator = validator;
3540
}
3641

3742
@Override
38-
public String sendKitRequest(Enrollee enrollee, KitRequest kitRequest, PepperKitAddress address) {
39-
var request = buildAuthedPostRequest("shipKit", makeKitRequestBody(enrollee, kitRequest, address));
43+
public String sendKitRequest(String studyShortcode, Enrollee enrollee, KitRequest kitRequest, PepperKitAddress address) {
44+
var request = buildAuthedPostRequest("shipKit", makeKitRequestBody(studyShortcode, enrollee, kitRequest, address));
4045
return retrieveAndDeserializeResponse(request, String.class);
4146
}
4247

4348
@Override
4449
public PepperKitStatus fetchKitStatus(UUID kitRequestId) {
45-
var request = buildAuthedGetRequest("kitStatus/kit/%s".formatted(kitRequestId));
50+
// TODO: change "juniperKit" to "juniperkit" after DSM updates this path
51+
var request = buildAuthedGetRequest("kitstatus/juniperKit/%s".formatted(kitRequestId));
4652
var response = retrieveAndDeserializeResponse(request, PepperKitStatusResponse.class);
4753
if (response.getKits().length != 1) {
4854
throw new PepperException("Expected a single result from fetchKitStatus by ID (%s), got %d".formatted(
@@ -53,7 +59,7 @@ public PepperKitStatus fetchKitStatus(UUID kitRequestId) {
5359

5460
@Override
5561
public Collection<PepperKitStatus> fetchKitStatusByStudy(String studyShortcode) {
56-
var request = buildAuthedGetRequest("kitStatus/study/%s".formatted(makePepperStudyName(studyShortcode)));
62+
var request = buildAuthedGetRequest("kitstatus/study/%s".formatted(makePepperStudyName(studyShortcode)));
5763
var response = retrieveAndDeserializeResponse(request, PepperKitStatusResponse.class);
5864
return Arrays.asList(response.getKits());
5965
}
@@ -62,15 +68,15 @@ private String makePepperStudyName(String studyShortcode) {
6268
return "juniper-" + studyShortcode;
6369
}
6470

65-
private String makeKitRequestBody(Enrollee enrollee, KitRequest kitRequest, PepperKitAddress address) {
71+
private String makeKitRequestBody(String studyShortcode, Enrollee enrollee, KitRequest kitRequest, PepperKitAddress address) {
6672
var juniperKitRequest = PepperDSMKitRequest.JuniperKitRequest.builderWithAddress(address)
6773
.juniperKitId(kitRequest.getId().toString())
6874
.juniperParticipantId(enrollee.getShortcode())
6975
.build();
7076
var pepperDSMKitRequest = PepperDSMKitRequest.builder()
7177
.juniperKitRequest(juniperKitRequest)
7278
.kitType(kitRequest.getKitType().getName())
73-
.juniperStudyId("Juniper-mock-guid")
79+
.juniperStudyId("juniper-%s".formatted(studyShortcode))
7480
.build();
7581

7682
try {
@@ -108,6 +114,7 @@ private String generateDsmJwt() {
108114
var now = System.currentTimeMillis();
109115
var fifteenMinutes = 15 * 60 * 1000;
110116
return JWT.create()
117+
.withIssuer(pepperDSMConfig.getIssuerClaim())
111118
.withExpiresAt(Instant.ofEpochMilli(now + fifteenMinutes))
112119
.sign(Algorithm.HMAC256(pepperDSMConfig.getSecret()));
113120
}
@@ -154,23 +161,43 @@ private <T> Function<String, Mono<T>> deserializeTo(Class<T> clazz) {
154161
return Mono.just(clazz.cast(body));
155162
}
156163
try {
157-
return Mono.just(objectMapper.readValue(body, clazz));
164+
var object = objectMapper.readValue(body, clazz);
165+
validate(object, clazz, body);
166+
return Mono.just(object);
158167
} catch (JsonProcessingException e) {
159168
return Mono.error(new PepperException(
160169
"Unable to parse response from Pepper as %s: %s".formatted(clazz.getName(), body), e));
161170
}
162171
};
163172
}
164173

174+
/** Validate returned object based on javax.validation annotations. */
175+
private <T> void validate(Object object, Class<T> clazz, String body) {
176+
var violations = validator.validate(object);
177+
if (!violations.isEmpty()) {
178+
throw new PepperException(
179+
"Unexpected %s from Pepper: %s; %s".formatted(
180+
clazz.getName(),
181+
body,
182+
violations.stream()
183+
.map(violation -> "%s %s".formatted(
184+
violation.getPropertyPath(),
185+
violation.getMessage()))
186+
.collect(Collectors.joining(", "))));
187+
}
188+
}
189+
165190
@Component
166191
@Getter
167192
@Setter
168193
public static class PepperDSMConfig {
169194
private String basePath;
195+
private String issuerClaim;
170196
private String secret;
171197

172198
public PepperDSMConfig(Environment environment) {
173199
this.basePath = environment.getProperty("env.dsm.basePath");
200+
this.issuerClaim = environment.getProperty("env.dsm.issuerClaim");
174201
this.secret = environment.getProperty("env.dsm.secret");
175202
}
176203
}

core/src/main/java/bio/terra/pearl/core/service/kit/PepperDSMClient.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ public interface PepperDSMClient {
1010
/**
1111
* Sends a sample kit request to Pepper.
1212
*
13-
* @param enrollee the enrollee to receive the sample kit
14-
* @param kitRequest sample kit request details
15-
* @param address mailing address for the sample kit
13+
* @param studyShortcode the shortcode of the Juniper study
14+
* @param enrollee the enrollee to receive the sample kit
15+
* @param kitRequest sample kit request details
16+
* @param address mailing address for the sample kit
1617
* @return status result from Pepper
1718
* @throws PepperException on error from Pepper or failure to process the Pepper response
1819
*/
19-
String sendKitRequest(Enrollee enrollee, KitRequest kitRequest, PepperKitAddress address);
20+
String sendKitRequest(String studyShortcode, Enrollee enrollee, KitRequest kitRequest, PepperKitAddress address);
2021
PepperKitStatus fetchKitStatus(UUID kitRequestId);
2122
Collection<PepperKitStatus> fetchKitStatusByStudy(String studyShortcode);
2223
}
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package bio.terra.pearl.core.service.kit;
22

3-
import com.fasterxml.jackson.annotation.JsonProperty;
43
import lombok.*;
54
import lombok.experimental.SuperBuilder;
65

6+
import javax.validation.constraints.NotNull;
7+
78
@Getter @Setter
89
@SuperBuilder @NoArgsConstructor
9-
@EqualsAndHashCode
10+
@EqualsAndHashCode(callSuper = true)
1011
@ToString
11-
public class PepperErrorResponse {
12+
public class PepperErrorResponse extends PepperResponse {
13+
@NotNull
1214
private String errorMessage;
15+
@NotNull
1316
private String juniperKitId;
1417
private String value;
1518
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package bio.terra.pearl.core.service.kit;
22

3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
34
import lombok.*;
45
import lombok.experimental.SuperBuilder;
56

67
@Getter @Setter
78
@SuperBuilder @NoArgsConstructor
89
@EqualsAndHashCode
910
@ToString
11+
@JsonIgnoreProperties(ignoreUnknown = true)
1012
public class PepperKitStatus {
11-
private String kitId;
13+
private String juniperKitId;
1214
private String currentStatus;
1315
private String labelDate;
1416
private String scanDate;
@@ -17,4 +19,5 @@ public class PepperKitStatus {
1719
private String returnTrackingNumber;
1820
private String errorMessage;
1921
private String errorDate;
22+
private Boolean error;
2023
}

core/src/main/java/bio/terra/pearl/core/service/kit/PepperKitStatusResponse.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
@Setter
88
@SuperBuilder
99
@NoArgsConstructor
10-
@EqualsAndHashCode
10+
@EqualsAndHashCode(callSuper = true)
1111
@ToString
12-
public class PepperKitStatusResponse {
12+
public class PepperKitStatusResponse extends PepperResponse {
1313
private PepperKitStatus[] kits;
1414
}

0 commit comments

Comments
 (0)