• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DataBiosphere / consent / #5119

24 May 2024 05:31PM UTC coverage: 76.36% (+0.08%) from 76.282%
#5119

push

web-flow
[DCJ-49][DCJ-50][risk=no] New APIs for DAA Enforced DAR Create/Update (#2330)

Co-authored-by: fboulnois <fboulnois@users.noreply.github.com>

88 of 93 new or added lines in 6 files covered. (94.62%)

1 existing line in 1 file now uncovered.

9739 of 12754 relevant lines covered (76.36%)

0.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.75
/src/main/java/org/broadinstitute/consent/http/resources/DataAccessRequestResource.java
1
package org.broadinstitute.consent.http.resources;
2

3
import com.google.cloud.storage.BlobId;
4
import com.google.common.base.Strings;
5
import com.google.inject.Inject;
6
import io.dropwizard.auth.Auth;
7
import jakarta.annotation.security.PermitAll;
8
import jakarta.annotation.security.RolesAllowed;
9
import jakarta.ws.rs.BadRequestException;
10
import jakarta.ws.rs.Consumes;
11
import jakarta.ws.rs.DELETE;
12
import jakarta.ws.rs.ForbiddenException;
13
import jakarta.ws.rs.GET;
14
import jakarta.ws.rs.NotFoundException;
15
import jakarta.ws.rs.POST;
16
import jakarta.ws.rs.PUT;
17
import jakarta.ws.rs.Path;
18
import jakarta.ws.rs.PathParam;
19
import jakarta.ws.rs.Produces;
20
import jakarta.ws.rs.core.Context;
21
import jakarta.ws.rs.core.MediaType;
22
import jakarta.ws.rs.core.Response;
23
import jakarta.ws.rs.core.StreamingOutput;
24
import jakarta.ws.rs.core.UriInfo;
25
import java.io.IOException;
26
import java.io.InputStream;
27
import java.net.URI;
28
import java.util.Collections;
29
import java.util.List;
30
import java.util.Objects;
31
import java.util.UUID;
32
import java.util.stream.Collectors;
33
import java.util.stream.Stream;
34
import org.apache.commons.lang3.StringUtils;
35
import org.broadinstitute.consent.http.cloudstore.GCSService;
36
import org.broadinstitute.consent.http.enumeration.DarDocumentType;
37
import org.broadinstitute.consent.http.enumeration.UserRoles;
38
import org.broadinstitute.consent.http.models.AuthUser;
39
import org.broadinstitute.consent.http.models.DataAccessRequest;
40
import org.broadinstitute.consent.http.models.DataAccessRequestData;
41
import org.broadinstitute.consent.http.models.DataUse;
42
import org.broadinstitute.consent.http.models.Dataset;
43
import org.broadinstitute.consent.http.models.Error;
44
import org.broadinstitute.consent.http.models.User;
45
import org.broadinstitute.consent.http.service.DataAccessRequestService;
46
import org.broadinstitute.consent.http.service.DatasetService;
47
import org.broadinstitute.consent.http.service.EmailService;
48
import org.broadinstitute.consent.http.service.MatchService;
49
import org.broadinstitute.consent.http.service.UserService;
50
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
51
import org.glassfish.jersey.media.multipart.FormDataParam;
52

53
@Path("api/dar")
54
public class DataAccessRequestResource extends Resource {
55

56
  private final DataAccessRequestService dataAccessRequestService;
57
  private final EmailService emailService;
58
  private final GCSService gcsService;
59
  private final MatchService matchService;
60
  private final UserService userService;
61
  private final DatasetService datasetService;
62

63
  @Inject
64
  public DataAccessRequestResource(
65
      DataAccessRequestService dataAccessRequestService,
66
      EmailService emailService,
67
      GCSService gcsService,
68
      UserService userService,
69
      DatasetService datasetService,
70
      MatchService matchService
71
  ) {
1✔
72
    this.dataAccessRequestService = dataAccessRequestService;
1✔
73
    this.emailService = emailService;
1✔
74
    this.gcsService = gcsService;
1✔
75
    this.userService = userService;
1✔
76
    this.datasetService = datasetService;
1✔
77
    this.matchService = matchService;
1✔
78
  }
1✔
79

80
  @GET
81
  @Produces("application/json")
82
  @PermitAll
83
  @Path("/v2")
84
  public Response getDataAccessRequests(@Auth AuthUser authUser) {
85
    try {
86
      User user = userService.findUserByEmail(authUser.getEmail());
1✔
87
      List<DataAccessRequest> dars = dataAccessRequestService.getDataAccessRequestsByUserRole(user);
1✔
88
      return Response.ok().entity(dars).build();
1✔
89
    } catch (Exception e) {
×
90
      return createExceptionResponse(e);
×
91
    }
92
  }
93

94
  @POST
95
  @Consumes("application/json")
96
  @Produces("application/json")
97
  @RolesAllowed(RESEARCHER)
98
  @Path("/v2")
99
  public Response createDataAccessRequest(
100
      @Auth AuthUser authUser, @Context UriInfo info, String dar) {
101
    try {
102
      User user = findUserByEmail(authUser.getEmail());
1✔
103
      if (Objects.isNull(user.getLibraryCards()) || user.getLibraryCards().isEmpty()) {
1✔
104
        throw new IllegalArgumentException("User must have a library card to create a DAR.");
1✔
105
      }
106

107
      DataAccessRequest payload = populateDarFromJsonString(user, dar);
1✔
108
      DataAccessRequest newDar = dataAccessRequestService.createDataAccessRequest(user, payload);
1✔
109
      Integer collectionId = newDar.getCollectionId();
1✔
110
      try {
111
        emailService.sendNewDARCollectionMessage(collectionId);
1✔
112
      } catch (Exception e) {
×
113
        // non-fatal exception
114
        logException("Exception sending email for collection id: " + collectionId, e);
×
115
      }
1✔
116
      URI uri = info.getRequestUriBuilder().build();
1✔
117
      matchService.reprocessMatchesForPurpose(newDar.getReferenceId());
1✔
118
      return Response.created(uri).entity(newDar.convertToSimplifiedDar()).build();
1✔
119
    } catch (Exception e) {
1✔
120
      return createExceptionResponse(e);
1✔
121
    }
122
  }
123

124
  @POST
125
  @Consumes("application/json")
126
  @Produces("application/json")
127
  @RolesAllowed(RESEARCHER)
128
  @Path("/v3")
129
  public Response createDataAccessRequestWithDAARestrictions(
130
      @Auth AuthUser authUser, @Context UriInfo info, String dar) {
131
    try {
132
      User user = findUserByEmail(authUser.getEmail());
1✔
133
      if (Objects.isNull(user.getLibraryCards()) || user.getLibraryCards().isEmpty()) {
1✔
NEW
134
        throw new IllegalArgumentException("User must have a library card to create a DAR.");
×
135
      }
136
      DataAccessRequest payload = populateDarFromJsonString(user, dar);
1✔
137
      // DAA Enforcement
138
      datasetService.enforceDAARestrictions(user, payload.getDatasetIds());
1✔
139
      DataAccessRequest newDar = dataAccessRequestService.createDataAccessRequest(user, payload);
1✔
140
      Integer collectionId = newDar.getCollectionId();
1✔
141
      try {
142
        emailService.sendNewDARCollectionMessage(collectionId);
1✔
NEW
143
      } catch (Exception e) {
×
144
        // non-fatal exception
NEW
145
        logException("Exception sending email for collection id: " + collectionId, e);
×
146
      }
1✔
147
      URI uri = info.getRequestUriBuilder().build();
1✔
148
      matchService.reprocessMatchesForPurpose(newDar.getReferenceId());
1✔
149
      return Response.created(uri).entity(newDar.convertToSimplifiedDar()).build();
1✔
150
    } catch (Exception e) {
1✔
151
      return createExceptionResponse(e);
1✔
152
    }
153
  }
154

155
  @GET
156
  @Path("/v2/{referenceId}")
157
  @Produces("application/json")
158
  @PermitAll
159
  public Response getByReferenceId(
160
      @Auth AuthUser authUser, @PathParam("referenceId") String referenceId) {
161
    validateAuthedRoleUser(
1✔
162
        Stream.of(UserRoles.ADMIN, UserRoles.CHAIRPERSON, UserRoles.MEMBER)
1✔
163
            .collect(Collectors.toList()),
1✔
164
        authUser, referenceId);
165
    try {
166
      DataAccessRequest dar = dataAccessRequestService.findByReferenceId(referenceId);
1✔
167
      if (Objects.nonNull(dar)) {
1✔
168
        return Response.status(Response.Status.OK).entity(dar.convertToSimplifiedDar()).build();
1✔
169
      }
170
      return Response.status(Response.Status.NOT_FOUND)
×
171
          .entity(
×
172
              new Error(
173
                  "Unable to find Data Access Request with reference id: " + referenceId,
174
                  Response.Status.NOT_FOUND.getStatusCode()))
×
175
          .build();
×
176
    } catch (Exception e) {
×
177
      return createExceptionResponse(e);
×
178
    }
179
  }
180

181
  @PUT
182
  @Path("/v2/{referenceId}")
183
  @Produces("application/json")
184
  @RolesAllowed(RESEARCHER)
185
  public Response updateByReferenceId(
186
      @Auth AuthUser authUser, @PathParam("referenceId") String referenceId, String dar) {
187
    try {
188
      User user = findUserByEmail(authUser.getEmail());
1✔
189
      DataAccessRequest originalDar = dataAccessRequestService.findByReferenceId(referenceId);
1✔
190
      checkAuthorizedUpdateUser(user, originalDar);
1✔
191
      DataAccessRequestData data = DataAccessRequestData.fromString(dar);
1✔
192
      // Keep dar data reference id in sync with the dar until we fully deprecate
193
      // it in dar data.
194
      data.setReferenceId(originalDar.getReferenceId());
1✔
195
      originalDar.setData(data);
1✔
196
      DataAccessRequest updatedDar =
1✔
197
          dataAccessRequestService.updateByReferenceId(user, originalDar);
1✔
198
      matchService.reprocessMatchesForPurpose(referenceId);
1✔
199
      return Response.ok().entity(updatedDar.convertToSimplifiedDar()).build();
1✔
200
    } catch (Exception e) {
1✔
201
      return createExceptionResponse(e);
1✔
202
    }
203
  }
204

205
  @GET
206
  @Produces("application/json")
207
  @Path("/v2/draft")
208
  @RolesAllowed(RESEARCHER)
209
  public Response getDraftDataAccessRequests(@Auth AuthUser authUser) {
210
    try {
211
      User user = findUserByEmail(authUser.getEmail());
1✔
212
      List<DataAccessRequest> draftDars = dataAccessRequestService.findAllDraftDataAccessRequestsByUser(
1✔
213
          user.getUserId());
1✔
214
      return Response.ok().entity(draftDars).build();
1✔
215
    } catch (Exception e) {
1✔
216
      return createExceptionResponse(e);
1✔
217
    }
218
  }
219

220
  @GET
221
  @Produces("application/json")
222
  @Path("/v2/draft/{referenceId}")
223
  @RolesAllowed(RESEARCHER)
224
  public Response getDraftDar(@Auth AuthUser authUser, @PathParam("referenceId") String id) {
225
    try {
226
      User user = findUserByEmail(authUser.getEmail());
1✔
227
      DataAccessRequest dar = dataAccessRequestService.findByReferenceId(id);
1✔
228
      if (dar.getUserId().equals(user.getUserId())) {
1✔
229
        return Response.ok().entity(dar).build();
1✔
230
      }
231
      throw new ForbiddenException("User does not have permission");
1✔
232
    } catch (Exception e) {
1✔
233
      return createExceptionResponse(e);
1✔
234
    }
235
  }
236

237
  @POST
238
  @Consumes("application/json")
239
  @Produces("application/json")
240
  @Path("/v2/draft")
241
  @RolesAllowed(RESEARCHER)
242
  public Response createDraftDataAccessRequest(
243
      @Auth AuthUser authUser, @Context UriInfo info, String dar) {
244
    try {
245
      User user = findUserByEmail(authUser.getEmail());
1✔
246
      DataAccessRequest newDar = populateDarFromJsonString(user, dar);
1✔
247
      DataAccessRequest result =
1✔
248
          dataAccessRequestService.insertDraftDataAccessRequest(user, newDar);
1✔
249
      URI uri = info.getRequestUriBuilder().path("/" + result.getReferenceId()).build();
1✔
250
      return Response.created(uri).entity(result.convertToSimplifiedDar()).build();
1✔
251
    } catch (Exception e) {
×
252
      return createExceptionResponse(e);
×
253
    }
254
  }
255

256
  @POST
257
  @Consumes("application/json")
258
  @Produces("application/json")
259
  @Path("/v3/draft")
260
  @RolesAllowed(RESEARCHER)
261
  public Response createDraftDataAccessRequestWithDAARestrictions(
262
      @Auth AuthUser authUser, @Context UriInfo info, String dar) {
263
    try {
264
      User user = findUserByEmail(authUser.getEmail());
1✔
265
      DataAccessRequest newDar = populateDarFromJsonString(user, dar);
1✔
266
      // DAA Enforcement
267
      datasetService.enforceDAARestrictions(user, newDar.getDatasetIds());
1✔
268
      DataAccessRequest result =
1✔
269
          dataAccessRequestService.insertDraftDataAccessRequest(user, newDar);
1✔
270
      URI uri = info.getRequestUriBuilder().path("/" + result.getReferenceId()).build();
1✔
271
      return Response.created(uri).entity(result.convertToSimplifiedDar()).build();
1✔
272
    } catch (Exception e) {
1✔
273
      return createExceptionResponse(e);
1✔
274
    }
275
  }
276

277
  @PUT
278
  @Consumes("application/json")
279
  @Produces("application/json")
280
  @Path("/v2/draft/{referenceId}")
281
  @RolesAllowed(RESEARCHER)
282
  public Response updatePartialDataAccessRequest(
283
      @Auth AuthUser authUser, @PathParam("referenceId") String referenceId, String dar) {
284
    try {
285
      User user = findUserByEmail(authUser.getEmail());
1✔
286
      DataAccessRequest originalDar = dataAccessRequestService.findByReferenceId(referenceId);
1✔
287
      checkAuthorizedUpdateUser(user, originalDar);
1✔
288
      DataAccessRequestData data = DataAccessRequestData.fromString(dar);
1✔
289
      // Keep dar data reference id in sync with the dar until we fully deprecate
290
      // it in dar data.
291
      data.setReferenceId(originalDar.getReferenceId());
1✔
292
      originalDar.setData(data);
1✔
293
      originalDar.setDatasetIds(data.getDatasetIds());
1✔
294
      DataAccessRequest updatedDar =
1✔
295
          dataAccessRequestService.updateByReferenceId(user, originalDar);
1✔
296
      return Response.ok().entity(updatedDar.convertToSimplifiedDar()).build();
1✔
297
    } catch (Exception e) {
1✔
298
      return createExceptionResponse(e);
1✔
299
    }
300
  }
301

302
  @PUT
303
  @Consumes("application/json")
304
  @Produces("application/json")
305
  @Path("/v3/draft/{referenceId}")
306
  @RolesAllowed(RESEARCHER)
307
  public Response updatePartialDataAccessRequestWithDAARestrictions(
308
      @Auth AuthUser authUser, @PathParam("referenceId") String referenceId, String dar) {
309
    try {
310
      User user = findUserByEmail(authUser.getEmail());
1✔
311
      DataAccessRequest originalDar = dataAccessRequestService.findByReferenceId(referenceId);
1✔
312
      checkAuthorizedUpdateUser(user, originalDar);
1✔
313
      DataAccessRequestData data = DataAccessRequestData.fromString(dar);
1✔
314
      // Keep dar data reference id in sync with the dar until we fully deprecate
315
      // it in dar data.
316
      data.setReferenceId(originalDar.getReferenceId());
1✔
317
      originalDar.setData(data);
1✔
318
      originalDar.setDatasetIds(data.getDatasetIds());
1✔
319
      // DAA Enforcement
320
      datasetService.enforceDAARestrictions(user, originalDar.getDatasetIds());
1✔
321
      DataAccessRequest updatedDar =
1✔
322
          dataAccessRequestService.updateByReferenceId(user, originalDar);
1✔
323
      return Response.ok().entity(updatedDar.convertToSimplifiedDar()).build();
1✔
324
    } catch (Exception e) {
1✔
325
      return createExceptionResponse(e);
1✔
326
    }
327
  }
328

329
  @GET
330
  @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON})
