Skip to content

BI-2258 - Endpoint filtering for Experimental Collaborator role#385

Merged
mlm483 merged 19 commits intodevelopfrom
feature/BI-2258
Aug 29, 2024
Merged

BI-2258 - Endpoint filtering for Experimental Collaborator role#385
mlm483 merged 19 commits intodevelopfrom
feature/BI-2258

Conversation

@mlm483
Copy link
Contributor

@mlm483 mlm483 commented Aug 9, 2024

Description

Story: https://breedinginsight.atlassian.net/browse/BI-2258

  • Added filtering-by-authorized-experiments to two endpoints that Experimental Collaborators are allowed to access. Filtering only happens for users in the Experimental Collaborator role, for other users, no filtering is applied. The following methods were updated.
    • BrAPIStudiesController::getStudies
    • BrAPITrialsController::getExperiments
  • Added integration tests for this filtering.
  • Explicitly implmented /seasons GET endpoint instead of relying on BrAPIV2Controller::getCatchall. The idea here is that Experimental Collaborators are allowed to access this endpoint, but not every GET endpoint that was handled by the catchall method.

Testing

See acceptance criteria on Jira.
The testing steps on for BI-2283, in PR #393 test this story as well.

Checklist:

  • I have performed a self-review of my own code
  • I have tested my code and ensured it meets the acceptance criteria of the story
  • I have tested that my code works with both the brapi-java-server and BreedBase
  • I have create/modified unit tests to cover this change
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to documentation
  • I have run TAF: <please include a link to TAF run>

@mlm483 mlm483 marked this pull request as ready for review August 19, 2024 20:58
@mlm483 mlm483 requested review from a team, davedrp and dmeidlin and removed request for a team August 19, 2024 21:35
Copy link
Contributor

@davedrp davedrp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passes Test

