diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java index f78664f72..948e68c20 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java @@ -1,5 +1,6 @@ package org.breedinginsight.brapi.v2; +import io.micronaut.context.annotation.Property; import io.micronaut.http.HttpHeaders; import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; @@ -18,6 +19,7 @@ import org.brapi.v2.model.BrAPIIndexPagination; import org.brapi.v2.model.BrAPIMetadata; import org.brapi.v2.model.BrAPIStatus; +import org.brapi.v2.model.core.BrAPITrial; import org.brapi.v2.model.germ.*; import org.brapi.v2.model.germ.request.BrAPIGermplasmSearchRequest; import org.brapi.v2.model.germ.response.*; @@ -31,6 +33,7 @@ import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; import org.breedinginsight.brapi.v2.model.request.query.GermplasmQuery; +import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.model.Program; import org.breedinginsight.services.ProgramService; import org.breedinginsight.utilities.Utilities; @@ -49,11 +52,14 @@ import javax.inject.Inject; import javax.validation.Valid; import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; @Slf4j @Controller("/${micronaut.bi.api.version}") @Secured(SecurityRule.IS_AUTHENTICATED) public class BrAPIGermplasmController { + private final String referenceSource; private final BrAPIGermplasmService germplasmService; private final GermplasmQueryMapper germplasmQueryMapper; @@ -67,13 +73,15 @@ public class BrAPIGermplasmController { @Inject - public BrAPIGermplasmController(BrAPIGermplasmService germplasmService, + public BrAPIGermplasmController(@Property(name = "brapi.server.reference-source") String referenceSource, + BrAPIGermplasmService germplasmService, GermplasmQueryMapper germplasmQueryMapper, ProgramDAO programDAO, BrAPIGermplasmDAO germplasmDAO, GenotypeService genoService, BrAPIEndpointProvider brAPIEndpointProvider, ProgramService programService) { + this.referenceSource = referenceSource; this.germplasmService = germplasmService; this.germplasmQueryMapper = germplasmQueryMapper; this.programDAO = programDAO; @@ -84,12 +92,13 @@ public BrAPIGermplasmController(BrAPIGermplasmService germplasmService, } // NOTE: bypasses cache and makes api request directly to brapi service + // Needs to convert DeltaBreed UUIDs to BrAPI Service DbIds and back @Post("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/search/germplasm") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse searchGermplasm( @PathVariable("programId") UUID programId, - @Body BrAPIGermplasmSearchRequest body) throws ApiException { + @Body BrAPIGermplasmSearchRequest body) throws ApiException, DoesNotExistException { log.debug("searchGermplasm: fetching germplasm by filters"); @@ -105,19 +114,84 @@ public HttpResponse searchGermplasm( String extRefId = program.get().getId().toString(); body.externalReferenceIds(List.of(extRefId)); + // convert request filter dbIds from DeltaBreed UUID to BrAPI service dbIds + List convertedDbIds = germplasmService.getGermplasmDbIdsForUUIDs(program.get().getId(), body.getGermplasmDbIds()); + body.setGermplasmDbIds(convertedDbIds); + ApiResponse, Optional>> brapiGermplasm; brapiGermplasm = brAPIEndpointProvider .get(programDAO.getCoreClient(program.get().getId()), GermplasmApi.class) .searchGermplasmPost(body); + return getObjectHttpResponse(brapiGermplasm, program.get().getKey()); + } + + // NOTE: bypasses cache and makes api request directly to brapi service + // Needs to convert BrAPIService dbIds to DeltaBreed UUIDs + @Get("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/search/germplasm/{searchResultId}{?queryParams*}") + @Produces(MediaType.APPLICATION_JSON) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) + public HttpResponse getSearchResult( + @PathVariable("programId") UUID programId, + @PathVariable("searchResultId") UUID searchResultId, + @QueryValue @QueryValid(using = GermplasmQueryMapper.class) @Valid GermplasmQuery queryParams) throws ApiException { + + log.debug("getGermplasmResult: getting results"); + + Optional program = programService.getById(programId); + if(program.isEmpty()) { + log.warn("Program id: " + programId + " not found"); + return HttpResponse.notFound(); + } + + ApiResponse, Optional>> brapiGermplasm; + brapiGermplasm = brAPIEndpointProvider + .get(programDAO.getCoreClient(program.get().getId()), GermplasmApi.class) + .searchGermplasmSearchResultsDbIdGet(searchResultId.toString(), queryParams.getPage(), queryParams.getPageSize()); + + return getObjectHttpResponse(brapiGermplasm, program.get().getKey()); + } + + private HttpResponse getObjectHttpResponse(ApiResponse, Optional>> brapiGermplasm, + String programKey) throws ApiException { if (brapiGermplasm.getBody().getLeft().isPresent()) { - return HttpResponse.ok(brapiGermplasm.getBody().getLeft().get()); + // convert dbIds to DeltaBreed UUID + BrAPIGermplasmListResponse response = brapiGermplasm.getBody().getLeft().get(); + List germplasm = response.getResult().getData(); + //germplasm.forEach(g -> setDbIdsAndStripProgramKeys(g, programKey)); + batchProcessGermplasm(germplasm, programKey); + return HttpResponse.ok(response); } else if (brapiGermplasm.getBody().getRight().isPresent()) { return HttpResponse.ok(brapiGermplasm.getBody().getRight().get()); } else { throw new ApiException("Expected search response"); } + } + /** + * Keep dbIds in DeltaBreed UUID context and strip program keys from synonyms and pedigree string for Field Book display + * @param germplasmList + * @param programKey + */ + private void batchProcessGermplasm(List germplasmList, String programKey) throws IllegalStateException { + // Prepare a regex pattern for program key removal + Pattern programKeyPattern = Utilities.getRegexPatternMatchAllProgramKeysAnyAccession(programKey); + germplasmList.parallelStream().forEach(germplasm -> { + // Set dbId + germplasm.germplasmDbId(Utilities.getExternalReference(germplasm.getExternalReferences(), "breedinginsight.org") + .orElseThrow(() -> new IllegalStateException("No BI external reference found")) + .getReferenceId()); + // Process synonyms + if (germplasm.getSynonyms() != null) { + germplasm.getSynonyms().forEach(synonym -> { + synonym.setSynonym(Utilities.removeProgramKey(synonym.getSynonym(), programKey, germplasm.getAccessionNumber())); + }); + } + // Process pedigree + if (germplasm.getPedigree() != null) { + germplasm.setPedigree(programKeyPattern.matcher(germplasm.getPedigree()).replaceAll("")); + } + }); } @Get("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/germplasm{?queryParams*}") diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java index c4dd54233..2e9371be4 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java @@ -345,6 +345,22 @@ public BrAPIGermplasm getGermplasmByUUID(String germplasmId, UUID programId) thr return germplasm; } + public List getGermplasmDbIdsForUUIDs(List germplasmUUIDs, UUID programId) throws ApiException, DoesNotExistException { + Map cache = programGermplasmCache.get(programId); + List germplasmList = new ArrayList<>(); + if (cache != null) { + // not using streams because want to throw checked exception + for (String germplasmUUID : germplasmUUIDs) { + BrAPIGermplasm germplasm = cache.get(germplasmUUID); + if (germplasm == null) { + throw new DoesNotExistException("UUID for this germplasm does not exist: " + germplasmUUID); + } + germplasmList.add(germplasm.getGermplasmDbId()); + } + } + return germplasmList; + } + public Optional getGermplasmByDBID(String germplasmDbId, UUID programId) throws ApiException { Map cache = programGermplasmCache.get(programId); //key is UUID, want to filter by DBID diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java index 468a6b5b3..eafde7e1e 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java @@ -68,6 +68,14 @@ public BrAPIGermplasm getGermplasmByUUID(UUID programId, String germplasmId) thr } } + public List getGermplasmDbIdsForUUIDs(UUID programId, List germplasmUUIDs) throws DoesNotExistException { + try { + return germplasmDAO.getGermplasmDbIdsForUUIDs(germplasmUUIDs, programId); + } catch (ApiException e) { + throw new InternalServerException(e.getMessage(), e); + } + } + public Optional getGermplasmByDBID(UUID programId, String germplasmId) throws ApiException { return germplasmDAO.getGermplasmByDBID(germplasmId, programId); } diff --git a/src/main/java/org/breedinginsight/utilities/Utilities.java b/src/main/java/org/breedinginsight/utilities/Utilities.java index 3d9d6d04c..92b46623e 100644 --- a/src/main/java/org/breedinginsight/utilities/Utilities.java +++ b/src/main/java/org/breedinginsight/utilities/Utilities.java @@ -32,6 +32,7 @@ import java.util.Optional; import java.util.UUID; import java.util.function.Function; +import java.util.regex.Pattern; public class Utilities { @@ -97,6 +98,10 @@ public static String removeProgramKeyAnyAccession(String str, String programKey) return str.replaceAll("\\[" + programKey + "-.*\\]", "").trim(); } + public static Pattern getRegexPatternMatchAllProgramKeysAnyAccession(String programKey) { + return Pattern.compile(String.format("\\s*\\[%s-.*?\\]\\s*", programKey)); + } + /** * Remove program key from a string. Returns a new value instead of altering original string. * @@ -162,8 +167,21 @@ public static Object formatBrapiObjForDisplay(Object brapiInstance, Class brapiC return brapiInstance; } + /** + * \s*: Matches zero or more whitespace characters before the opening bracket. + * \[: Matches the opening square bracket [. The backslash is used to escape the special meaning of [ in regex. + * .*?: Matches any character (except newline) zero or more times, non-greedily. + * . matches any character except newline. + * * means "zero or more times". + * ? makes the matching non-greedy, so it stops at the first closing bracket. + * \]: Matches the closing square bracket ]. Again, the backslash is used to escape it. + * \s*: Matches zero or more whitespace characters after the closing bracket. + * @param original + * @param programKey + * @return + */ public static String removeProgramKeyAndUnknownAdditionalData(String original, String programKey) { - String keyValueRegEx = String.format(" \\[%s\\-.*\\]", programKey); + String keyValueRegEx = String.format("\\s*\\[%s-.*?\\]\\s*", programKey); String stripped = original.replaceAll(keyValueRegEx, ""); return stripped; } diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index a4d6b9e84..e9c549d94 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,4 +15,4 @@ # version=v0.10.0+823 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/68609b93fef11c2c0fa5434d979cc93d1f8920ec +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/68609b93fef11c2c0fa5434d979cc93d1f8920ec \ No newline at end of file