331
  @Path("/v2/{referenceId}/irbDocument")
332
  @RolesAllowed({ADMIN, CHAIRPERSON, MEMBER, RESEARCHER})
333
  public Response getIrbDocument(
334
      @Auth AuthUser authUser,
335
      @PathParam("referenceId") String referenceId) {
336
    try {
337
      DataAccessRequest dar = getDarById(referenceId);
1✔
338
      validateAuthedRoleUser(
1✔
339
          Stream.of(UserRoles.ADMIN, UserRoles.CHAIRPERSON, UserRoles.MEMBER)
1✔
340
              .collect(Collectors.toList()),
1✔
341
          authUser, referenceId);
342
      if (dar.getData() != null &&
1✔
343
          StringUtils.isNotEmpty(dar.getData().getIrbDocumentLocation()) &&
1✔
344
          StringUtils.isNotEmpty(dar.getData().getIrbDocumentName())
1✔
345
      ) {
346
        String blobIdName = dar.getData().getIrbDocumentLocation();
1✔
347
        String fileName = dar.getData().getIrbDocumentName();
1✔
348
        InputStream is = gcsService.getDocument(blobIdName);
1✔
349
        StreamingOutput stream = createStreamingOutput(is);
1✔
350
        return Response.ok(stream)
1✔
351
            .header("Content-Disposition", "attachment; filename=" + fileName)
1✔
352
            .build();
1✔
353
      }
354
      throw new NotFoundException();
1✔
355
    } catch (Exception e) {
1✔
356
      return createExceptionResponse(e);
1✔
357
    }
358
  }