Comment on lines +101 to +119
Optional<ProgramUser> experimentalCollaborator = programUserService.getIfExperimentalCollaborator(programId, securityService.getUser().getId());
// If the program user is an experimental collaborator, filter results.
if (experimentalCollaborator.isPresent()) {
Optional<Program> program = programService.getById(programId);
if (program.isEmpty()) {
return HttpResponse.notFound();
}

List<UUID> experimentIds = experimentalCollaboratorService.getAuthorizedExperimentIds(experimentalCollaborator.get().getId());
studies = studyService.getStudiesByExperimentIds(program.get(), experimentIds)
.stream()
.peek(this::setDbIds)
.collect(Collectors.toList());
} else {
studies = studyService.getStudies(programId)
.stream()
.peek(this::setDbIds)
.collect(Collectors.toList());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional methods could be used to refactor the fetching logic.

Suggested change
Optional<ProgramUser> experimentalCollaborator = programUserService.getIfExperimentalCollaborator(programId, securityService.getUser().getId());
// If the program user is an experimental collaborator, filter results.
if (experimentalCollaborator.isPresent()) {
Optional<Program> program = programService.getById(programId);
if (program.isEmpty()) {
return HttpResponse.notFound();
}
List<UUID> experimentIds = experimentalCollaboratorService.getAuthorizedExperimentIds(experimentalCollaborator.get().getId());
studies = studyService.getStudiesByExperimentIds(program.get(), experimentIds)
.stream()
.peek(this::setDbIds)
.collect(Collectors.toList());
} else {
studies = studyService.getStudies(programId)
.stream()
.peek(this::setDbIds)
.collect(Collectors.toList());
}
Optional<HttpResponse> httpResponse = programUserService.getIfExperimentalCollaborator(programId, securityService.getUser().getId())
.flatMap(experimentalCollaborator -> { //If the program user is an experimental collaborator, filter results.
Optional<Program> program = programService.getById(programId);
return program.map(p -> {
List<UUID> experimentIds = experimentalCollaboratorService.getAuthorizedExperimentIds(experimentalCollaborator.getId());
List<Study> studies = studyService.getStudiesByExperimentIds(p, experimentIds)
.stream()
.peek(this::setDbIds)
.collect(Collectors.toList());
return HttpResponse.ok(studies);
});
})
.orElseGet(() -> { //Otherwise, fallback to fetching studies without considering collaborator status
List<Study> studies = studyService.getStudies(programId)
.stream()
.peek(this::setDbIds)
.collect(Collectors.toList());
return HttpResponse.ok(studies);
});
return httpResponse.orElse(HttpResponse.notFound());

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to test this and the BrAPITrialsController suggestions locally, they both resulted in build errors. Are there problems in my code as written that you would like me to address?

Comment on lines +101 to +113
Optional<ProgramUser> experimentalCollaborator = programUserService.getIfExperimentalCollaborator(programId, securityService.getUser().getId());
// If the program user is an experimental collaborator, filter results.
if (experimentalCollaborator.isPresent()) {
Optional<Program> program = programService.getById(programId);
if (program.isEmpty()) {
return HttpResponse.notFound();
}
List<UUID> experimentIds = experimentalCollaboratorService.getAuthorizedExperimentIds(experimentalCollaborator.get().getId());
experiments = experimentService.getTrialsByExperimentIds(program.get(), experimentIds).stream().peek(this::setDbIds).collect(Collectors.toList());
} else {
experiments = experimentService.getExperiments(programId).stream().peek(this::setDbIds).collect(Collectors.toList());
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Optional<ProgramUser> experimentalCollaborator = programUserService.getIfExperimentalCollaborator(programId, securityService.getUser().getId());
// If the program user is an experimental collaborator, filter results.
if (experimentalCollaborator.isPresent()) {
Optional<Program> program = programService.getById(programId);
if (program.isEmpty()) {
return HttpResponse.notFound();
}
List<UUID> experimentIds = experimentalCollaboratorService.getAuthorizedExperimentIds(experimentalCollaborator.get().getId());
experiments = experimentService.getTrialsByExperimentIds(program.get(), experimentIds).stream().peek(this::setDbIds).collect(Collectors.toList());
} else {
experiments = experimentService.getExperiments(programId).stream().peek(this::setDbIds).collect(Collectors.toList());
}
Optional<HttpResponse> httpResponse = programUserService.getIfExperimentalCollaborator(programId, securityService.getUser().getId())
.flatMap(experimentalCollaborator ->
programService.getById(programId)
.map(program ->
experimentalCollaboratorService.getAuthorizedExperimentIds(experimentalCollaborator.getId())
.stream()
.flatMap(experimentId -> experimentService.getTrialsByExperimentIds(program, List.of(experimentId)).stream())
.peek(this::setDbIds)
.collect(Collectors.toList())))
.or(() ->
Optional.of(experimentService.getExperiments(programId)
.stream()
.peek(this::setDbIds)
.collect(Collectors.toList()))
.filter(experiments -> !experiments.isEmpty())
.map(HttpResponse::ok)
.orElse(HttpResponse.notFound());
return httpResponse.orElse(HttpResponse.notFound());

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment on the BrAPIStudiesController.java suggestion.

Comment on lines +284 to +294
public Optional<ProgramUser> getIfExperimentalCollaborator(UUID programId, UUID userId) throws DoesNotExistException {
Optional<ProgramUser> programUser = getProgramUserbyId(programId, userId);
if (programUser.isEmpty()) {
throw new DoesNotExistException("Program User does not exist");
}
boolean isExperimentalCollaborator = programUser.get().getRoles().stream().anyMatch(x -> ProgramSecuredRole.getEnum(x.getDomain()).equals(ProgramSecuredRole.EXPERIMENTAL_COLLABORATOR));
if (isExperimentalCollaborator) {
return programUser;
}
return Optional.empty();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could refactor here using Optional methods

Suggested change
public Optional<ProgramUser> getIfExperimentalCollaborator(UUID programId, UUID userId) throws DoesNotExistException {
Optional<ProgramUser> programUser = getProgramUserbyId(programId, userId);
if (programUser.isEmpty()) {
throw new DoesNotExistException("Program User does not exist");
}
boolean isExperimentalCollaborator = programUser.get().getRoles().stream().anyMatch(x -> ProgramSecuredRole.getEnum(x.getDomain()).equals(ProgramSecuredRole.EXPERIMENTAL_COLLABORATOR));
if (isExperimentalCollaborator) {
return programUser;
}
return Optional.empty();
}
public Optional<ProgramUser> getIfExperimentalCollaborator(UUID programId, UUID userId) throws DoesNotExistException {
return getProgramUserbyId(programId, userId)
.filter(programUser -> programUser.getRoles().stream()
.anyMatch(x -> ProgramSecuredRole.getEnum(x.getDomain()).equals(ProgramSecuredRole.EXPERIMENTAL_COLLABORATOR)))
.or(() -> {
throw new RuntimeException(new DoesNotExistException("Program User does not exist"));
});
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the suggestion. I tested it locally and it caused tests to fail because returning Optional.empty() is an important case which conceptually indicates that the program user exists, but is not in the "Experimental Collaborator" role.

@mlm483 mlm483 requested a review from dmeidlin August 27, 2024 18:46
@mlm483 mlm483 merged commit a910b89 into develop Aug 29, 2024
@mlm483 mlm483 deleted the feature/BI-2258 branch August 29, 2024 19:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants