Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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.*;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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<Object> searchGermplasm(
@PathVariable("programId") UUID programId,
@Body BrAPIGermplasmSearchRequest body) throws ApiException {
@Body BrAPIGermplasmSearchRequest body) throws ApiException, DoesNotExistException {

log.debug("searchGermplasm: fetching germplasm by filters");

Expand All @@ -105,19 +114,84 @@ public HttpResponse<Object> searchGermplasm(
String extRefId = program.get().getId().toString();
body.externalReferenceIds(List.of(extRefId));

// convert request filter dbIds from DeltaBreed UUID to BrAPI service dbIds
List<String> convertedDbIds = germplasmService.getGermplasmDbIdsForUUIDs(program.get().getId(), body.getGermplasmDbIds());
body.setGermplasmDbIds(convertedDbIds);

ApiResponse<Pair<Optional<BrAPIGermplasmListResponse>, Optional<BrAPIAcceptedSearchResponse>>> 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<Object> 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> program = programService.getById(programId);
if(program.isEmpty()) {
log.warn("Program id: " + programId + " not found");
return HttpResponse.notFound();
}

ApiResponse<Pair<Optional<BrAPIGermplasmListResponse>, Optional<BrAPIAcceptedSearchResponse>>> 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<Object> getObjectHttpResponse(ApiResponse<Pair<Optional<BrAPIGermplasmListResponse>, Optional<BrAPIAcceptedSearchResponse>>> 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<BrAPIGermplasm> 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<BrAPIGermplasm> 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*}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,22 @@ public BrAPIGermplasm getGermplasmByUUID(String germplasmId, UUID programId) thr
return germplasm;
}

public List<String> getGermplasmDbIdsForUUIDs(List<String> germplasmUUIDs, UUID programId) throws ApiException, DoesNotExistException {
Map<String, BrAPIGermplasm> cache = programGermplasmCache.get(programId);
List<String> 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<BrAPIGermplasm> getGermplasmByDBID(String germplasmDbId, UUID programId) throws ApiException {
Map<String, BrAPIGermplasm> cache = programGermplasmCache.get(programId);
//key is UUID, want to filter by DBID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ public BrAPIGermplasm getGermplasmByUUID(UUID programId, String germplasmId) thr
}
}

public List<String> getGermplasmDbIdsForUUIDs(UUID programId, List<String> germplasmUUIDs) throws DoesNotExistException {
try {
return germplasmDAO.getGermplasmDbIdsForUUIDs(germplasmUUIDs, programId);
} catch (ApiException e) {
throw new InternalServerException(e.getMessage(), e);
}
}

public Optional<BrAPIGermplasm> getGermplasmByDBID(UUID programId, String germplasmId) throws ApiException {
return germplasmDAO.getGermplasmByDBID(germplasmId, programId);
}
Expand Down
20 changes: 19 additions & 1 deletion src/main/java/org/breedinginsight/utilities/Utilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/version.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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