359

360
  @POST
361
  @Consumes(MediaType.MULTIPART_FORM_DATA)
362
  @Produces(MediaType.APPLICATION_JSON)
363
  @Path("/v2/{referenceId}/irbDocument")
364
  @RolesAllowed({RESEARCHER})
365
  public Response uploadIrbDocument(
366
      @Auth AuthUser authUser,
367
      @PathParam("referenceId") String referenceId,
368
      @FormDataParam("file") InputStream uploadInputStream,
369
      @FormDataParam("file") FormDataContentDisposition fileDetail) {
370
    try {
371
      User user = findUserByEmail(authUser.getEmail());
1✔
372
      DataAccessRequest dar = getDarById(referenceId);
1✔
373
      checkAuthorizedUpdateUser(user, dar);
1✔
374
      DataAccessRequest updatedDar = updateDarWithDocumentContents(DarDocumentType.IRB, user, dar,
1✔
375
          uploadInputStream, fileDetail);
376
      return Response.ok(updatedDar.convertToSimplifiedDar()).build();
1✔
377
    } catch (Exception e) {
1✔
378
      return createExceptionResponse(e);
1✔
379
    }
380
  }
381

382
  @POST
383
  @Consumes(MediaType.MULTIPART_FORM_DATA)
384
  @Produces(MediaType.APPLICATION_JSON)
