From 98ff599a29ef26e397e532602c0940f2023f8629 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Fri, 15 Nov 2024 12:33:02 -0500 Subject: [PATCH 1/9] add delete endpoint to list controller --- .../brapi/v2/BrAPIGermplasmController.java | 37 ---- .../brapi/v2/BrAPIListController.java | 183 +++++++++++++++--- .../org/breedinginsight/daos/ListDAO.java | 102 ++++++++++ .../model/delta/DeltaEntity.java | 3 +- .../model/delta/DeltaEntityFactory.java | 55 +++++- .../delta/DeltaGermplasmListDetails.java | 62 ++++++ .../delta/DeltaGermplasmListSummary.java | 16 ++ .../model/delta/DeltaListDetails.java | 78 ++++++++ .../model/delta/DeltaListSummary.java | 20 ++ .../breedinginsight/services/ListService.java | 33 ++++ .../breedinginsight/utilities/Utilities.java | 2 + 11 files changed, 521 insertions(+), 70 deletions(-) create mode 100644 src/main/java/org/breedinginsight/daos/ListDAO.java create mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java create mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java create mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java create mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java create mode 100644 src/main/java/org/breedinginsight/services/ListService.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java index 948e68c20..4d67e2a00 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java @@ -221,43 +221,6 @@ public HttpResponse>>> getGermplasm( } } - @Get("/programs/{programId}/germplasm/lists/{listDbId}/records{?queryParams*}") - @Produces(MediaType.APPLICATION_JSON) - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - public HttpResponse>>> getGermplasmListRecords( - @PathVariable("programId") UUID programId, - @PathVariable("listDbId") String listDbId, - @QueryValue @QueryValid(using = GermplasmQueryMapper.class) @Valid GermplasmQuery queryParams) { - try { - List germplasm = germplasmService.getGermplasmByList(programId, listDbId); - SearchRequest searchRequest = queryParams.constructSearchRequest(); - return ResponseUtils.getBrapiQueryResponse(germplasm, germplasmQueryMapper, queryParams, searchRequest); - } catch (Exception e) { - log.info(e.getMessage(), e); - return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving germplasm list records"); - } - } - - @Get("/programs/{programId}/germplasm/lists/{listDbId}/export{?fileExtension}") - @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - public HttpResponse germplasmListExport( - @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, @QueryValue(defaultValue = "XLSX") String fileExtension) { - String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; - try { - FileType extension = Enum.valueOf(FileType.class, fileExtension); - DownloadFile germplasmListFile = germplasmService.exportGermplasmList(programId, listDbId, extension); - HttpResponse germplasmListExport = HttpResponse.ok(germplasmListFile.getStreamedFile()).header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="+germplasmListFile.getFileName()+extension.getExtension()); - return germplasmListExport; - } - catch (Exception e) { - log.info(e.getMessage(), e); - e.printStackTrace(); - HttpResponse response = HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, downloadErrorMessage).contentType(MediaType.TEXT_PLAIN).body(downloadErrorMessage); - return response; - } - } - @Get("/programs/{programId}/germplasm/export{?fileExtension}") @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 3dd6fd1f4..0eebc602d 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -1,26 +1,10 @@ -/* - * See the NOTICE file distributed with this work for additional information - * regarding copyright ownership. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.breedinginsight.brapi.v2; -import io.micronaut.http.HttpResponse; -import io.micronaut.http.HttpStatus; -import io.micronaut.http.MediaType; +import io.micronaut.core.beans.BeanIntrospection; +import io.micronaut.core.beans.BeanProperty; +import io.micronaut.http.*; import io.micronaut.http.annotation.*; +import io.micronaut.http.server.types.files.StreamedFile; import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; @@ -29,43 +13,55 @@ import org.brapi.v2.model.core.BrAPIListTypes; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; -import org.breedinginsight.api.model.v1.request.query.SearchRequest; import org.breedinginsight.api.model.v1.response.DataResponse; import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.api.model.v1.validators.QueryValid; import org.breedinginsight.brapi.v1.controller.BrapiVersion; +import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; import org.breedinginsight.brapi.v2.model.request.query.ListQuery; import org.breedinginsight.brapi.v2.services.BrAPIListService; +import org.breedinginsight.brapps.importer.model.exports.FileType; +import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.Program; +import org.breedinginsight.model.delta.DeltaListDetails; +import org.breedinginsight.services.ListService; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.response.ResponseUtils; +import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; import org.breedinginsight.utilities.response.mappers.ListQueryMapper; +import org.breedinginsight.api.model.v1.request.query.SearchRequest; import javax.inject.Inject; +import javax.validation.ConstraintViolation; import javax.validation.Valid; +import javax.validation.Validator; import java.util.List; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; @Slf4j -@Controller +@Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @Secured(SecurityRule.IS_AUTHENTICATED) public class BrAPIListController { - private final ProgramService programService; - private final BrAPIListService listService; + private final BrAPIListService brapiListService; + private final ListService listService; private final ListQueryMapper listQueryMapper; + private final Validator validator; @Inject - public BrAPIListController(ProgramService programService, BrAPIListService listService, - ListQueryMapper listQueryMapper) { + public BrAPIListController(ProgramService programService, BrAPIListService brapiListService, ListService listService, + ListQueryMapper listQueryMapper, Validator validator) { this.programService = programService; + this.brapiListService = brapiListService; this.listService = listService; this.listQueryMapper = listQueryMapper; + this.validator = validator; } - //@Get(BrapiVersion.BRAPI_V2 + "/lists") - @Get("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/lists{?queryParams*}") + @Get("/deltalists{?queryParams*}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse>> getLists( @@ -77,7 +73,6 @@ public HttpResponse>> getLists( .getById(programId) .orElseThrow(() -> new DoesNotExistException("Program does not exist")); - // get germplasm lists by default BrAPIListTypes type = BrAPIListTypes.fromValue(queryParams.getListType()); String source = null; String id = null; @@ -93,7 +88,7 @@ public HttpResponse>> getLists( if (dateFormatParam != null) { listQueryMapper.setDateDisplayFormat(dateFormatParam); } - List brapiLists = listService.getListSummariesByTypeAndXref(type, source, id, program); + List brapiLists = brapiListService.getListSummariesByTypeAndXref(type, source, id, program); SearchRequest searchRequest = queryParams.constructSearchRequest(); return ResponseUtils.getBrapiQueryResponse(brapiLists, listQueryMapper, queryParams, searchRequest); @@ -105,4 +100,132 @@ public HttpResponse>> getLists( throw new RuntimeException(e); } } + + @Delete("/deltalists/{listDbId}") + @Produces(MediaType.APPLICATION_JSON) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) + public HttpResponse>> deleteListById( + @PathVariable("programId") UUID programId, + @PathVariable("listDbId") String listDbId, + HttpRequest request + ) throws DoesNotExistException, ApiException { + boolean hardDelete = false; + if (request.getParameters().contains("hardDelete")) { + String paramValue = request.getParameters().get("hardDelete"); + hardDelete = "true".equals(paramValue); + } + try { + listService.deleteBrAPIList(listDbId, programId, hardDelete); + return HttpResponse.status(HttpStatus.NO_CONTENT); + } catch (Exception e) { + log.info(e.getMessage(), e); + return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving germplasm list records"); + } + } + + @Get("/deltalists/{listDbId}") + @Produces(MediaType.APPLICATION_JSON) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) + @SuppressWarnings("unchecked") + public HttpResponse>>> getListById( + @PathVariable("programId") UUID programId, + @PathVariable("listDbId") String listDbId, + HttpRequest request) { + try { + // Get the list from the BrAPI service + DeltaListDetails details = listService.getDeltaListDetails(listDbId, programId); + + // Get a new instance of BrAPI query matching the type of list contents + T queryParams = (T) details.getQuery(); + + // Bind query parameters to the object + bindQueryParams(queryParams, request); + + // Perform standard bean validation + Set> violations = validator.validate(queryParams); + if (!violations.isEmpty()) { + List errorMessages = violations.stream() + .map(ConstraintViolation::getMessage) + .collect(Collectors.toList()); + log.info(String.join(", ", errorMessages)); + return HttpResponse.status(HttpStatus.BAD_REQUEST, "Error with list contents search parameters"); + } + + // Fetch the list contents from the BrAPI service + List listContentsBrAPIObjects = (List) details.getDataObjects(); + + // Construct a search request for sorting the list contents + SearchRequest searchRequest = details.constructSearchRequest(queryParams); + + // Get the map used to connect query sorting keys to contents object values + AbstractQueryMapper contentsQueryMapper = details.getQueryMapper(); + + return ResponseUtils.getBrapiQueryResponse(listContentsBrAPIObjects, contentsQueryMapper, queryParams, searchRequest); + } catch (Exception e) { + log.info(e.getMessage(), e); + return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving list records"); + } + } + + @Get("/deltalists/{listDbId}/export{?fileExtension}") + @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) + public HttpResponse germplasmListExport( + @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, @QueryValue(defaultValue = "XLSX") String fileExtension) { + String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; + try { + // Get the list from the BrAPI service + DeltaListDetails details = listService.getDeltaListDetails(listDbId, programId); + + FileType extension = Enum.valueOf(FileType.class, fileExtension); + DownloadFile listContentsFile = details.exportListObjects(extension); + return HttpResponse.ok(listContentsFile.getStreamedFile()).header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="+listContentsFile.getFileName()+extension.getExtension()); + } + catch (Exception e) { + log.info(e.getMessage(), e); + e.printStackTrace(); + HttpResponse response = HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, downloadErrorMessage).contentType(MediaType.TEXT_PLAIN).body(downloadErrorMessage); + return response; + } + } + + private void bindQueryParams(BrapiQuery queryParams, HttpRequest request) { + BeanIntrospection introspection = BeanIntrospection.getIntrospection(BrapiQuery.class); + for (BeanProperty property : introspection.getBeanProperties()) { + String paramName = property.getName(); + if (request.getParameters().contains(paramName)) { + String paramValue = request.getParameters().get(paramName); + Object convertedValue; + Class propertyType = property.getType(); + + if (propertyType.isEnum()) { + convertedValue = convertToEnum(paramValue, (Class>) propertyType); + } else { + convertedValue = convertValue(paramValue, propertyType); + } + + property.set(queryParams, convertedValue); + } + } + } + + private > T convertToEnum(String value, Class> enumClass) { + if (value == null) { + return null; + } + return Enum.valueOf((Class) enumClass, value.toUpperCase()); + } + + + // Convert, if necessary, the values of query parameters to match the type defined for the fields in the BrapiQuery class + private Object convertValue(String value, Class targetType) { + // Implement type conversion logic here + // Other list content types might need more complex logic + if (targetType == String.class) return value; + if (targetType == Integer.class) return Integer.parseInt(value); + if (targetType == Long.class) return Long.parseLong(value); + if (targetType == Boolean.class) return Boolean.parseBoolean(value); + // Add more type conversions as needed + return value; + } } diff --git a/src/main/java/org/breedinginsight/daos/ListDAO.java b/src/main/java/org/breedinginsight/daos/ListDAO.java new file mode 100644 index 000000000..f44a8757f --- /dev/null +++ b/src/main/java/org/breedinginsight/daos/ListDAO.java @@ -0,0 +1,102 @@ +package org.breedinginsight.daos; + +import io.micronaut.http.HttpStatus; +import io.micronaut.http.exceptions.HttpStatusException; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.brapi.client.v2.ApiResponse; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.client.v2.modules.core.ListsApi; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; +import org.breedinginsight.model.ProgramBrAPIEndpoints; +import org.breedinginsight.model.delta.DeltaEntityFactory; +import org.breedinginsight.model.delta.DeltaListDetails; +import org.breedinginsight.services.ProgramService; +import org.breedinginsight.services.brapi.BrAPIEndpointProvider; +import org.breedinginsight.services.exceptions.DoesNotExistException; +import org.breedinginsight.utilities.BrAPIDAOUtil; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.IOException; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Singleton +public class ListDAO { + private final ProgramDAO programDAO; + private final BrAPIDAOUtil brAPIDAOUtil; + private final BrAPIEndpointProvider brAPIEndpointProvider; + private final DeltaEntityFactory deltaEntityFactory; + private final ProgramService programService; + + @Inject + public ListDAO(ProgramDAO programDAO, + BrAPIDAOUtil brAPIDAOUtil, + BrAPIEndpointProvider brAPIEndpointProvider, + DeltaEntityFactory deltaEntityFactory, ProgramService programService) { + this.programDAO = programDAO; + this.brAPIDAOUtil = brAPIDAOUtil; + this.brAPIEndpointProvider = brAPIEndpointProvider; + this.deltaEntityFactory = deltaEntityFactory; + this.programService = programService; + } + + public DeltaListDetails getDeltaListDetailsByDbId(String listDbId, UUID programId) throws ApiException { + ListsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ListsApi.class); + ApiResponse response = api.listsListDbIdGet(listDbId); + if (Objects.isNull(response.getBody()) || Objects.isNull(response.getBody().getResult())) + { + throw new ApiException(); + } + + BrAPIListDetails details = response.getBody().getResult(); + return deltaEntityFactory.makeDeltaListDetailsBean(details); + } + + public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); + var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); + requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); + HttpUrl url = requestUrl.build(); + var brapiRequest = new Request.Builder().url(url) + .method("DELETE", null) + .addHeader("Content-Type", "application/json") + .build(); + + makeCall(brapiRequest); + } + + private void makeCall(Request brapiRequest) throws ApiException { + OkHttpClient client = new OkHttpClient.Builder() + .readTimeout(5, TimeUnit.MINUTES) + .build(); + try { + client.newCall(brapiRequest).execute(); + } catch (IOException e) { + log.error("Error calling BrAPI Service", e); + throw new ApiException("Error calling BrAPI Service"); + } + } + + private String getProgramBrAPIBaseUrl(UUID programId) { + ProgramBrAPIEndpoints programBrAPIEndpoints; + try { + programBrAPIEndpoints = programService.getBrapiEndpoints(programId); + } catch (DoesNotExistException e) { + throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); + } + + if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { + log.error("Program: " + programId + " is missing BrAPI URL config"); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); + } + var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); + programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; + return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; + } +} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaEntity.java b/src/main/java/org/breedinginsight/model/delta/DeltaEntity.java index 11646f9f8..4f5b8ead9 100644 --- a/src/main/java/org/breedinginsight/model/delta/DeltaEntity.java +++ b/src/main/java/org/breedinginsight/model/delta/DeltaEntity.java @@ -7,7 +7,7 @@ public abstract class DeltaEntity { - protected final Gson gson; + protected static final Gson gson = new GsonBuilder().registerTypeAdapterFactory(new GeometryAdapterFactory()).create(); @NonNull protected final T entity; @@ -15,7 +15,6 @@ public abstract class DeltaEntity { // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. protected DeltaEntity(@NonNull T entity) { this.entity = entity; - this.gson = new GsonBuilder().registerTypeAdapterFactory(new GeometryAdapterFactory()).create(); } protected T getEntity() { diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java index 3787c5d71..8d473ce5a 100644 --- a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java +++ b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java @@ -2,8 +2,14 @@ import io.micronaut.context.annotation.Bean; import io.micronaut.context.annotation.Factory; +import io.micronaut.context.annotation.Property; import io.micronaut.context.annotation.Prototype; import lombok.NonNull; +import org.brapi.v2.model.core.BrAPIListSummary; +import org.brapi.v2.model.core.BrAPIListTypes; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; +import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; import org.breedinginsight.brapps.importer.services.processors.experiment.service.ObservationUnitService; import org.brapi.v2.model.core.BrAPIStudy; @@ -13,6 +19,7 @@ import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.brapi.v2.model.pheno.BrAPIObservationVariable; import org.breedinginsight.model.ProgramLocation; +import org.breedinginsight.utilities.Utilities; import javax.inject.Inject; @@ -20,10 +27,16 @@ public class DeltaEntityFactory { private final ObservationUnitService observationUnitService; + private final BrAPIGermplasmService germplasmService; + private final String applicationReferenceSourceBase; @Inject - public DeltaEntityFactory(ObservationUnitService observationUnitService) { + public DeltaEntityFactory(ObservationUnitService observationUnitService, + BrAPIGermplasmService germplasmService, + @Property(name = "brapi.server.reference-source") String applicationReferenceSourceBase) { this.observationUnitService = observationUnitService; + this.germplasmService = germplasmService; + this.applicationReferenceSourceBase = applicationReferenceSourceBase; } private Experiment makeExperiment(BrAPITrial brAPIObject) { @@ -54,6 +67,46 @@ private DeltaObservationVariable makeDeltaObservationVariable(BrAPIObservationVa return new DeltaObservationVariable(brAPIObject); } + private DeltaGermplasmListSummary makeDeltaGermplasmListSummary(BrAPIListSummary brAPIObject) { + return new DeltaGermplasmListSummary(brAPIObject); + } + + private DeltaGermplasmListDetails makeDeltaGermplasmListDetails(BrAPIListDetails brAPIObject, String referenceSourceBase, BrAPIGermplasmService germplasmService) { + return new DeltaGermplasmListDetails(brAPIObject, referenceSourceBase, germplasmService); + } + + @Bean + @Prototype + public DeltaListSummary makeDeltaListSummaryBean(@NonNull BrAPIListSummary brAPIObject) { + BrAPIListTypes listType = brAPIObject.getListType(); + switch (listType) { + case GERMPLASM: return makeDeltaGermplasmListSummary(brAPIObject); + default: return null; + } + } + + @Bean + @Prototype + public DeltaListDetails makeDeltaListDetailsBean(@NonNull BrAPIListDetails brAPIObject) { + BrAPIListTypes listType = brAPIObject.getListType(); + switch (listType) { + case GERMPLASM: return makeDeltaGermplasmListDetailsBean(brAPIObject); + default: return null; + } + } + + @Bean + @Prototype + public DeltaGermplasmListSummary makeDeltaGermplasmListSummaryBean(@NonNull BrAPIListSummary brAPIObject) { + return makeDeltaGermplasmListSummary(brAPIObject); + } + + @Bean + @Prototype + public DeltaGermplasmListDetails makeDeltaGermplasmListDetailsBean(@NonNull BrAPIListDetails brAPIObject) { + return makeDeltaGermplasmListDetails(brAPIObject, applicationReferenceSourceBase, germplasmService); + } + @Bean @Prototype public Experiment makeExperimentBean(@NonNull BrAPITrial brAPIObject) { diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java new file mode 100644 index 000000000..1f8ea3d9c --- /dev/null +++ b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java @@ -0,0 +1,62 @@ +package org.breedinginsight.model.delta; + +import io.micronaut.context.annotation.Prototype; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.brapi.v2.model.germ.BrAPIGermplasm; +import org.breedinginsight.api.model.v1.request.query.SearchRequest; +import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; +import org.breedinginsight.brapi.v2.model.request.query.GermplasmQuery; +import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; +import org.breedinginsight.brapps.importer.model.exports.FileType; +import org.breedinginsight.model.DownloadFile; +import org.breedinginsight.services.exceptions.DoesNotExistException; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; +import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; +import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; + +import java.io.IOException; +import java.util.List; +import java.util.UUID; + + +@Prototype +public class DeltaGermplasmListDetails extends DeltaListDetails { + + private final BrAPIGermplasmService germplasmService; + + // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. + DeltaGermplasmListDetails(BrAPIListDetails brAPIListDetails, + String referenceSourceBase, + BrAPIGermplasmService germplasmService) { + super(brAPIListDetails, referenceSourceBase); + this.germplasmService = germplasmService; + } + + @Override + public List getDataObjects() throws ApiException, UnprocessableEntityException { + UUID programId = getProgramId().orElseThrow(() -> new UnprocessableEntityException("Program Id not found for list " + getListName())); + return germplasmService.getGermplasmByList(programId, getListDbId()); + } + + @Override + public DownloadFile exportListObjects(FileType extension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException, UnprocessableEntityException { + UUID programId = getProgramId().orElseThrow(() -> new UnprocessableEntityException("Program Id not found for list " + getListName())); + return germplasmService.exportGermplasmList(programId, getListDbId(), extension); + } + + @Override + public AbstractQueryMapper getQueryMapper() { + return new GermplasmQueryMapper(); + } + + @Override + public BrapiQuery getQuery() { + return new GermplasmQuery(); + } + + @Override + public SearchRequest constructSearchRequest(BrapiQuery queryParams) { + return ((GermplasmQuery) queryParams).constructSearchRequest(); + } +} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java new file mode 100644 index 000000000..6ddd2518b --- /dev/null +++ b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java @@ -0,0 +1,16 @@ +package org.breedinginsight.model.delta; + +import io.micronaut.context.annotation.Prototype; +import lombok.Getter; +import lombok.Setter; +import org.brapi.v2.model.core.BrAPIListSummary; +import org.breedinginsight.brapps.importer.model.response.ImportObjectState; + + +@Prototype +public class DeltaGermplasmListSummary extends DeltaListSummary { + + // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. + DeltaGermplasmListSummary(BrAPIListSummary brAPIListSummary) { super(brAPIListSummary); } + +} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java b/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java new file mode 100644 index 000000000..9f2c745bf --- /dev/null +++ b/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java @@ -0,0 +1,78 @@ +package org.breedinginsight.model.delta; + +import io.micronaut.context.annotation.Prototype; +import lombok.Getter; +import lombok.Setter; +import lombok.Value; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.BrAPIExternalReference; +import org.brapi.v2.model.core.BrAPIListTypes; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.breedinginsight.api.model.v1.request.query.SearchRequest; +import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; +import org.breedinginsight.brapps.importer.model.exports.FileType; +import org.breedinginsight.brapps.importer.model.response.ImportObjectState; +import io.micronaut.context.annotation.Property; +import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; +import org.breedinginsight.model.DownloadFile; +import org.breedinginsight.services.exceptions.DoesNotExistException; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; +import org.breedinginsight.utilities.Utilities; +import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + + +@Prototype +public abstract class DeltaListDetails extends DeltaEntity { + private final String referenceSourceBase; + @Getter + @Setter + private ImportObjectState state; + + // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. + DeltaListDetails(BrAPIListDetails brAPIListDetails, String referenceSourceBase) { + super(brAPIListDetails); + this.referenceSourceBase = referenceSourceBase; + } + + public abstract List getDataObjects() throws ApiException, UnprocessableEntityException; + public abstract DownloadFile exportListObjects(FileType extension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException, UnprocessableEntityException; + public abstract AbstractQueryMapper getQueryMapper(); + + public abstract BrapiQuery getQuery(); + + public abstract SearchRequest constructSearchRequest(BrapiQuery queryParams); + + public BrAPIListTypes getListType() { + return entity.getListType(); + } + + public Optional getListId() { + return getXrefId(ExternalReferenceSource.LISTS); + } + + public Optional getProgramId() { + return getXrefId(ExternalReferenceSource.PROGRAMS); + } + + public String getListDbId() { + return entity.getListDbId(); + } + + public String getListName() { + return entity.getListName(); + } + + private Optional getXrefId(ExternalReferenceSource source) { + // Get the external reference if it exists + Optional xrefOptional = Utilities.getExternalReference(entity.getExternalReferences(), referenceSourceBase, source); + + // Parse the Deltabreed ID from the xref + return xrefOptional.map(BrAPIExternalReference::getReferenceId).map(UUID::fromString); + } +} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java b/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java new file mode 100644 index 000000000..80569d077 --- /dev/null +++ b/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java @@ -0,0 +1,20 @@ +package org.breedinginsight.model.delta; + +import io.micronaut.context.annotation.Prototype; +import lombok.Getter; +import lombok.Setter; +import org.brapi.v2.model.core.BrAPIListSummary; +import org.breedinginsight.brapps.importer.model.response.ImportObjectState; + + +@Prototype +public class DeltaListSummary extends DeltaEntity { + + @Getter + @Setter + private ImportObjectState state; + + // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. + DeltaListSummary(BrAPIListSummary brAPIListSummary) { super(brAPIListSummary); } + +} diff --git a/src/main/java/org/breedinginsight/services/ListService.java b/src/main/java/org/breedinginsight/services/ListService.java new file mode 100644 index 000000000..c0c18f734 --- /dev/null +++ b/src/main/java/org/breedinginsight/services/ListService.java @@ -0,0 +1,33 @@ +package org.breedinginsight.services; + +import lombok.extern.slf4j.Slf4j; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; +import org.breedinginsight.daos.ListDAO; +import org.breedinginsight.model.delta.DeltaListDetails; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.UUID; + +@Slf4j +@Singleton +public class ListService { + private final BrAPIListDAO brAPIListDAO; + private final ListDAO listDAO; + + @Inject + public ListService(BrAPIListDAO brAPIListDAO, ListDAO listDAO) { + this.brAPIListDAO = brAPIListDAO; + this.listDAO = listDAO; + } + + public DeltaListDetails getDeltaListDetails(String listDbId, UUID programId) throws ApiException { + return listDAO.getDeltaListDetailsByDbId(listDbId, programId); + } + + public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + listDAO.deleteBrAPIList(listDbId, programId, hardDelete); + } + +} diff --git a/src/main/java/org/breedinginsight/utilities/Utilities.java b/src/main/java/org/breedinginsight/utilities/Utilities.java index 20f3254d6..7ed72cfef 100644 --- a/src/main/java/org/breedinginsight/utilities/Utilities.java +++ b/src/main/java/org/breedinginsight/utilities/Utilities.java @@ -17,6 +17,7 @@ package org.breedinginsight.utilities; +import io.micronaut.context.annotation.Property; import org.apache.commons.lang3.StringUtils; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.BrAPIExternalReference; @@ -24,6 +25,7 @@ import org.breedinginsight.model.Program; import org.flywaydb.core.api.migration.Context; +import javax.inject.Inject; import java.lang.reflect.Field; import java.sql.ResultSet; import java.sql.Statement; From 664083e402b6958a09678a84eb081d5bdbe0e5e7 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:24:31 -0500 Subject: [PATCH 2/9] get germplasm list contents via germplasm controller --- .../brapi/v2/BrAPIGermplasmController.java | 3 +- .../brapi/v2/BrAPIListController.java | 21 ++-- .../brapi/v2/dao/BrAPIListDAO.java | 78 +++++++++++++- .../model/request/query/GermplasmQuery.java | 3 + .../brapi/v2/services/BrAPIListService.java | 10 ++ .../org/breedinginsight/daos/ListDAO.java | 102 ------------------ .../breedinginsight/services/ListService.java | 33 ------ 7 files changed, 97 insertions(+), 153 deletions(-) delete mode 100644 src/main/java/org/breedinginsight/daos/ListDAO.java delete mode 100644 src/main/java/org/breedinginsight/services/ListService.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java index 4d67e2a00..57ead5bd3 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java @@ -209,7 +209,8 @@ public HttpResponse>>> getGermplasm( germplasmQueryMapper.setDateDisplayFormat(dateFormatParam); } - List germplasm = germplasmService.getGermplasm(programId); + // Fetch all germplasm in the program unless a list id is supplied to return only germplasm in that collection + List germplasm = queryParams.getList() == null ? germplasmService.getGermplasm(programId) : germplasmService.getGermplasmByList(programId, queryParams.getList());; SearchRequest searchRequest = queryParams.constructSearchRequest(); return ResponseUtils.getBrapiQueryResponse(germplasm, germplasmQueryMapper, queryParams, searchRequest); } catch (ApiException e) { diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 0eebc602d..2d3324a65 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -13,6 +13,7 @@ import org.brapi.v2.model.core.BrAPIListTypes; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; +import org.breedinginsight.api.model.v1.request.query.SearchRequest; import org.breedinginsight.api.model.v1.response.DataResponse; import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.api.model.v1.validators.QueryValid; @@ -24,13 +25,11 @@ import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.Program; import org.breedinginsight.model.delta.DeltaListDetails; -import org.breedinginsight.services.ListService; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.response.ResponseUtils; import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; import org.breedinginsight.utilities.response.mappers.ListQueryMapper; -import org.breedinginsight.api.model.v1.request.query.SearchRequest; import javax.inject.Inject; import javax.validation.ConstraintViolation; @@ -47,21 +46,19 @@ public class BrAPIListController { private final ProgramService programService; private final BrAPIListService brapiListService; - private final ListService listService; private final ListQueryMapper listQueryMapper; private final Validator validator; @Inject - public BrAPIListController(ProgramService programService, BrAPIListService brapiListService, ListService listService, + public BrAPIListController(ProgramService programService, BrAPIListService brapiListService, ListQueryMapper listQueryMapper, Validator validator) { this.programService = programService; this.brapiListService = brapiListService; - this.listService = listService; this.listQueryMapper = listQueryMapper; this.validator = validator; } - @Get("/deltalists{?queryParams*}") + @Get("/lists{?queryParams*}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse>> getLists( @@ -101,7 +98,7 @@ public HttpResponse>> getLists( } } - @Delete("/deltalists/{listDbId}") + @Delete("/lists/{listDbId}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse>> deleteListById( @@ -115,7 +112,7 @@ public HttpResponse>> deleteListById( hardDelete = "true".equals(paramValue); } try { - listService.deleteBrAPIList(listDbId, programId, hardDelete); + brapiListService.deleteBrAPIList(listDbId, programId, hardDelete); return HttpResponse.status(HttpStatus.NO_CONTENT); } catch (Exception e) { log.info(e.getMessage(), e); @@ -123,7 +120,7 @@ public HttpResponse>> deleteListById( } } - @Get("/deltalists/{listDbId}") + @Get("/lists/{listDbId}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) @SuppressWarnings("unchecked") @@ -133,7 +130,7 @@ public HttpResponse>>> g HttpRequest request) { try { // Get the list from the BrAPI service - DeltaListDetails details = listService.getDeltaListDetails(listDbId, programId); + DeltaListDetails details = brapiListService.getDeltaListDetails(listDbId, programId); // Get a new instance of BrAPI query matching the type of list contents T queryParams = (T) details.getQuery(); @@ -167,7 +164,7 @@ public HttpResponse>>> g } } - @Get("/deltalists/{listDbId}/export{?fileExtension}") + @Get("/lists/{listDbId}/export{?fileExtension}") @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse germplasmListExport( @@ -175,7 +172,7 @@ public HttpResponse germplasmListExport( String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; try { // Get the list from the BrAPI service - DeltaListDetails details = listService.getDeltaListDetails(listDbId, programId); + DeltaListDetails details = brapiListService.getDeltaListDetails(listDbId, programId); FileType extension = Enum.valueOf(FileType.class, fileExtension); DownloadFile listContentsFile = details.exportListObjects(extension); diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index 801b9775c..ed7db131c 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -17,7 +17,12 @@ package org.breedinginsight.brapi.v2.dao; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.exceptions.HttpStatusException; import lombok.extern.slf4j.Slf4j; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; import org.brapi.client.v2.ApiResponse; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.model.queryParams.core.ListQueryParams; @@ -31,19 +36,24 @@ import org.brapi.v2.model.core.request.BrAPIListSearchRequest; import org.brapi.v2.model.core.response.*; import org.brapi.v2.model.pheno.BrAPIObservation; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.daos.ProgramDAO; +import org.breedinginsight.model.ProgramBrAPIEndpoints; +import org.breedinginsight.model.delta.DeltaEntityFactory; +import org.breedinginsight.model.delta.DeltaListDetails; +import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; +import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.BrAPIDAOUtil; import org.breedinginsight.utilities.Utilities; import javax.inject.Inject; import javax.validation.constraints.NotNull; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.TimeUnit; @Slf4j public class BrAPIListDAO { @@ -52,13 +62,17 @@ public class BrAPIListDAO { private ImportDAO importDAO; private final BrAPIDAOUtil brAPIDAOUtil; private final BrAPIEndpointProvider brAPIEndpointProvider; + private final DeltaEntityFactory deltaEntityFactory; + private final ProgramService programService; @Inject - public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider) { + public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, DeltaEntityFactory deltaEntityFactory, ProgramService programService) { this.programDAO = programDAO; this.importDAO = importDAO; this.brAPIDAOUtil = brAPIDAOUtil; this.brAPIEndpointProvider = brAPIEndpointProvider; + this.deltaEntityFactory = deltaEntityFactory; + this.programService = programService; } public List getListByName(List listNames, UUID programId) throws ApiException { @@ -196,4 +210,58 @@ public List createBrAPILists(List brapiLi throw new ApiException("No response after creating list"); } + + public DeltaListDetails getDeltaListDetailsByDbId(String listDbId, UUID programId) throws ApiException { + ListsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ListsApi.class); + ApiResponse response = api.listsListDbIdGet(listDbId); + if (Objects.isNull(response.getBody()) || Objects.isNull(response.getBody().getResult())) + { + throw new ApiException(); + } + + BrAPIListDetails details = response.getBody().getResult(); + return deltaEntityFactory.makeDeltaListDetailsBean(details); + } + + public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); + var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); + requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); + HttpUrl url = requestUrl.build(); + var brapiRequest = new Request.Builder().url(url) + .method("DELETE", null) + .addHeader("Content-Type", "application/json") + .build(); + + makeCall(brapiRequest); + } + + private void makeCall(Request brapiRequest) throws ApiException { + OkHttpClient client = new OkHttpClient.Builder() + .readTimeout(5, TimeUnit.MINUTES) + .build(); + try { + client.newCall(brapiRequest).execute(); + } catch (IOException e) { + log.error("Error calling BrAPI Service", e); + throw new ApiException("Error calling BrAPI Service"); + } + } + + private String getProgramBrAPIBaseUrl(UUID programId) { + ProgramBrAPIEndpoints programBrAPIEndpoints; + try { + programBrAPIEndpoints = programService.getBrapiEndpoints(programId); + } catch (DoesNotExistException e) { + throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); + } + + if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { + log.error("Program: " + programId + " is missing BrAPI URL config"); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); + } + var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); + programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; + return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; + } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java b/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java index 88f6d142c..92752fad6 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java +++ b/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java @@ -27,6 +27,9 @@ public class GermplasmQuery extends BrapiQuery { // This is a meta-parameter, it describes the display format of any date fields. private String dateDisplayFormat; + // The list id used to get a collection of germplasm + private String list; + public SearchRequest constructSearchRequest() { List filters = new ArrayList<>(); if (!StringUtils.isBlank(getImportEntryNumber())) { diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java index 3353ac17c..7dd7c81c6 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java @@ -12,6 +12,7 @@ import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.model.Program; +import org.breedinginsight.model.delta.DeltaListDetails; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.Utilities; @@ -19,6 +20,7 @@ import javax.inject.Singleton; import java.util.List; import java.util.Optional; +import java.util.UUID; import java.util.stream.Collectors; @Slf4j @@ -88,4 +90,12 @@ public List getListSummariesByTypeAndXref( return programLists; } + + public DeltaListDetails getDeltaListDetails(String listDbId, UUID programId) throws ApiException { + return listDAO.getDeltaListDetailsByDbId(listDbId, programId); + } + + public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + listDAO.deleteBrAPIList(listDbId, programId, hardDelete); + } } diff --git a/src/main/java/org/breedinginsight/daos/ListDAO.java b/src/main/java/org/breedinginsight/daos/ListDAO.java deleted file mode 100644 index f44a8757f..000000000 --- a/src/main/java/org/breedinginsight/daos/ListDAO.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.breedinginsight.daos; - -import io.micronaut.http.HttpStatus; -import io.micronaut.http.exceptions.HttpStatusException; -import lombok.extern.slf4j.Slf4j; -import okhttp3.*; -import org.brapi.client.v2.ApiResponse; -import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.client.v2.modules.core.ListsApi; -import org.brapi.v2.model.core.response.BrAPIListDetails; -import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; -import org.breedinginsight.brapi.v1.controller.BrapiVersion; -import org.breedinginsight.model.ProgramBrAPIEndpoints; -import org.breedinginsight.model.delta.DeltaEntityFactory; -import org.breedinginsight.model.delta.DeltaListDetails; -import org.breedinginsight.services.ProgramService; -import org.breedinginsight.services.brapi.BrAPIEndpointProvider; -import org.breedinginsight.services.exceptions.DoesNotExistException; -import org.breedinginsight.utilities.BrAPIDAOUtil; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.io.IOException; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -@Slf4j -@Singleton -public class ListDAO { - private final ProgramDAO programDAO; - private final BrAPIDAOUtil brAPIDAOUtil; - private final BrAPIEndpointProvider brAPIEndpointProvider; - private final DeltaEntityFactory deltaEntityFactory; - private final ProgramService programService; - - @Inject - public ListDAO(ProgramDAO programDAO, - BrAPIDAOUtil brAPIDAOUtil, - BrAPIEndpointProvider brAPIEndpointProvider, - DeltaEntityFactory deltaEntityFactory, ProgramService programService) { - this.programDAO = programDAO; - this.brAPIDAOUtil = brAPIDAOUtil; - this.brAPIEndpointProvider = brAPIEndpointProvider; - this.deltaEntityFactory = deltaEntityFactory; - this.programService = programService; - } - - public DeltaListDetails getDeltaListDetailsByDbId(String listDbId, UUID programId) throws ApiException { - ListsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ListsApi.class); - ApiResponse response = api.listsListDbIdGet(listDbId); - if (Objects.isNull(response.getBody()) || Objects.isNull(response.getBody().getResult())) - { - throw new ApiException(); - } - - BrAPIListDetails details = response.getBody().getResult(); - return deltaEntityFactory.makeDeltaListDetailsBean(details); - } - - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { - var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); - var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); - requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); - HttpUrl url = requestUrl.build(); - var brapiRequest = new Request.Builder().url(url) - .method("DELETE", null) - .addHeader("Content-Type", "application/json") - .build(); - - makeCall(brapiRequest); - } - - private void makeCall(Request brapiRequest) throws ApiException { - OkHttpClient client = new OkHttpClient.Builder() - .readTimeout(5, TimeUnit.MINUTES) - .build(); - try { - client.newCall(brapiRequest).execute(); - } catch (IOException e) { - log.error("Error calling BrAPI Service", e); - throw new ApiException("Error calling BrAPI Service"); - } - } - - private String getProgramBrAPIBaseUrl(UUID programId) { - ProgramBrAPIEndpoints programBrAPIEndpoints; - try { - programBrAPIEndpoints = programService.getBrapiEndpoints(programId); - } catch (DoesNotExistException e) { - throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); - } - - if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { - log.error("Program: " + programId + " is missing BrAPI URL config"); - throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); - } - var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); - programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; - return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; - } -} diff --git a/src/main/java/org/breedinginsight/services/ListService.java b/src/main/java/org/breedinginsight/services/ListService.java deleted file mode 100644 index c0c18f734..000000000 --- a/src/main/java/org/breedinginsight/services/ListService.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.breedinginsight.services; - -import lombok.extern.slf4j.Slf4j; -import org.brapi.client.v2.model.exceptions.ApiException; -import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; -import org.breedinginsight.daos.ListDAO; -import org.breedinginsight.model.delta.DeltaListDetails; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.UUID; - -@Slf4j -@Singleton -public class ListService { - private final BrAPIListDAO brAPIListDAO; - private final ListDAO listDAO; - - @Inject - public ListService(BrAPIListDAO brAPIListDAO, ListDAO listDAO) { - this.brAPIListDAO = brAPIListDAO; - this.listDAO = listDAO; - } - - public DeltaListDetails getDeltaListDetails(String listDbId, UUID programId) throws ApiException { - return listDAO.getDeltaListDetailsByDbId(listDbId, programId); - } - - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { - listDAO.deleteBrAPIList(listDbId, programId, hardDelete); - } - -} From 582c695d8036e3988fc5df69f8cb5985a8132e02 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:51:38 -0500 Subject: [PATCH 3/9] revert GET list data objects from ListController --- .../brapi/v2/BrAPIGermplasmController.java | 24 ++-- .../brapi/v2/BrAPIListController.java | 124 +----------------- .../brapi/v2/dao/BrAPIListDAO.java | 31 ++--- .../v2/services/BrAPIGermplasmService.java | 14 +- .../brapi/v2/services/BrAPIListService.java | 5 - .../model/delta/DeltaEntityFactory.java | 57 +------- .../delta/DeltaGermplasmListDetails.java | 62 --------- .../delta/DeltaGermplasmListSummary.java | 16 --- .../model/delta/DeltaListDetails.java | 78 ----------- .../model/delta/DeltaListSummary.java | 20 --- .../breedinginsight/utilities/Utilities.java | 2 - 11 files changed, 40 insertions(+), 393 deletions(-) delete mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java delete mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java delete mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java delete mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java index 57ead5bd3..94cf9c9fc 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 com.drew.lang.annotations.Nullable; import io.micronaut.context.annotation.Property; import io.micronaut.http.HttpHeaders; import io.micronaut.http.HttpResponse; @@ -19,10 +20,11 @@ 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.*; +import org.brapi.v2.model.germ.response.BrAPIGermplasmListResponse; +import org.brapi.v2.model.germ.response.BrAPIGermplasmPedigreeResponse; +import org.brapi.v2.model.germ.response.BrAPIGermplasmProgenyResponse; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; import org.breedinginsight.api.model.v1.request.query.SearchRequest; @@ -33,27 +35,25 @@ 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; -import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; import org.breedinginsight.brapps.importer.model.exports.FileType; import org.breedinginsight.daos.ProgramDAO; import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.GermplasmGenotype; +import org.breedinginsight.model.Program; +import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; import org.breedinginsight.services.exceptions.AuthorizationException; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.services.geno.GenotypeService; +import org.breedinginsight.utilities.Utilities; import org.breedinginsight.utilities.response.ResponseUtils; +import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; 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}") @@ -222,15 +222,17 @@ public HttpResponse>>> getGermplasm( } } - @Get("/programs/{programId}/germplasm/export{?fileExtension}") + @Get("/programs/{programId}/germplasm/export{?fileExtension,list}") @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse germplasmExport( - @PathVariable("programId") UUID programId, @QueryValue(defaultValue = "XLSX") String fileExtension) { + @PathVariable("programId") UUID programId, + @QueryValue(defaultValue = "XLSX") String fileExtension, + @QueryValue Optional list) { String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; try { FileType extension = Enum.valueOf(FileType.class, fileExtension); - DownloadFile germplasmListFile = germplasmService.exportGermplasm(programId, extension); + DownloadFile germplasmListFile = list.isEmpty() ? germplasmService.exportGermplasm(programId, extension) : germplasmService.exportGermplasmList(programId, list.get(), extension); HttpResponse germplasmExport = HttpResponse.ok(germplasmListFile.getStreamedFile()).header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="+germplasmListFile.getFileName()+extension.getExtension()); return germplasmExport; } diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 2d3324a65..3cb73dbae 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -1,10 +1,10 @@ package org.breedinginsight.brapi.v2; -import io.micronaut.core.beans.BeanIntrospection; -import io.micronaut.core.beans.BeanProperty; -import io.micronaut.http.*; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; import io.micronaut.http.annotation.*; -import io.micronaut.http.server.types.files.StreamedFile; import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; @@ -18,27 +18,19 @@ import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.api.model.v1.validators.QueryValid; import org.breedinginsight.brapi.v1.controller.BrapiVersion; -import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; import org.breedinginsight.brapi.v2.model.request.query.ListQuery; import org.breedinginsight.brapi.v2.services.BrAPIListService; -import org.breedinginsight.brapps.importer.model.exports.FileType; -import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.Program; -import org.breedinginsight.model.delta.DeltaListDetails; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.response.ResponseUtils; -import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; import org.breedinginsight.utilities.response.mappers.ListQueryMapper; import javax.inject.Inject; -import javax.validation.ConstraintViolation; import javax.validation.Valid; import javax.validation.Validator; import java.util.List; -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; @Slf4j @Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @@ -105,7 +97,7 @@ public HttpResponse>> deleteListById( @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, HttpRequest request - ) throws DoesNotExistException, ApiException { + ) { boolean hardDelete = false; if (request.getParameters().contains("hardDelete")) { String paramValue = request.getParameters().get("hardDelete"); @@ -119,110 +111,4 @@ public HttpResponse>> deleteListById( return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving germplasm list records"); } } - - @Get("/lists/{listDbId}") - @Produces(MediaType.APPLICATION_JSON) - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - @SuppressWarnings("unchecked") - public HttpResponse>>> getListById( - @PathVariable("programId") UUID programId, - @PathVariable("listDbId") String listDbId, - HttpRequest request) { - try { - // Get the list from the BrAPI service - DeltaListDetails details = brapiListService.getDeltaListDetails(listDbId, programId); - - // Get a new instance of BrAPI query matching the type of list contents - T queryParams = (T) details.getQuery(); - - // Bind query parameters to the object - bindQueryParams(queryParams, request); - - // Perform standard bean validation - Set> violations = validator.validate(queryParams); - if (!violations.isEmpty()) { - List errorMessages = violations.stream() - .map(ConstraintViolation::getMessage) - .collect(Collectors.toList()); - log.info(String.join(", ", errorMessages)); - return HttpResponse.status(HttpStatus.BAD_REQUEST, "Error with list contents search parameters"); - } - - // Fetch the list contents from the BrAPI service - List listContentsBrAPIObjects = (List) details.getDataObjects(); - - // Construct a search request for sorting the list contents - SearchRequest searchRequest = details.constructSearchRequest(queryParams); - - // Get the map used to connect query sorting keys to contents object values - AbstractQueryMapper contentsQueryMapper = details.getQueryMapper(); - - return ResponseUtils.getBrapiQueryResponse(listContentsBrAPIObjects, contentsQueryMapper, queryParams, searchRequest); - } catch (Exception e) { - log.info(e.getMessage(), e); - return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving list records"); - } - } - - @Get("/lists/{listDbId}/export{?fileExtension}") - @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - public HttpResponse germplasmListExport( - @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, @QueryValue(defaultValue = "XLSX") String fileExtension) { - String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; - try { - // Get the list from the BrAPI service - DeltaListDetails details = brapiListService.getDeltaListDetails(listDbId, programId); - - FileType extension = Enum.valueOf(FileType.class, fileExtension); - DownloadFile listContentsFile = details.exportListObjects(extension); - return HttpResponse.ok(listContentsFile.getStreamedFile()).header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="+listContentsFile.getFileName()+extension.getExtension()); - } - catch (Exception e) { - log.info(e.getMessage(), e); - e.printStackTrace(); - HttpResponse response = HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, downloadErrorMessage).contentType(MediaType.TEXT_PLAIN).body(downloadErrorMessage); - return response; - } - } - - private void bindQueryParams(BrapiQuery queryParams, HttpRequest request) { - BeanIntrospection introspection = BeanIntrospection.getIntrospection(BrapiQuery.class); - for (BeanProperty property : introspection.getBeanProperties()) { - String paramName = property.getName(); - if (request.getParameters().contains(paramName)) { - String paramValue = request.getParameters().get(paramName); - Object convertedValue; - Class propertyType = property.getType(); - - if (propertyType.isEnum()) { - convertedValue = convertToEnum(paramValue, (Class>) propertyType); - } else { - convertedValue = convertValue(paramValue, propertyType); - } - - property.set(queryParams, convertedValue); - } - } - } - - private > T convertToEnum(String value, Class> enumClass) { - if (value == null) { - return null; - } - return Enum.valueOf((Class) enumClass, value.toUpperCase()); - } - - - // Convert, if necessary, the values of query parameters to match the type defined for the fields in the BrapiQuery class - private Object convertValue(String value, Class targetType) { - // Implement type conversion logic here - // Other list content types might need more complex logic - if (targetType == String.class) return value; - if (targetType == Integer.class) return Integer.parseInt(value); - if (targetType == Long.class) return Long.parseLong(value); - if (targetType == Boolean.class) return Boolean.parseBoolean(value); - // Add more type conversions as needed - return value; - } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index ed7db131c..55b395ea3 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -25,7 +25,6 @@ import okhttp3.Request; import org.brapi.client.v2.ApiResponse; import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.client.v2.model.queryParams.core.ListQueryParams; import org.brapi.client.v2.modules.core.ListsApi; import org.brapi.v2.model.BrAPIExternalReference; import org.brapi.v2.model.BrAPIResponse; @@ -34,15 +33,15 @@ import org.brapi.v2.model.core.BrAPIListTypes; import org.brapi.v2.model.core.request.BrAPIListNewRequest; import org.brapi.v2.model.core.request.BrAPIListSearchRequest; -import org.brapi.v2.model.core.response.*; -import org.brapi.v2.model.pheno.BrAPIObservation; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.brapi.v2.model.core.response.BrAPIListsListResponse; +import org.brapi.v2.model.core.response.BrAPIListsListResponseResult; +import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.daos.ProgramDAO; import org.breedinginsight.model.ProgramBrAPIEndpoints; -import org.breedinginsight.model.delta.DeltaEntityFactory; -import org.breedinginsight.model.delta.DeltaListDetails; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; import org.breedinginsight.services.exceptions.DoesNotExistException; @@ -52,7 +51,10 @@ import javax.inject.Inject; import javax.validation.constraints.NotNull; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; import java.util.concurrent.TimeUnit; @Slf4j @@ -62,16 +64,14 @@ public class BrAPIListDAO { private ImportDAO importDAO; private final BrAPIDAOUtil brAPIDAOUtil; private final BrAPIEndpointProvider brAPIEndpointProvider; - private final DeltaEntityFactory deltaEntityFactory; private final ProgramService programService; @Inject - public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, DeltaEntityFactory deltaEntityFactory, ProgramService programService) { + public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, ProgramService programService) { this.programDAO = programDAO; this.importDAO = importDAO; this.brAPIDAOUtil = brAPIDAOUtil; this.brAPIEndpointProvider = brAPIEndpointProvider; - this.deltaEntityFactory = deltaEntityFactory; this.programService = programService; } @@ -211,19 +211,8 @@ public List createBrAPILists(List brapiLi throw new ApiException("No response after creating list"); } - public DeltaListDetails getDeltaListDetailsByDbId(String listDbId, UUID programId) throws ApiException { - ListsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ListsApi.class); - ApiResponse response = api.listsListDbIdGet(listDbId); - if (Objects.isNull(response.getBody()) || Objects.isNull(response.getBody().getResult())) - { - throw new ApiException(); - } - - BrAPIListDetails details = response.getBody().getResult(); - return deltaEntityFactory.makeDeltaListDetailsBean(details); - } - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + // TODO: Switch to using the ListsApi from the BrAPI client library once the delete endpoints are merged into it var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); 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 8023327d7..231978368 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java @@ -82,7 +82,7 @@ public Optional getGermplasmByDBID(UUID programId, String germpl return germplasmDAO.getGermplasmByDBID(germplasmId, programId); } - public List> processListData(List germplasm, BrAPIListDetails germplasmList, Program program){ + public List> processListData(List germplasm, List listData, Program program){ Map germplasmByName = new HashMap<>(); for (BrAPIGermplasm g: germplasm) { // Use the full, unique germplasmName with programKey and accessionNumber (GID) for 2 reasons: @@ -97,14 +97,14 @@ public List> processListData(List germplasm, // This holds the BrAPI list items or all germplasm in a program if the list is null. List orderedGermplasmNames = new ArrayList<>(); - if (germplasmList == null) { + if (listData == null) { orderedGermplasmNames = germplasm.stream().sorted((left, right) -> { Integer leftAccessionNumber = Integer.parseInt(left.getAccessionNumber()); Integer rightAccessionNumber = Integer.parseInt(right.getAccessionNumber()); return leftAccessionNumber.compareTo(rightAccessionNumber); }).map(BrAPIGermplasm::getGermplasmName).collect(Collectors.toList()); } else { - orderedGermplasmNames = germplasmList.getData(); + orderedGermplasmNames = listData; } // For export, assign entry number sequentially based on BrAPI list order. @@ -124,7 +124,7 @@ public List> processListData(List germplasm, row.put("Source", source); // Use the entry number in the list map if generated - if(germplasmList == null) { + if(listData == null) { // Not downloading a real list, use GID (https://breedinginsight.atlassian.net/browse/BI-2266). row.put("Entry No", Integer.valueOf(germplasmEntry.getAccessionNumber())); } else { @@ -254,7 +254,9 @@ public DownloadFile exportGermplasm(UUID programId, FileType fileExtension) thro return new DownloadFile(fileName, downloadFile); } - public DownloadFile exportGermplasmList(UUID programId, String listId, FileType fileExtension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException { + public DownloadFile exportGermplasmList(UUID programId, + String listId, + FileType fileExtension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException { List columns = GermplasmFileColumns.getOrderedColumns(); //Retrieve germplasm list data @@ -270,7 +272,7 @@ public DownloadFile exportGermplasmList(UUID programId, String listId, FileType String fileName = createFileName(listData, listName); StreamedFile downloadFile; //Convert list data to List> data to pass into file writer - List> processedData = processListData(germplasm, listData, program); + List> processedData = processListData(germplasm, germplasmNames, program); if (fileExtension == FileType.CSV){ downloadFile = CSVWriter.writeToDownload(columns, processedData, fileExtension); diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java index 7dd7c81c6..1ee77e310 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java @@ -12,7 +12,6 @@ import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.model.Program; -import org.breedinginsight.model.delta.DeltaListDetails; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.Utilities; @@ -91,10 +90,6 @@ public List getListSummariesByTypeAndXref( return programLists; } - public DeltaListDetails getDeltaListDetails(String listDbId, UUID programId) throws ApiException { - return listDAO.getDeltaListDetailsByDbId(listDbId, programId); - } - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { listDAO.deleteBrAPIList(listDbId, programId, hardDelete); } diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java index 8d473ce5a..f1724ef12 100644 --- a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java +++ b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java @@ -7,19 +7,16 @@ import lombok.NonNull; import org.brapi.v2.model.core.BrAPIListSummary; import org.brapi.v2.model.core.BrAPIListTypes; -import org.brapi.v2.model.core.response.BrAPIListDetails; -import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; -import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; -import org.breedinginsight.brapps.importer.services.processors.experiment.service.ObservationUnitService; - import org.brapi.v2.model.core.BrAPIStudy; import org.brapi.v2.model.core.BrAPITrial; +import org.brapi.v2.model.core.response.BrAPIListDetails; import org.brapi.v2.model.germ.BrAPIGermplasm; import org.brapi.v2.model.pheno.BrAPIObservation; import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.brapi.v2.model.pheno.BrAPIObservationVariable; +import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; +import org.breedinginsight.brapps.importer.services.processors.experiment.service.ObservationUnitService; import org.breedinginsight.model.ProgramLocation; -import org.breedinginsight.utilities.Utilities; import javax.inject.Inject; @@ -27,16 +24,10 @@ public class DeltaEntityFactory { private final ObservationUnitService observationUnitService; - private final BrAPIGermplasmService germplasmService; - private final String applicationReferenceSourceBase; @Inject - public DeltaEntityFactory(ObservationUnitService observationUnitService, - BrAPIGermplasmService germplasmService, - @Property(name = "brapi.server.reference-source") String applicationReferenceSourceBase) { + public DeltaEntityFactory(ObservationUnitService observationUnitService) { this.observationUnitService = observationUnitService; - this.germplasmService = germplasmService; - this.applicationReferenceSourceBase = applicationReferenceSourceBase; } private Experiment makeExperiment(BrAPITrial brAPIObject) { @@ -67,46 +58,6 @@ private DeltaObservationVariable makeDeltaObservationVariable(BrAPIObservationVa return new DeltaObservationVariable(brAPIObject); } - private DeltaGermplasmListSummary makeDeltaGermplasmListSummary(BrAPIListSummary brAPIObject) { - return new DeltaGermplasmListSummary(brAPIObject); - } - - private DeltaGermplasmListDetails makeDeltaGermplasmListDetails(BrAPIListDetails brAPIObject, String referenceSourceBase, BrAPIGermplasmService germplasmService) { - return new DeltaGermplasmListDetails(brAPIObject, referenceSourceBase, germplasmService); - } - - @Bean - @Prototype - public DeltaListSummary makeDeltaListSummaryBean(@NonNull BrAPIListSummary brAPIObject) { - BrAPIListTypes listType = brAPIObject.getListType(); - switch (listType) { - case GERMPLASM: return makeDeltaGermplasmListSummary(brAPIObject); - default: return null; - } - } - - @Bean - @Prototype - public DeltaListDetails makeDeltaListDetailsBean(@NonNull BrAPIListDetails brAPIObject) { - BrAPIListTypes listType = brAPIObject.getListType(); - switch (listType) { - case GERMPLASM: return makeDeltaGermplasmListDetailsBean(brAPIObject); - default: return null; - } - } - - @Bean - @Prototype - public DeltaGermplasmListSummary makeDeltaGermplasmListSummaryBean(@NonNull BrAPIListSummary brAPIObject) { - return makeDeltaGermplasmListSummary(brAPIObject); - } - - @Bean - @Prototype - public DeltaGermplasmListDetails makeDeltaGermplasmListDetailsBean(@NonNull BrAPIListDetails brAPIObject) { - return makeDeltaGermplasmListDetails(brAPIObject, applicationReferenceSourceBase, germplasmService); - } - @Bean @Prototype public Experiment makeExperimentBean(@NonNull BrAPITrial brAPIObject) { diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java deleted file mode 100644 index 1f8ea3d9c..000000000 --- a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.breedinginsight.model.delta; - -import io.micronaut.context.annotation.Prototype; -import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.v2.model.core.response.BrAPIListDetails; -import org.brapi.v2.model.germ.BrAPIGermplasm; -import org.breedinginsight.api.model.v1.request.query.SearchRequest; -import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; -import org.breedinginsight.brapi.v2.model.request.query.GermplasmQuery; -import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; -import org.breedinginsight.brapps.importer.model.exports.FileType; -import org.breedinginsight.model.DownloadFile; -import org.breedinginsight.services.exceptions.DoesNotExistException; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; -import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; -import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; - -import java.io.IOException; -import java.util.List; -import java.util.UUID; - - -@Prototype -public class DeltaGermplasmListDetails extends DeltaListDetails { - - private final BrAPIGermplasmService germplasmService; - - // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. - DeltaGermplasmListDetails(BrAPIListDetails brAPIListDetails, - String referenceSourceBase, - BrAPIGermplasmService germplasmService) { - super(brAPIListDetails, referenceSourceBase); - this.germplasmService = germplasmService; - } - - @Override - public List getDataObjects() throws ApiException, UnprocessableEntityException { - UUID programId = getProgramId().orElseThrow(() -> new UnprocessableEntityException("Program Id not found for list " + getListName())); - return germplasmService.getGermplasmByList(programId, getListDbId()); - } - - @Override - public DownloadFile exportListObjects(FileType extension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException, UnprocessableEntityException { - UUID programId = getProgramId().orElseThrow(() -> new UnprocessableEntityException("Program Id not found for list " + getListName())); - return germplasmService.exportGermplasmList(programId, getListDbId(), extension); - } - - @Override - public AbstractQueryMapper getQueryMapper() { - return new GermplasmQueryMapper(); - } - - @Override - public BrapiQuery getQuery() { - return new GermplasmQuery(); - } - - @Override - public SearchRequest constructSearchRequest(BrapiQuery queryParams) { - return ((GermplasmQuery) queryParams).constructSearchRequest(); - } -} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java deleted file mode 100644 index 6ddd2518b..000000000 --- a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.breedinginsight.model.delta; - -import io.micronaut.context.annotation.Prototype; -import lombok.Getter; -import lombok.Setter; -import org.brapi.v2.model.core.BrAPIListSummary; -import org.breedinginsight.brapps.importer.model.response.ImportObjectState; - - -@Prototype -public class DeltaGermplasmListSummary extends DeltaListSummary { - - // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. - DeltaGermplasmListSummary(BrAPIListSummary brAPIListSummary) { super(brAPIListSummary); } - -} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java b/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java deleted file mode 100644 index 9f2c745bf..000000000 --- a/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.breedinginsight.model.delta; - -import io.micronaut.context.annotation.Prototype; -import lombok.Getter; -import lombok.Setter; -import lombok.Value; -import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.v2.model.BrAPIExternalReference; -import org.brapi.v2.model.core.BrAPIListTypes; -import org.brapi.v2.model.core.response.BrAPIListDetails; -import org.breedinginsight.api.model.v1.request.query.SearchRequest; -import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; -import org.breedinginsight.brapps.importer.model.exports.FileType; -import org.breedinginsight.brapps.importer.model.response.ImportObjectState; -import io.micronaut.context.annotation.Property; -import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; -import org.breedinginsight.model.DownloadFile; -import org.breedinginsight.services.exceptions.DoesNotExistException; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; -import org.breedinginsight.utilities.Utilities; -import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; - -import java.io.IOException; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; - - -@Prototype -public abstract class DeltaListDetails extends DeltaEntity { - private final String referenceSourceBase; - @Getter - @Setter - private ImportObjectState state; - - // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. - DeltaListDetails(BrAPIListDetails brAPIListDetails, String referenceSourceBase) { - super(brAPIListDetails); - this.referenceSourceBase = referenceSourceBase; - } - - public abstract List getDataObjects() throws ApiException, UnprocessableEntityException; - public abstract DownloadFile exportListObjects(FileType extension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException, UnprocessableEntityException; - public abstract AbstractQueryMapper getQueryMapper(); - - public abstract BrapiQuery getQuery(); - - public abstract SearchRequest constructSearchRequest(BrapiQuery queryParams); - - public BrAPIListTypes getListType() { - return entity.getListType(); - } - - public Optional getListId() { - return getXrefId(ExternalReferenceSource.LISTS); - } - - public Optional getProgramId() { - return getXrefId(ExternalReferenceSource.PROGRAMS); - } - - public String getListDbId() { - return entity.getListDbId(); - } - - public String getListName() { - return entity.getListName(); - } - - private Optional getXrefId(ExternalReferenceSource source) { - // Get the external reference if it exists - Optional xrefOptional = Utilities.getExternalReference(entity.getExternalReferences(), referenceSourceBase, source); - - // Parse the Deltabreed ID from the xref - return xrefOptional.map(BrAPIExternalReference::getReferenceId).map(UUID::fromString); - } -} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java b/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java deleted file mode 100644 index 80569d077..000000000 --- a/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.breedinginsight.model.delta; - -import io.micronaut.context.annotation.Prototype; -import lombok.Getter; -import lombok.Setter; -import org.brapi.v2.model.core.BrAPIListSummary; -import org.breedinginsight.brapps.importer.model.response.ImportObjectState; - - -@Prototype -public class DeltaListSummary extends DeltaEntity { - - @Getter - @Setter - private ImportObjectState state; - - // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. - DeltaListSummary(BrAPIListSummary brAPIListSummary) { super(brAPIListSummary); } - -} diff --git a/src/main/java/org/breedinginsight/utilities/Utilities.java b/src/main/java/org/breedinginsight/utilities/Utilities.java index 7ed72cfef..20f3254d6 100644 --- a/src/main/java/org/breedinginsight/utilities/Utilities.java +++ b/src/main/java/org/breedinginsight/utilities/Utilities.java @@ -17,7 +17,6 @@ package org.breedinginsight.utilities; -import io.micronaut.context.annotation.Property; import org.apache.commons.lang3.StringUtils; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.BrAPIExternalReference; @@ -25,7 +24,6 @@ import org.breedinginsight.model.Program; import org.flywaydb.core.api.migration.Context; -import javax.inject.Inject; import java.lang.reflect.Field; import java.sql.ResultSet; import java.sql.Statement; From c93e8344295e2cee7619c71505c86211e63a7320 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:39:09 -0500 Subject: [PATCH 4/9] update tests --- .../org/breedinginsight/model/delta/DeltaEntityFactory.java | 5 ----- .../brapi/v2/GermplasmControllerIntegrationTest.java | 4 ++-- .../brapi/v2/ListControllerIntegrationTest.java | 5 +---- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java index f1724ef12..059f91e66 100644 --- a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java +++ b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java @@ -2,19 +2,14 @@ import io.micronaut.context.annotation.Bean; import io.micronaut.context.annotation.Factory; -import io.micronaut.context.annotation.Property; import io.micronaut.context.annotation.Prototype; import lombok.NonNull; -import org.brapi.v2.model.core.BrAPIListSummary; -import org.brapi.v2.model.core.BrAPIListTypes; import org.brapi.v2.model.core.BrAPIStudy; import org.brapi.v2.model.core.BrAPITrial; -import org.brapi.v2.model.core.response.BrAPIListDetails; import org.brapi.v2.model.germ.BrAPIGermplasm; import org.brapi.v2.model.pheno.BrAPIObservation; import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.brapi.v2.model.pheno.BrAPIObservationVariable; -import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; import org.breedinginsight.brapps.importer.services.processors.experiment.service.ObservationUnitService; import org.breedinginsight.model.ProgramLocation; diff --git a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java index 23e8f54d4..7c80482d9 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java @@ -245,7 +245,7 @@ public void getAllGermplasmByListSuccess() { String germplasmListDbId = fetchGermplasmListDbId(programId); // Build the endpoint to get germplasm by germplasm list. - String endpoint = String.format("/programs/%s/germplasm/lists/%s/records", programId, germplasmListDbId); + String endpoint = String.format("/programs/%s/brapi/v2/germplasm?list=%s", programId, germplasmListDbId); // Get germplasm by list. Flowable> call = client.exchange( @@ -258,7 +258,7 @@ public void getAllGermplasmByListSuccess() { JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject().getAsJsonObject("result"); JsonArray data = result.getAsJsonArray("data"); - assertEquals(data.size(), 3, "Wrong number of germplasm were returned"); + assertEquals(3, data.size(), "Wrong number of germplasm were returned"); } @Test diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index 9515b72a6..e29ce7e24 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -145,9 +145,7 @@ public void setup() { newExp.put(traits.get(0).getObservationVariableName(), "1"); JsonObject result = importTestUtils.uploadAndFetchWorkflow( - importTestUtils.writeExperimentDataToFile(List.of(newExp), traits), null, true, client, program, mappingId, newExperimentWorkflowId - ); - + importTestUtils.writeExperimentDataToFile(List.of(newExp), traits), null, true, client, program, mappingId, newExperimentWorkflowId); } @Test @@ -189,5 +187,4 @@ public void getAllListsSuccess() { } } } - } From a4169ff86fcfdf393358921c6f21bcd17567e8b2 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:24:18 -0500 Subject: [PATCH 5/9] add httpok helper methods to BrAPIDAOUtil --- .../brapi/v2/dao/BrAPIListDAO.java | 39 +++------------ .../utilities/BrAPIDAOUtil.java | 47 +++++++++++++++++-- .../v2/ListControllerIntegrationTest.java | 32 +++++++++++++ .../daos/BrAPIDAOUtilUnitTest.java | 10 +++- ...gwaGenotypeServiceImplIntegrationTest.java | 7 ++- 5 files changed, 96 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index 55b395ea3..11e079842 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -64,15 +64,16 @@ public class BrAPIListDAO { private ImportDAO importDAO; private final BrAPIDAOUtil brAPIDAOUtil; private final BrAPIEndpointProvider brAPIEndpointProvider; - private final ProgramService programService; @Inject - public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, ProgramService programService) { + public BrAPIListDAO(ProgramDAO programDAO, + ImportDAO importDAO, + BrAPIDAOUtil brAPIDAOUtil, + BrAPIEndpointProvider brAPIEndpointProvider) { this.programDAO = programDAO; this.importDAO = importDAO; this.brAPIDAOUtil = brAPIDAOUtil; this.brAPIEndpointProvider = brAPIEndpointProvider; - this.programService = programService; } public List getListByName(List listNames, UUID programId) throws ApiException { @@ -213,7 +214,7 @@ public List createBrAPILists(List brapiLi public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { // TODO: Switch to using the ListsApi from the BrAPI client library once the delete endpoints are merged into it - var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); + var programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(programId); var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); HttpUrl url = requestUrl.build(); @@ -222,35 +223,7 @@ public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) .addHeader("Content-Type", "application/json") .build(); - makeCall(brapiRequest); + brAPIDAOUtil.makeCall(brapiRequest); } - private void makeCall(Request brapiRequest) throws ApiException { - OkHttpClient client = new OkHttpClient.Builder() - .readTimeout(5, TimeUnit.MINUTES) - .build(); - try { - client.newCall(brapiRequest).execute(); - } catch (IOException e) { - log.error("Error calling BrAPI Service", e); - throw new ApiException("Error calling BrAPI Service"); - } - } - - private String getProgramBrAPIBaseUrl(UUID programId) { - ProgramBrAPIEndpoints programBrAPIEndpoints; - try { - programBrAPIEndpoints = programService.getBrapiEndpoints(programId); - } catch (DoesNotExistException e) { - throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); - } - - if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { - log.error("Program: " + programId + " is missing BrAPI URL config"); - throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); - } - var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); - programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; - return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; - } } diff --git a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java index e03a67017..075d6bb2b 100644 --- a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java +++ b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java @@ -18,25 +18,33 @@ package org.breedinginsight.utilities; import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.exceptions.HttpStatusException; import io.micronaut.http.server.exceptions.InternalServerException; import io.reactivex.functions.*; import lombok.extern.slf4j.Slf4j; +import okhttp3.OkHttpClient; +import okhttp3.Request; import org.apache.commons.lang3.StringUtils; 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.modules.germplasm.GermplasmApi; import org.brapi.v2.model.*; -import org.brapi.v2.model.germ.BrAPIGermplasm; -import org.brapi.v2.model.germ.response.BrAPIGermplasmSingleResponse; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapps.importer.model.ImportUpload; +import org.breedinginsight.model.ProgramBrAPIEndpoints; +import org.breedinginsight.services.ProgramService; +import org.breedinginsight.services.exceptions.DoesNotExistException; import javax.inject.Inject; import javax.inject.Singleton; +import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.TimeUnit; import static org.brapi.v2.model.BrAPIWSMIMEDataTypes.APPLICATION_JSON; @@ -48,16 +56,19 @@ public class BrAPIDAOUtil { private final Duration searchTimeout; private final int pageSize; private final int postGroupSize; + private final ProgramService programService; @Inject public BrAPIDAOUtil(@Property(name = "brapi.search.wait-time") int searchWaitTime, @Property(name = "brapi.read-timeout") Duration searchTimeout, @Property(name = "brapi.page-size") int pageSize, - @Property(name = "brapi.post-group-size") int postGroupSize) { + @Property(name = "brapi.post-group-size") int postGroupSize, + ProgramService programService) { this.searchWaitTime = searchWaitTime; this.searchTimeout = searchTimeout; this.pageSize = pageSize; this.postGroupSize = postGroupSize; + this.programService = programService; } public List search(Function, Optional>>> searchMethod, @@ -366,4 +377,32 @@ public List post(List brapiObjects, return post(brapiObjects, null, postMethod, null); } + public void makeCall(Request brapiRequest) throws ApiException { + OkHttpClient client = new OkHttpClient.Builder() + .readTimeout(5, TimeUnit.MINUTES) + .build(); + try { + client.newCall(brapiRequest).execute(); + } catch (IOException e) { + log.error("Error calling BrAPI Service", e); + throw new ApiException("Error calling BrAPI Service"); + } + } + + public String getProgramBrAPIBaseUrl(UUID programId) { + ProgramBrAPIEndpoints programBrAPIEndpoints; + try { + programBrAPIEndpoints = programService.getBrapiEndpoints(programId); + } catch (DoesNotExistException e) { + throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); + } + + if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { + log.error("Program: " + programId + " is missing BrAPI URL config"); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); + } + var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); + programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; + return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; + } } diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index e29ce7e24..84f7385b9 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -49,6 +49,7 @@ import java.time.OffsetDateTime; import java.util.*; +import static io.micronaut.http.HttpRequest.DELETE; import static io.micronaut.http.HttpRequest.GET; import static org.junit.jupiter.api.Assertions.*; @@ -187,4 +188,35 @@ public void getAllListsSuccess() { } } } + + + @Test + @SneakyThrows + public void deleteListSuccess() { + // A GET request to the brapi/v2/lists endpoint with no query params should return all lists. + Flowable> getCall = client.exchange( + GET(String.format("/programs/%s/brapi/v2/lists", program.getId().toString())) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + + // Ensure 200 OK response for fetching lists. + HttpResponse beforeResponse = getCall.blockingFirst(); + assertEquals(HttpStatus.OK, beforeResponse.getStatus()); + + // Parse result. + JsonObject beforeResult = JsonParser.parseString(beforeResponse.body()).getAsJsonObject().getAsJsonObject("result"); + JsonArray beforeData = beforeResult.getAsJsonArray("data"); + + // A DELETE request to the brapi/v2/lists/ endpoint with no query params should delete the list. + String deleteListDbId = beforeData.get(0).getAsJsonObject().get("listDbId").getAsString(); + Flowable> deleteCall = client.exchange( + DELETE(String.format("/programs/%s/brapi/v2/lists/%s", program.getId().toString(), deleteListDbId)) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + + // Ensure 204 NO_CONTENT response for deleting a list. + HttpResponse deleteResponse = deleteCall.blockingFirst(); + assertEquals(HttpStatus.NO_CONTENT, deleteResponse.getStatus()); + + } } diff --git a/src/test/java/org/breedinginsight/daos/BrAPIDAOUtilUnitTest.java b/src/test/java/org/breedinginsight/daos/BrAPIDAOUtilUnitTest.java index 0cb51387e..427ed6d88 100644 --- a/src/test/java/org/breedinginsight/daos/BrAPIDAOUtilUnitTest.java +++ b/src/test/java/org/breedinginsight/daos/BrAPIDAOUtilUnitTest.java @@ -1,6 +1,7 @@ package org.breedinginsight.daos; import com.google.gson.JsonObject; +import io.micronaut.test.annotation.MockBean; import lombok.SneakyThrows; import org.apache.commons.lang3.tuple.Pair; import org.brapi.client.v2.ApiResponse; @@ -11,6 +12,7 @@ import org.brapi.v2.model.germ.response.BrAPIGermplasmListResponseResult; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.model.Program; +import org.breedinginsight.services.ProgramService; import org.breedinginsight.utilities.BrAPIDAOUtil; import org.breedinginsight.utilities.Utilities; import org.junit.jupiter.api.BeforeEach; @@ -21,6 +23,8 @@ import java.time.temporal.ChronoUnit; import java.util.*; +import static org.mockito.Mockito.mock; + @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class BrAPIDAOUtilUnitTest { @@ -30,6 +34,10 @@ public class BrAPIDAOUtilUnitTest { private Program testProgram; private List paginatedGermplasm; private BrAPIGermplasmSearchRequest germplasmSearch; + @MockBean(ProgramService.class) + ProgramService programService() { + return mock(ProgramService.class); + } public Integer fetchPaginatedGermplasm(int page, int pageSize) { paginatedGermplasm = new ArrayList<>(); @@ -62,7 +70,7 @@ public ApiResponse, Optional Date: Mon, 2 Dec 2024 21:02:43 -0500 Subject: [PATCH 6/9] update ListControllerIntegrationTest --- .../brapi/v2/BrAPIListController.java | 7 +++---- .../brapi/v2/dao/BrAPIListDAO.java | 7 +++++-- .../brapi/v2/services/BrAPIListService.java | 7 +++++-- .../utilities/BrAPIDAOUtil.java | 21 +++++++++++++++---- .../v2/ListControllerIntegrationTest.java | 13 ++++++++++++ 5 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 3cb73dbae..3a2672252 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -93,7 +93,7 @@ public HttpResponse>> getLists( @Delete("/lists/{listDbId}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - public HttpResponse>> deleteListById( + public HttpResponse deleteListById( @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, HttpRequest request @@ -104,11 +104,10 @@ public HttpResponse>> deleteListById( hardDelete = "true".equals(paramValue); } try { - brapiListService.deleteBrAPIList(listDbId, programId, hardDelete); - return HttpResponse.status(HttpStatus.NO_CONTENT); + return brapiListService.deleteBrAPIList(listDbId, programId, hardDelete); } catch (Exception e) { log.info(e.getMessage(), e); - return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving germplasm list records"); + return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error deleting germplasm list"); } } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index 11e079842..6a1784389 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -17,6 +17,7 @@ package org.breedinginsight.brapi.v2.dao; +import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; import io.micronaut.http.exceptions.HttpStatusException; import lombok.extern.slf4j.Slf4j; @@ -37,6 +38,8 @@ import org.brapi.v2.model.core.response.BrAPIListsListResponse; import org.brapi.v2.model.core.response.BrAPIListsListResponseResult; import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; +import org.breedinginsight.api.model.v1.response.DataResponse; +import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; @@ -212,7 +215,7 @@ public List createBrAPILists(List brapiLi throw new ApiException("No response after creating list"); } - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + public HttpResponse deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { // TODO: Switch to using the ListsApi from the BrAPI client library once the delete endpoints are merged into it var programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(programId); var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); @@ -223,7 +226,7 @@ public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) .addHeader("Content-Type", "application/json") .build(); - brAPIDAOUtil.makeCall(brapiRequest); + return brAPIDAOUtil.makeCall(brapiRequest); } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java index 1ee77e310..65be8f5cd 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java @@ -1,6 +1,7 @@ package org.breedinginsight.brapi.v2.services; import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpResponse; import lombok.extern.slf4j.Slf4j; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.BrAPIExternalReference; @@ -8,6 +9,8 @@ import org.brapi.v2.model.core.BrAPIListTypes; import org.brapi.v2.model.core.request.BrAPIListSearchRequest; import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; +import org.breedinginsight.api.model.v1.response.DataResponse; +import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; @@ -90,7 +93,7 @@ public List getListSummariesByTypeAndXref( return programLists; } - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { - listDAO.deleteBrAPIList(listDbId, programId, hardDelete); + public HttpResponse deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + return listDAO.deleteBrAPIList(listDbId, programId, hardDelete); } } diff --git a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java index 075d6bb2b..15d98e1c3 100644 --- a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java +++ b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java @@ -18,6 +18,7 @@ package org.breedinginsight.utilities; import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; import io.micronaut.http.exceptions.HttpStatusException; import io.micronaut.http.server.exceptions.InternalServerException; @@ -25,11 +26,13 @@ import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Request; +import okhttp3.Response; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.brapi.client.v2.ApiResponse; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.*; +import org.breedinginsight.api.model.v1.response.DataResponse; import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.model.ProgramBrAPIEndpoints; @@ -377,15 +380,25 @@ public List post(List brapiObjects, return post(brapiObjects, null, postMethod, null); } - public void makeCall(Request brapiRequest) throws ApiException { + public HttpResponse makeCall(Request brapiRequest) { + // Create OkHttpClient with timeout OkHttpClient client = new OkHttpClient.Builder() .readTimeout(5, TimeUnit.MINUTES) .build(); - try { - client.newCall(brapiRequest).execute(); + + try (Response brapiResponse = client.newCall(brapiRequest).execute()) { + int statusCode = brapiResponse.code(); + + if (!brapiResponse.isSuccessful()) { + return HttpResponse.status(HttpStatus.valueOf(statusCode)); + } + + String responseBody = brapiResponse.body() != null ? brapiResponse.body().string() : ""; + return HttpResponse.status(HttpStatus.valueOf(statusCode), responseBody); + } catch (IOException e) { log.error("Error calling BrAPI Service", e); - throw new ApiException("Error calling BrAPI Service"); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Error calling BrAPI Service"); } } diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index 84f7385b9..8aeea6a7c 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -23,6 +23,7 @@ import io.micronaut.http.HttpStatus; import io.micronaut.http.client.RxHttpClient; import io.micronaut.http.client.annotation.Client; +import io.micronaut.http.client.exceptions.HttpClientResponseException; import io.micronaut.http.netty.cookies.NettyCookie; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import io.reactivex.Flowable; @@ -191,6 +192,7 @@ public void getAllListsSuccess() { @Test + @Disabled("Delete-list tests are temporarily disabled until the delete endpoints are merged into the develop branch of breedinginsight:brapi-java-test-server") @SneakyThrows public void deleteListSuccess() { // A GET request to the brapi/v2/lists endpoint with no query params should return all lists. @@ -218,5 +220,16 @@ public void deleteListSuccess() { HttpResponse deleteResponse = deleteCall.blockingFirst(); assertEquals(HttpStatus.NO_CONTENT, deleteResponse.getStatus()); + + // A DELETE request to the brapi/v2/lists/ endpoint with invalid dbId. + Flowable> invalidDeleteCall = client.exchange( + DELETE(String.format("/programs/%s/brapi/v2/lists/%s", program.getId().toString(), "NOT-VALID-DBID")) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + + // Ensure 404 NOT_FOUND response for requesting to delete a non-existant list. + HttpResponse invalidDeleteResponse = invalidDeleteCall.blockingFirst(); + assertEquals(HttpStatus.NOT_FOUND, invalidDeleteResponse.getStatus()); + } } From 7fe03bf6f10b46b859974e52c14e47ddb4394348 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:18:37 -0500 Subject: [PATCH 7/9] update TODO comment --- .../java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index 6a1784389..e73b6836a 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -216,7 +216,7 @@ public List createBrAPILists(List brapiLi } public HttpResponse deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { - // TODO: Switch to using the ListsApi from the BrAPI client library once the delete endpoints are merged into it + // TODO: Switch to using the ListsApi from the BrAPI client library once the delete endpoints from BI-2397 are merged. var programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(programId); var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); From 710e677da016c2b7a8d8b365f7ffe63cfb464906 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:30:28 -0500 Subject: [PATCH 8/9] incorporate PR feedback --- .../brapi/v2/BrAPIGermplasmController.java | 2 +- .../brapi/v2/BrAPIListController.java | 5 +++++ .../v2/model/request/query/GermplasmQuery.java | 2 +- .../utilities/response/ResponseUtils.java | 1 + .../v2/ListControllerIntegrationTest.java | 18 ++++++++++++++---- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java index 94cf9c9fc..84a6a17f0 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java @@ -210,7 +210,7 @@ public HttpResponse>>> getGermplasm( } // Fetch all germplasm in the program unless a list id is supplied to return only germplasm in that collection - List germplasm = queryParams.getList() == null ? germplasmService.getGermplasm(programId) : germplasmService.getGermplasmByList(programId, queryParams.getList());; + List germplasm = queryParams.getListDbId() == null ? germplasmService.getGermplasm(programId) : germplasmService.getGermplasmByList(programId, queryParams.getListDbId()); SearchRequest searchRequest = queryParams.constructSearchRequest(); return ResponseUtils.getBrapiQueryResponse(germplasm, germplasmQueryMapper, queryParams, searchRequest); } catch (ApiException e) { diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 3a2672252..10528e298 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -7,7 +7,9 @@ import io.micronaut.http.annotation.*; import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; +import io.reactivex.rxjava3.core.Single; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.core.BrAPIListSummary; import org.brapi.v2.model.core.BrAPIListTypes; @@ -25,11 +27,14 @@ import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.response.ResponseUtils; import org.breedinginsight.utilities.response.mappers.ListQueryMapper; +import reactor.core.publisher.Mono; +import reactor.util.function.Tuples; import javax.inject.Inject; import javax.validation.Valid; import javax.validation.Validator; import java.util.List; +import java.util.Optional; import java.util.UUID; @Slf4j diff --git a/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java b/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java index 92752fad6..81039b7ef 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java +++ b/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java @@ -28,7 +28,7 @@ public class GermplasmQuery extends BrapiQuery { private String dateDisplayFormat; // The list id used to get a collection of germplasm - private String list; + private String listDbId; public SearchRequest constructSearchRequest() { List filters = new ArrayList<>(); diff --git a/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java b/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java index 4cee82f99..cb8791fae 100644 --- a/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java +++ b/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java @@ -18,6 +18,7 @@ package org.breedinginsight.utilities.response; import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpResponseFactory; import io.micronaut.http.HttpStatus; import io.micronaut.http.MediaType; import io.micronaut.http.exceptions.HttpStatusException; diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index 8aeea6a7c..2d421d306 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -42,6 +42,7 @@ import org.breedinginsight.model.Program; import org.breedinginsight.services.SpeciesService; import org.jooq.DSLContext; +import org.junit.Rule; import org.junit.jupiter.api.*; import javax.inject.Inject; @@ -152,6 +153,7 @@ public void setup() { @Test @SneakyThrows + @Order(1) public void getAllListsSuccess() { // A GET request to the brapi/v2/lists endpoint with no query params should return all lists. Flowable> call = client.exchange( @@ -159,6 +161,12 @@ public void getAllListsSuccess() { .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class ); + // Collect all responses + List> responses = call.toList().blockingGet(); + + // Ensure we got a response + assertFalse(responses.isEmpty(), "No response received"); + // Ensure 200 OK response. HttpResponse response = call.blockingFirst(); assertEquals(HttpStatus.OK, response.getStatus()); @@ -192,8 +200,8 @@ public void getAllListsSuccess() { @Test - @Disabled("Delete-list tests are temporarily disabled until the delete endpoints are merged into the develop branch of breedinginsight:brapi-java-test-server") @SneakyThrows + @Order(2) public void deleteListSuccess() { // A GET request to the brapi/v2/lists endpoint with no query params should return all lists. Flowable> getCall = client.exchange( @@ -228,8 +236,10 @@ public void deleteListSuccess() { ); // Ensure 404 NOT_FOUND response for requesting to delete a non-existant list. - HttpResponse invalidDeleteResponse = invalidDeleteCall.blockingFirst(); - assertEquals(HttpStatus.NOT_FOUND, invalidDeleteResponse.getStatus()); - + try { + invalidDeleteCall.blockingFirst(); + } catch(HttpClientResponseException e) { + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } } } From fe5f667810c23f1608f0ad7fc0c24171ac165439 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Fri, 10 Jan 2025 15:12:51 -0500 Subject: [PATCH 9/9] fix germplasm integration test --- .../brapi/v2/GermplasmControllerIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java index 7c80482d9..32781dd8c 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java @@ -245,7 +245,7 @@ public void getAllGermplasmByListSuccess() { String germplasmListDbId = fetchGermplasmListDbId(programId); // Build the endpoint to get germplasm by germplasm list. - String endpoint = String.format("/programs/%s/brapi/v2/germplasm?list=%s", programId, germplasmListDbId); + String endpoint = String.format("/programs/%s/brapi/v2/germplasm?listDbId=%s", programId, germplasmListDbId); // Get germplasm by list. Flowable> call = client.exchange(