Skip to content
Open
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
Expand Up @@ -26,6 +26,7 @@

import javax.validation.constraints.Positive;
import javax.validation.constraints.PositiveOrZero;
import java.util.Map;

@Getter
@Setter
Expand Down Expand Up @@ -58,4 +59,26 @@ protected FilterRequest constructFilterRequest(String field, String value) {
.value(value)
.build();
}

/**
* For BrapiQuery objects that have implemented this method, this map is used by the BrAPIDAOUtil
* to iterate through all valid filter columns and their associated values that are submitted from the fe.
*
* See @ExperimentQuery for example implementation
*/
public Map<String, String> getFilterValuesByBrAPIColumnName() {
throw new UnsupportedOperationException(
String.format("Server side BrAPI filtering not implemented for class [%s]", this.getClass().getSimpleName())
);
}

/**
* For BrapiQuery objects that have implemented this method, this map is used by the BrAPIDAOUtil
* to match submitted sortField column names to the associated brapi column name for sorting.
*
* See @ExperimentQuery for example implementation
*/
public Map<String, String> getBrAPIColumnNamesByBiColumnName() {
throw new UnsupportedOperationException(String.format("Server side BrAPI sorting not implemented for class [%s]", this.getClass().getSimpleName()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ public HttpResponse<Response<DataResponse<BrAPITrial>>> getExperiments(
Optional<Program> program = programService.getById(programId);
if (program.isEmpty()) { return HttpResponse.notFound(); }

SearchRequest searchRequest = queryParams.constructSearchRequest();
log.debug("fetching trials for program: " + programId);

// If the program user is an experimental collaborator, filter results for only authorized experiments.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import org.brapi.client.v2.model.queryParams.core.TrialQueryParams;
import org.brapi.client.v2.modules.core.TrialsApi;
import org.brapi.v2.model.BrAPIFilterBy;
import org.brapi.v2.model.BrAPIResponse;
import org.brapi.v2.model.BrAPISortBy;
import org.brapi.v2.model.core.BrAPIProgram;
import org.brapi.v2.model.core.BrAPITrial;
Expand Down Expand Up @@ -83,7 +82,6 @@ public BrAPITrialDAOImpl(ProgramDAO programDAO,
* This method requires a BI-API program. If the BrAPIProgram inside this data model is not set,
* this method will retrieve it.
*/
// TODO: Generalize Code for General Get By Program for BrAPI Entities [BI-2932]
private List<BrAPITrial> getBrAPITrialsUsingBrAPIProgramId(Program program) throws ApiException {

if (program == null || program.getId() == null) {
Expand All @@ -99,37 +97,16 @@ private List<BrAPITrial> getBrAPITrialsUsingBrAPIProgramId(Program program) thro
brapiProgramDbId = programDAO.getProgramBrAPI(program).getProgramDbId();
}

// TODO: Configurable max amount of trials per program, or paginate [BI-2932]

TrialQueryParams trialQueryParams =
TrialQueryParams.builder()
.programDbId(brapiProgramDbId)
.pageSize(1000)
.page(0)
.build();

return getTrialsFromBrAPI(program, trialQueryParams);
}

private List<BrAPITrial> getTrialsFromBrAPI(Program program, TrialQueryParams trialQueryParams) throws ApiException {
TrialsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(program.getId()), TrialsApi.class);

ApiResponse<BrAPITrialListResponse> response;

try {
response = api.trialsGet(trialQueryParams);
} catch (ApiException e) {
log.warn(Utilities.generateApiExceptionLogMessage(e));
throw new InternalServerException("Error making BrAPI call", e);
}

if (response.getBody().getMetadata().getPagination().getTotalCount() > trialQueryParams.pageSize()) {
throw new InternalServerException(String.format("More trials exist than requested [%s]", trialQueryParams));
}

List<BrAPITrial> trialsFromResponse = response.getBody().getResult().getData();
List<BrAPITrial> result = brAPIDAOUtil.get(api::trialsGet, trialQueryParams);

return processExperimentsForDisplay(trialsFromResponse, program.getKey());
return processExperimentsForDisplay(result, program.getKey());
}

@Override
Expand Down Expand Up @@ -193,12 +170,21 @@ public Optional<BrAPITrial> getTrialById(UUID programId, UUID trialId) throws Ap

TrialQueryParams params = TrialQueryParams.builder()
.trialDbId(trialId.toString())
.pageSize(1)
.page(0)
.build();

TrialsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), TrialsApi.class);

List<BrAPITrial> result = brAPIDAOUtil.get(api::trialsGet, params);

if (result.isEmpty()) {
throw new DoesNotExistException(String.format("Trial with ID [%s] does not exist", trialId));
} else if (result.size() > 1) {
throw new InternalServerException(String.format("Trial with ID [%s] found multiple times in database. This should not be possible.", trialId));
}

processExperimentsForDisplay(result, program.getKey());

return getTrialsFromBrAPI(program, params).stream().findFirst();
return result.stream().findFirst();
}

@Override
Expand Down Expand Up @@ -284,40 +270,8 @@ private BrAPITrialSearchRequest buildSearchRequest(Program program, List<UUID> b
searchRequest.setTrialDbIds(brapiTrialIds.stream().map(UUID::toString).collect(Collectors.toList()));
}

// Set FilterBy
List<BrAPIFilterBy> brAPIFilterBy = new ArrayList<>();

if (StringUtils.isNotBlank(experimentQuery.getName())) {
brAPIFilterBy.add(brAPIDAOUtil.constructFilterBy(brAPITrialsMapper.getBrAPIName(BI_FILTER_COLUMN_NAME), experimentQuery.getName()));
}
if (StringUtils.isNotBlank(experimentQuery.getActive())) {
brAPIFilterBy.add(brAPIDAOUtil.constructFilterBy(brAPITrialsMapper.getBrAPIName(BI_FILTER_COLUMN_ACTIVE), experimentQuery.getActive()));
}
if (StringUtils.isNotBlank(experimentQuery.getCreatedBy())) {
brAPIFilterBy.add(brAPIDAOUtil.constructFilterBy(brAPITrialsMapper.getBrAPIName(BI_FILTER_COLUMN_CREATED_BY), experimentQuery.getCreatedBy()));
}
if (StringUtils.isNotBlank(experimentQuery.getCreatedDate())) {
brAPIFilterBy.add(brAPIDAOUtil.constructFilterBy(brAPITrialsMapper.getBrAPIName(BI_FILTER_COLUMN_CREATED_DATE), experimentQuery.getCreatedDate()));
}

if (!brAPIFilterBy.isEmpty()) {
searchRequest.setFilterBy(brAPIFilterBy);
}

// Set SortBy
List<BrAPISortBy> brAPISortBy = new ArrayList<>();

if (StringUtils.isNotBlank(experimentQuery.getSortField()) && brAPITrialsMapper.exists(experimentQuery.getSortField())) {
brAPISortBy.add(brAPIDAOUtil.constructSortBy(brAPITrialsMapper.getBrAPIName(experimentQuery.getSortField()), experimentQuery.getSortOrder().toString()));
}

if (!brAPISortBy.isEmpty()) {
searchRequest.setSortBy(brAPISortBy);
}

brAPIDAOUtil.setPagination(searchRequest, experimentQuery);
brAPIDAOUtil.setGenericSearchParameters(searchRequest, experimentQuery);

return searchRequest;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@

import io.micronaut.core.annotation.Introspected;
import lombok.Getter;
import org.breedinginsight.api.model.v1.request.query.FilterRequest;
import org.breedinginsight.api.model.v1.request.query.SearchRequest;
import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery;
import org.jooq.tools.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;

@Getter
@Introspected
Expand All @@ -18,20 +15,27 @@ public class ExperimentQuery extends BrapiQuery {
private String createdBy;
private String createdDate;

public SearchRequest constructSearchRequest() {
List<FilterRequest> filters = new ArrayList<>();
if (!StringUtils.isBlank(getName())) {
filters.add(constructFilterRequest("name", getName()));
}
if (!StringUtils.isBlank(getActive())) {
filters.add(constructFilterRequest("active", getActive()));
}
if (!StringUtils.isBlank(getCreatedBy())) {
filters.add(constructFilterRequest("createdBy", getCreatedBy()));
}
if (!StringUtils.isBlank(getCreatedDate())) {
filters.add(constructFilterRequest("createdDate", getCreatedDate()));
}
return new SearchRequest(filters);
@Override
public Map<String, String> getFilterValuesByBrAPIColumnName() {
Map<String, String> filterValuesByBrAPIColumnName = new HashMap<>();

filterValuesByBrAPIColumnName.put("trialName", getName());
filterValuesByBrAPIColumnName.put("active", getActive());
filterValuesByBrAPIColumnName.put("createdBy", getCreatedBy());
filterValuesByBrAPIColumnName.put("createdDate", getCreatedDate());

return filterValuesByBrAPIColumnName;
}

@Override
public Map<String, String> getBrAPIColumnNamesByBiColumnName() {
Map<String, String> brAPIColumnNamesByBiColumnName = new HashMap<>();

brAPIColumnNamesByBiColumnName.put("name", "trialName");
brAPIColumnNamesByBiColumnName.put("active", "active");
brAPIColumnNamesByBiColumnName.put("createdBy", "createdBy");
brAPIColumnNamesByBiColumnName.put("createdDate", "createdDate");

return brAPIColumnNamesByBiColumnName;
}
}
100 changes: 88 additions & 12 deletions src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
import org.apache.commons.lang3.tuple.Pair;
import org.brapi.client.v2.ApiResponse;
import org.brapi.client.v2.model.exceptions.ApiException;
import org.brapi.client.v2.model.queryParams.core.BrAPIQueryParams;
import org.brapi.v2.model.*;
import org.breedinginsight.api.model.v1.response.DataResponse;
import org.breedinginsight.brapi.v1.controller.BrapiVersion;
import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery;
import org.breedinginsight.brapps.importer.model.ImportUpload;
Expand Down Expand Up @@ -76,6 +76,55 @@ public BrAPIDAOUtil(@Property(name = "brapi.search.wait-time") int searchWaitTim
this.programService = programService;
}

public <T extends BrAPIResponse, U extends BrAPIQueryParams, V> List<V> get(Function<U, ApiResponse<T>> getMethod,
U queryParams) throws ApiException {
T brapiResponseResult;

boolean paginationSet = queryParams.page() != null && queryParams.pageSize() != null;

if (!paginationSet) {
queryParams.setPage(0);
queryParams.setPageSize(pageSize);
}

try {
brapiResponseResult = Optional.ofNullable(getMethod.apply(queryParams))
.map(ApiResponse::getBody)
.orElseThrow();
} catch (ApiException e) {
log.warn(Utilities.generateApiExceptionLogMessage(e));
throw e;
} catch (NoSuchElementException e) {
log.debug("Unable to retrieve BrAPIResponse from POST search request", e);
throw new InternalServerException(e.toString(), e);
} catch (Exception e) {
log.debug("error", e);
throw new InternalServerException(e.toString(), e);
}

BrAPIPagination responsePagination = Optional.of(brapiResponseResult)
.map(BrAPIResponse::getMetadata)
.map(BrAPIMetadata::getPagination)
.orElse(null);

if (responsePagination == null) {
throw new InternalServerException("Expected pagination metadata in BrAPI get response not present");
}

if (!paginationSet && responsePagination.getTotalPages() > 1) {
// Size of BrAPI entity GET data can not exceed configured pageSize.
// If it does, it is recommended the GET method not be used for this purpose.
// If you are trying to utilize this method to get a large amount of data for an entity, you can use
// searchInternal for now. In the future, we need a more robust way of transmitting this data using
// compression client techniques to reduce strain on the server and avoid keeping all the database data
// in java memory.
throw new InternalServerException(String.format("More than one page found, meaning there is more data to be returned " +
"with pageSize = [%s]", queryParams.pageSize()));
}

return getListResult(brapiResponseResult);
}

// Performs a POST brapi search on an entity without looping paging logic, utilizing filters and pagination provided in the searchBody.
// Also verifies response paging matches requested paging
public <T extends BrAPIResponse, U extends BrAPISearchRequestParametersPaging, V> T simpleSearch(Function<U, ApiResponse<Pair<Optional<T>, Optional<BrAPIAcceptedSearchResponse>>>> searchMethod,
Expand Down Expand Up @@ -352,14 +401,6 @@ public <T extends BrAPIResponse, V> List<V> getListResult(T response) {
.orElseThrow();
}

// TODO: write generic put code
public <T> List<T> put(List<T> brapiObjects,
ImportUpload upload,
Function<List<T>, ApiResponse> putMethod,
Consumer<ImportUpload> progressUpdateMethod) throws ApiException {
throw new UnsupportedOperationException();
}

public <T, R> List<R> post(List<T> brapiObjects,
ImportUpload upload,
Function<List<T>, ApiResponse> postMethod,
Expand Down Expand Up @@ -506,15 +547,15 @@ public String getProgramBrAPIBaseUrl(UUID programId) {
return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2;
}

public BrAPIFilterBy constructFilterBy(String filterOn, String value) {
private BrAPIFilterBy constructFilterBy(String filterOn, String value) {
var filterBy = new BrAPIFilterBy();

filterBy.setFilterOn(filterOn);
filterBy.setValue(value);
return filterBy;
}

public BrAPISortBy constructSortBy(String sortOn, String sortOrder) {
private BrAPISortBy constructSortBy(String sortOn, String sortOrder) {
var sortBy = new BrAPISortBy();
sortBy.setSortedOn(sortOn);

Expand All @@ -529,7 +570,42 @@ public BrAPISortBy constructSortBy(String sortOn, String sortOrder) {
return sortBy;
}

public <T extends BrAPISearchRequestParametersPaging, U extends BrapiQuery> void setPagination(T brapiSearchRequest, U biSearchQuery) {
/**
* Sets three generic search parameters for an outgoing BrAPI Search Query:
* - Sorting
* - Filtering
* - Pagination
*/
public <T extends BrAPISearchRequestParametersPaging, U extends BrapiQuery> void setGenericSearchParameters(T brapiSearchRequest, U biSearchQuery) {
// Set SortBy
List<BrAPISortBy> brAPISortBy = new ArrayList<>();

Map<String, String> brapiColNameByBiColName = biSearchQuery.getBrAPIColumnNamesByBiColumnName();

if (StringUtils.isNotBlank(biSearchQuery.getSortField()) && brapiColNameByBiColName.containsKey(biSearchQuery.getSortField())) {
brAPISortBy.add(constructSortBy(brapiColNameByBiColName.get(biSearchQuery.getSortField()), biSearchQuery.getSortOrder().toString()));
}

if (!brAPISortBy.isEmpty()) {
brapiSearchRequest.setSortBy(brAPISortBy);
}

// Set FilterBy
List<BrAPIFilterBy> brAPIFilterBy = new ArrayList<>();

Map<String, String> filterValuesByBrAPIColName = biSearchQuery.getFilterValuesByBrAPIColumnName();

filterValuesByBrAPIColName.forEach((brapiColumnName, value) -> {
if (StringUtils.isNotBlank(value)) {
brAPIFilterBy.add(constructFilterBy(brapiColumnName, value));
}
});

if (!brAPIFilterBy.isEmpty()) {
brapiSearchRequest.setFilterBy(brAPIFilterBy);
}

// Set Pagination
if (biSearchQuery.getPage() == null) {
brapiSearchRequest.setPage(biSearchQuery.getDefaultPage());
} else {
Expand Down
Loading