385
  @Path("/v2/progress_report/{parentReferenceId}")
386
  @RolesAllowed({RESEARCHER})
387
  public Response postProgressReport(
388
      @Auth AuthUser authUser,
389
      @PathParam("parentReferenceId") String parentReferenceId,
390
      @FormDataParam("dar") String dar,
391
      @FormDataParam("collaboratorRequiredFile") InputStream collabInputStream,
392
      @FormDataParam("collaboratorRequiredFile") FormDataContentDisposition collabFileDetails,
393
      @FormDataParam("ethicsApprovalRequiredFile") InputStream ethicsInputStream,
394
      @FormDataParam("ethicsApprovalRequiredFile") FormDataContentDisposition ethicsFileDetails) {
395
    User user = userService.findUserByEmail(authUser.getEmail());
1✔
396
    DataAccessRequest parentDar = dataAccessRequestService.findByReferenceId(parentReferenceId);
1✔
397
    checkAuthorizedUpdateUser(user, parentDar);
1✔
398
    DataAccessRequest payload = populateDarFromJsonString(user, dar);
1✔
399
    DataAccessRequest childDar = dataAccessRequestService.createDataAccessRequest(user, payload);
1✔
400

401
    for (Integer datasetId : childDar.getDatasetIds()) {
1✔
402
      Dataset dataset = datasetService.findDatasetById(datasetId);
1✔
403
      if (dataset == null) {
1✔
404
        throw new NotFoundException("Dataset " + datasetId + " not found");
1✔
405
      }
406
      DataUse dataUse = dataset.getDataUse();
1✔
407
      if (dataUse == null || dataUse.getCollaboratorRequired() == null
1✔
408
          || dataUse.getEthicsApprovalRequired() == null) {
1✔
409
        throw new BadRequestException("Dataset " + datasetId + " is missing data use(s)");
1✔
410
      }
411
      if (dataUse.getCollaboratorRequired()) {
1✔
412
        String parentCollabLocation = parentDar.getData().getCollaborationLetterLocation();
1✔
413
        if ((collabFileDetails == null || collabFileDetails.getSize() <= 0)
1✔
414
            && Strings.isNullOrEmpty(parentCollabLocation)) {
1✔
415
          throw new BadRequestException("Collaboration document is required");
1✔
416
        }
417
        try {
418
          childDar = updateDarWithDocumentContents(DarDocumentType.COLLABORATION, user, childDar,
1✔
419
              collabInputStream, collabFileDetails);
420
        } catch (IOException e) {
×
421
          return createExceptionResponse(e);
×
422
        }
1✔
423
      }
424
      if (dataUse.getEthicsApprovalRequired()) {
1✔
425
        String parentEthicsLocation = parentDar.getData().getIrbDocumentLocation();
1✔
426
        if ((ethicsFileDetails == null || ethicsFileDetails.getSize() <= 0)
1✔
427
            && Strings.isNullOrEmpty(parentEthicsLocation)) {
1✔
428
          throw new BadRequestException("Ethics approval document is required");
×
429
        }
430
        try {
431
          childDar = updateDarWithDocumentContents(DarDocumentType.IRB, user, childDar,
1✔
432
              ethicsInputStream, ethicsFileDetails);
433
        } catch (IOException e) {
×
434
          return createExceptionResponse(e);
×
435
        }
1✔
436
      }
437
    }
1✔
438

439
    return Response.ok(childDar.convertToSimplifiedDar()).build();
1✔
440
  }
441

442
  @GET
443
  @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON})
444
  @Path("/v2/{referenceId}/collaborationDocument")
445
  @RolesAllowed({ADMIN, CHAIRPERSON, MEMBER, RESEARCHER})
446
  public Response getCollaborationDocument(
447
      @Auth AuthUser authUser,
448
      @PathParam("referenceId") String referenceId) {
449
    try {
450
      DataAccessRequest dar = getDarById(referenceId);
1✔
451
      validateAuthedRoleUser(
1✔
452
          Stream.of(UserRoles.ADMIN, UserRoles.CHAIRPERSON, UserRoles.MEMBER)
1✔
453
              .collect(Collectors.toList()),
1✔
454
          authUser, referenceId);
455
      if (dar.getData() != null &&
1✔
456
          StringUtils.isNotEmpty(dar.getData().getCollaborationLetterLocation()) &&
1✔
457
          StringUtils.isNotEmpty(dar.getData().getCollaborationLetterName())
1✔
458
      ) {
459
        String blobIdName = dar.getData().getCollaborationLetterLocation();
1✔
460
        String fileName = dar.getData().getCollaborationLetterName();
1✔
461
        InputStream is = gcsService.getDocument(blobIdName);
1✔
462
        StreamingOutput stream = createStreamingOutput(is);
1✔
463
        return Response.ok(stream)
1✔
464
            .header("Content-Disposition", "attachment; filename=" + fileName)
1✔
465
            .build();
1✔
466
      }
467
      throw new NotFoundException();
1✔
468
    } catch (Exception e) {
1✔
469
      return createExceptionResponse(e);
1✔
470
    }
471
  }
472

473
  @POST
474
  @Consumes(MediaType.MULTIPART_FORM_DATA)
475
  @Produces(MediaType.APPLICATION_JSON)
476
  @Path("/v2/{referenceId}/collaborationDocument")
477
  @RolesAllowed({RESEARCHER})
478
  public Response uploadCollaborationDocument(
479
      @Auth AuthUser authUser,
480
      @PathParam("referenceId") String referenceId,
481
      @FormDataParam("file") InputStream uploadInputStream,
482
      @FormDataParam("file") FormDataContentDisposition fileDetail) {
483
    try {
484
      User user = findUserByEmail(authUser.getEmail());
1✔
485
      DataAccessRequest dar = getDarById(referenceId);
1✔
486
      checkAuthorizedUpdateUser(user, dar);
1✔
487
      DataAccessRequest updatedDar = updateDarWithDocumentContents(DarDocumentType.COLLABORATION,
1✔
488
          user, dar, uploadInputStream, fileDetail);
489
      return Response.ok(updatedDar.convertToSimplifiedDar()).build();
1✔
490
    } catch (Exception e) {
1✔
491
      return createExceptionResponse(e);
1✔
492
    }
493
  }
494

495
  @DELETE
496
  @Path("/v2/{referenceId}")
497
  @Produces("application/json")
498
  @RolesAllowed({ADMIN, RESEARCHER})
499
  public Response deleteDar(@Auth AuthUser authUser, @PathParam("referenceId") String referenceId) {
500
    validateAuthedRoleUser(Collections.singletonList(UserRoles.ADMIN), authUser, referenceId);
×
501
    try {
502
      User user = findUserByEmail(authUser.getEmail());
×
503
      dataAccessRequestService.deleteByReferenceId(user, referenceId);
×
504
      return Response.ok().build();
×
505
    } catch (Exception e) {
×
506
      return createExceptionResponse(e);
×
507
    }
508
  }
509

510
  private User findUserByEmail(String email) {
511
    User user = userService.findUserByEmail(email);
1✔
512
    if (user == null) {
1✔
513
      throw new NotFoundException("Unable to find User with the provided email: " + email);
×
514
    }
515
    return user;
1✔
516
  }
517

518
  private DataAccessRequest populateDarFromJsonString(User user, String json) {
519
    DataAccessRequest newDar = new DataAccessRequest();
1✔
520
    DataAccessRequestData data;
521
    try {
522
      data = DataAccessRequestData.fromString(json);
1✔
523
    } catch (Exception e) {
1✔
524
      throw new BadRequestException("Unable to parse DAR from JSON string");
1✔
525
    }
1✔
526
    if (Objects.isNull(data)) {
1✔
527
      data = new DataAccessRequestData();
1✔
528
    }
529
    // When posting a submitted dar, there are two cases:
530
    // 1. those that existed previously as a draft dar
531
    // 2. those that are brand new
532
    // Validate the provided referenceId with the authenticated user and draft status
533
    // Those that do not validate are considered a brand new dar
534
    if (Objects.nonNull(data.getReferenceId())) {
1✔
535
      DataAccessRequest existingDar =
×
536
          dataAccessRequestService.findByReferenceId(data.getReferenceId());
×
537
      if (Objects.nonNull(existingDar)
×
538
          && existingDar.getUserId().equals(user.getUserId())
×
539
          && existingDar.getDraft()) {
×
540
        newDar.setReferenceId(data.getReferenceId());
×
541

542
        // if dar was part of a collection, we should use the same collection.
543
        if (Objects.nonNull(existingDar.getCollectionId())) {
×
544
          newDar.setCollectionId(existingDar.getCollectionId());
×
545
        }
546
      } else {
547
        String referenceId = UUID.randomUUID().toString();
×
548
        newDar.setReferenceId(referenceId);
×
549
        data.setReferenceId(referenceId);
×
550
      }
551
    } else {
×
552
      String referenceId = UUID.randomUUID().toString();
1✔
553
      newDar.setReferenceId(referenceId);
1✔
554
      data.setReferenceId(referenceId);
1✔
555
    }
556
    newDar.setData(data);
1✔
557
    newDar.addDatasetIds(data.getDatasetIds());
1✔
558
    return newDar;
1✔
559
  }
560

561
  private void checkAuthorizedUpdateUser(User user, DataAccessRequest dar) {
562
    if (!user.getUserId().equals(dar.getUserId())) {
1✔
563
      throw new ForbiddenException("User not authorized to update this Data Access Request");
1✔
564
    }
565
  }
1✔
566

567
  private DataAccessRequest updateDarWithDocumentContents(
568
      DarDocumentType type,
569
      User user,
570
      DataAccessRequest dar,
571
      InputStream uploadInputStream,
572
      FormDataContentDisposition fileDetail) throws IOException {
573
    validateFileDetails(fileDetail);
1✔
574
    String fileName = fileDetail.getFileName();
1✔
575
    UUID id = UUID.randomUUID();
1✔
576
    BlobId blobId = gcsService.storeDocument(uploadInputStream, fileDetail.getType(), id);
1✔
577
    switch (type) {
1✔
578
      case IRB:
579
        // Delete the current document if it exists
580
        if (Objects.nonNull(dar.getData().getIrbDocumentLocation())) {
1✔
581
          deleteDarDocument(dar, dar.getData().getIrbDocumentLocation());
1✔
582
        }
583
        dar.getData().setIrbDocumentLocation(blobId.getName());
1✔
584
        dar.getData().setIrbDocumentName(fileName);
1✔
585
        break;
1✔
586
      case COLLABORATION:
587
        // Delete the current document if it exists
588
        if (Objects.nonNull(dar.getData().getCollaborationLetterLocation())) {
1✔
589
          deleteDarDocument(dar, dar.getData().getCollaborationLetterLocation());
1✔
590
        }
591
        dar.getData().setCollaborationLetterLocation(blobId.getName());
1✔
592
        dar.getData().setCollaborationLetterName(fileName);
1✔
593
        break;
1✔
594
      default:
595
        break;
596
    }
597
    return dataAccessRequestService.updateByReferenceId(user, dar);
1✔
598
  }
599

600
  private void deleteDarDocument(DataAccessRequest dar, String blobIdName) {
601
    try {
602
      gcsService.deleteDocument(blobIdName);
1✔
603
    } catch (Exception e) {
×
604
      String message = String.format(
×
605
          "Unable to delete document for DAR ID: %s; dar document location: %s",
606
          dar.getReferenceId(), blobIdName);
×
607
      logWarn(message);
×
608
    }
1✔
609
  }
1✔
610

611
  private DataAccessRequest getDarById(String referenceId) {
612
    DataAccessRequest dar = dataAccessRequestService.findByReferenceId(referenceId);
1✔
613
    if (Objects.isNull(dar)) {
1✔
614
      throw new NotFoundException();
1✔
615
    }
616
    return dar;
1✔
617
  }
618

619
  /**
620
   * Custom handler for validating that a user can access a DAR. User will have access if ANY of
621
   * these conditions are met: If the DAR create user is the same as the Auth User, then the user
622
   * can access the resource. If the user has any of the roles in allowableRoles, then the user can
623
   * access the resource. In practice, pass in allowableRoles for users that are not the create user
624
   * (i.e. Admin) so they can also have access to the DAR.
625
   *
626
   * @param allowableRoles List of roles that would allow the user to access the resource
627
   * @param authUser       The AuthUser
628
   * @param referenceId    The referenceId of the resource.
629
   */
630
  private void validateAuthedRoleUser(final List<UserRoles> allowableRoles, AuthUser authUser,
631
      String referenceId) {
632
    DataAccessRequest dataAccessRequest = getDarById(referenceId);
1✔
633
    User user = findUserByEmail(authUser.getEmail());
1✔
634
    if (Objects.nonNull(dataAccessRequest.getUserId()) && dataAccessRequest.getUserId() > 0) {
1✔
635
      super.validateAuthedRoleUser(allowableRoles, user, dataAccessRequest.getUserId());
1✔
636
    } else {
637
      logWarn("DataAccessRequest '" + referenceId + "' has an invalid userId");
×
638
      super.validateAuthedRoleUser(allowableRoles, user, dataAccessRequest.getUserId());
×
639
    }
640
  }
1✔
641
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc