From 30518eab48555aac6ae4aadd0152f5973c42a91c Mon Sep 17 00:00:00 2001 From: timparsons Date: Mon, 18 Sep 2023 18:29:23 -0400 Subject: [PATCH 01/20] [BI-1921] Adding OIDC endpoints Stubbed out methods needed for Field Book to fetch data --- .../v1/controller}/ExperimentController.java | 55 +------ .../api/v1/controller/TokenController.java | 2 +- .../auth/OIDCDiscoveryController.java | 71 +++++++++ ...ler.java => BrAPIGermplasmController.java} | 4 +- .../brapi/v2/BrAPIImagesController.java | 90 ++++++++++++ ...ntroller.java => BrAPIListController.java} | 6 +- .../v2/BrAPIObservationUnitController.java | 118 +++++++++++++++ .../BrAPIObservationVariableController.java | 93 ++++++++++++ .../brapi/v2/BrAPIObservationsController.java | 136 ++++++++++++++++++ .../brapi/v2/BrAPIProgramsController.java | 106 ++++++++++++++ ...oller.java => BrAPIStudiesController.java} | 34 ++++- .../brapi/v2/BrAPITrialsController.java | 101 +++++++++++++ .../brapi/v2/BrAPIV2Controller.java | 136 +++++++++++++++--- .../brapi/v2/ServiceBuilder.java | 80 +++++++++++ .../response/mappers/StudyQueryMapper.java | 7 + 15 files changed, 960 insertions(+), 79 deletions(-) rename src/main/java/org/breedinginsight/{brapi/v2 => api/v1/controller}/ExperimentController.java (63%) create mode 100644 src/main/java/org/breedinginsight/api/v1/controller/auth/OIDCDiscoveryController.java rename src/main/java/org/breedinginsight/brapi/v2/{GermplasmController.java => BrAPIGermplasmController.java} (98%) create mode 100644 src/main/java/org/breedinginsight/brapi/v2/BrAPIImagesController.java rename src/main/java/org/breedinginsight/brapi/v2/{ListController.java => BrAPIListController.java} (96%) create mode 100644 src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java create mode 100644 src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java create mode 100644 src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java create mode 100644 src/main/java/org/breedinginsight/brapi/v2/BrAPIProgramsController.java rename src/main/java/org/breedinginsight/brapi/v2/{StudyController.java => BrAPIStudiesController.java} (71%) create mode 100644 src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java create mode 100644 src/main/java/org/breedinginsight/brapi/v2/ServiceBuilder.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/ExperimentController.java b/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java similarity index 63% rename from src/main/java/org/breedinginsight/brapi/v2/ExperimentController.java rename to src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java index c819cc10c..7c64827fe 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/ExperimentController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java @@ -1,4 +1,4 @@ -package org.breedinginsight.brapi.v2; +package org.breedinginsight.api.v1.controller; import io.micronaut.http.HttpHeaders; import io.micronaut.http.HttpResponse; @@ -9,28 +9,21 @@ import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; -import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.v2.model.core.BrAPITrial; 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.v2.model.request.query.ExperimentExportQuery; -import org.breedinginsight.brapi.v2.model.request.query.ExperimentQuery; import org.breedinginsight.brapi.v2.services.BrAPITrialService; import org.breedinginsight.model.Dataset; import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.Program; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.exceptions.DoesNotExistException; -import org.breedinginsight.utilities.response.ResponseUtils; import org.breedinginsight.utilities.response.mappers.ExperimentQueryMapper; + import javax.inject.Inject; import javax.validation.Valid; -import java.util.*; +import java.util.UUID; @Slf4j @Controller @@ -47,48 +40,6 @@ public ExperimentController(BrAPITrialService experimentService, ExperimentQuery this.programService = programService; } - @Get("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/trials{?queryParams*}") - @Produces(MediaType.APPLICATION_JSON) - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse>>> getExperiments( - @PathVariable("programId") UUID programId, - @QueryValue @QueryValid(using = ExperimentQueryMapper.class) @Valid ExperimentQuery queryParams) { - try { - log.debug("fetching trials for program: " + programId); - - List experiments = experimentService.getExperiments(programId); - SearchRequest searchRequest = queryParams.constructSearchRequest(); - return ResponseUtils.getBrapiQueryResponse(experiments, experimentQueryMapper, queryParams, searchRequest); - } catch (ApiException e) { - log.info(e.getMessage(), e); - return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving experiments"); - } catch (DoesNotExistException e) { - log.info(e.getMessage(), e); - return HttpResponse.status(HttpStatus.NOT_FOUND, e.getMessage()); - } - } - - @Get("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/trials/{trialId}") - @Produces(MediaType.APPLICATION_JSON) - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse> getExperimentById( - @PathVariable("programId") UUID programId, - @PathVariable("trialId") UUID trialId, - @QueryValue(defaultValue = "false") Boolean stats){ - try { - String logMsg = "fetching trial id:" + trialId +" for program: " + programId; - if(stats){ - logMsg += " with stats"; - } - log.debug(logMsg); - Response response = new Response<>(experimentService.getTrialDataByUUID(programId, trialId, stats)); - return HttpResponse.ok(response); - } catch (DoesNotExistException e) { - log.info(e.getMessage(), e); - return HttpResponse.status(HttpStatus.NOT_FOUND, e.getMessage()); - } - } - @Get("/${micronaut.bi.api.version}/programs/{programId}/experiments/{experimentId}/export{?queryParams*}") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) @Produces(value={"text/csv", "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/octet-stream"}) diff --git a/src/main/java/org/breedinginsight/api/v1/controller/TokenController.java b/src/main/java/org/breedinginsight/api/v1/controller/TokenController.java index 9cae400fa..ca6053be7 100644 --- a/src/main/java/org/breedinginsight/api/v1/controller/TokenController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/TokenController.java @@ -61,7 +61,7 @@ public HttpResponse apiToken(@QueryValue @Nullable String returnUrl) { ApiToken apiToken = token.get(); if(returnUrl != null) { - if(StringUtils.trim(returnUrl).isEmpty()) { + if(StringUtils.trim(returnUrl).isEmpty() || "undefined".equalsIgnoreCase(returnUrl)) { return HttpResponse.badRequest("returnUrl cannot be blank"); } URI location = UriBuilder.of(returnUrl) diff --git a/src/main/java/org/breedinginsight/api/v1/controller/auth/OIDCDiscoveryController.java b/src/main/java/org/breedinginsight/api/v1/controller/auth/OIDCDiscoveryController.java new file mode 100644 index 000000000..4d58da165 --- /dev/null +++ b/src/main/java/org/breedinginsight/api/v1/controller/auth/OIDCDiscoveryController.java @@ -0,0 +1,71 @@ +/* + * 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.api.v1.controller.auth; + +import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Produces; +import io.micronaut.security.annotation.Secured; +import io.micronaut.security.rules.SecurityRule; +import org.breedinginsight.dao.db.tables.pojos.ProgramEntity; +import org.breedinginsight.daos.ProgramDAO; + +import javax.inject.Inject; +import java.util.UUID; + +@Controller +@Secured(SecurityRule.IS_ANONYMOUS) +public class OIDCDiscoveryController { + + private static final String OIDC_CONFIG = """ + { + "issuer": "%s", + "authorization_endpoint": "%s/programs/%s/brapi/authorize", + "jwks_uri": "", + "token_endpoint": "", + "grant_types_supported": ["implicit"], + "response_types_supported": ["token"], + "subject_types_supported": ["public"], + "id_token_signing_alg_values_supported": [] + } + """; + + private final String webUrl; + private final ProgramDAO programDAO; + + @Inject + public OIDCDiscoveryController(@Property(name="web.base-url") String webUrl, ProgramDAO programDAO) { + this.webUrl = webUrl; + this.programDAO = programDAO; + } + + + @Get("/${micronaut.bi.api.version}/programs/{programId}/.well-known/openid-configuration") + @Produces(MediaType.APPLICATION_JSON) + public HttpResponse getOpenIdConfig(UUID programId) { + ProgramEntity programEntity = programDAO.fetchOneById(programId); + if(programEntity != null) { + return HttpResponse.ok(String.format(OIDC_CONFIG, webUrl, webUrl, programId)); + } + + return HttpResponse.notFound("unknown program"); + } +} diff --git a/src/main/java/org/breedinginsight/brapi/v2/GermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java similarity index 98% rename from src/main/java/org/breedinginsight/brapi/v2/GermplasmController.java rename to src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java index 568e58d99..edc89b5f0 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/GermplasmController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java @@ -51,7 +51,7 @@ @Slf4j @Controller("/${micronaut.bi.api.version}") @Secured(SecurityRule.IS_AUTHENTICATED) -public class GermplasmController { +public class BrAPIGermplasmController { private final BrAPIGermplasmService germplasmService; private final GermplasmQueryMapper germplasmQueryMapper; @@ -64,7 +64,7 @@ public class GermplasmController { @Inject - public GermplasmController(BrAPIGermplasmService germplasmService, GermplasmQueryMapper germplasmQueryMapper, ProgramDAO programDAO, BrAPIGermplasmDAO germplasmDAO, GenotypeService genoService, BrAPIEndpointProvider brAPIEndpointProvider) { + public BrAPIGermplasmController(BrAPIGermplasmService germplasmService, GermplasmQueryMapper germplasmQueryMapper, ProgramDAO programDAO, BrAPIGermplasmDAO germplasmDAO, GenotypeService genoService, BrAPIEndpointProvider brAPIEndpointProvider) { this.germplasmService = germplasmService; this.germplasmQueryMapper = germplasmQueryMapper; this.programDAO = programDAO; diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIImagesController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIImagesController.java new file mode 100644 index 000000000..1893d10d0 --- /dev/null +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIImagesController.java @@ -0,0 +1,90 @@ +/* + * 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.annotation.*; +import io.micronaut.security.annotation.Secured; +import io.micronaut.security.rules.SecurityRule; +import lombok.extern.slf4j.Slf4j; +import org.brapi.v2.model.pheno.BrAPIImage; +import org.breedinginsight.api.auth.ProgramSecured; +import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; + +import java.util.List; +import java.util.UUID; + +@Slf4j +@Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) +@Secured(SecurityRule.IS_AUTHENTICATED) +public class BrAPIImagesController { + /* + TODO + - POST images + - PUT imagesImageDbIdImagecontent + - PUT imagesImageDbIdPut + */ + @Get("/images") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse imagesGet(@PathVariable("programId") UUID programId, + @QueryValue("imageDbId") String imageDbId, + @QueryValue("imageName") String imageName, + @QueryValue("observationUnitDbId") String observationUnitDbId, + @QueryValue("observationDbId") String observationDbId, + @QueryValue("descriptiveOntologyTerm") String descriptiveOntologyTerm, + @QueryValue("commonCropName") String commonCropName, + @QueryValue("programDbId") String programDbId, + @QueryValue("externalReferenceID") String externalReferenceID, + @QueryValue("externalReferenceId") String externalReferenceId, + @QueryValue("externalReferenceSource") String externalReferenceSource, + @QueryValue("page") Integer page, + @QueryValue("pageSize") Integer pageSize) { + return HttpResponse.notFound(); + } + + @Get("/images/{imageDbId}") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse imagesImageDbIdGet(@PathVariable("programId") UUID programId, + @PathVariable("imageDbId") String imageDbId) { + return HttpResponse.notFound(); + } + + @Put("/images/{imageDbId}/imagecontent") + @Consumes({"image/_*"}) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse imagesImageDbIdImagecontentPut(@PathVariable("programId") UUID programId, + @PathVariable("imageDbId") String imageDbId, + Object body) { + return HttpResponse.notFound(); + } + + @Put("/images/{imageDbId}") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse imagesImageDbIdPut(@PathVariable("programId") UUID programId, + @PathVariable("imageDbId") String imageDbId, + BrAPIImage body) { + return HttpResponse.notFound(); + } + + @Post("/images") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse imagesPost(@PathVariable("programId") UUID programId, List body) { + return HttpResponse.notFound(); + } +} diff --git a/src/main/java/org/breedinginsight/brapi/v2/ListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java similarity index 96% rename from src/main/java/org/breedinginsight/brapi/v2/ListController.java rename to src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 7b33629ef..115448d58 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/ListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -50,15 +50,15 @@ @Slf4j @Controller @Secured(SecurityRule.IS_AUTHENTICATED) -public class ListController { +public class BrAPIListController { private final ProgramService programService; private final BrAPIListService listService; private final ListQueryMapper listQueryMapper; @Inject - public ListController(ProgramService programService, BrAPIListService listService, - ListQueryMapper listQueryMapper) { + public BrAPIListController(ProgramService programService, BrAPIListService listService, + ListQueryMapper listQueryMapper) { this.programService = programService; this.listService = listService; this.listQueryMapper = listQueryMapper; diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java new file mode 100644 index 000000000..96b91df36 --- /dev/null +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java @@ -0,0 +1,118 @@ +/* + * 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.annotation.*; +import io.micronaut.security.annotation.Secured; +import io.micronaut.security.rules.SecurityRule; +import lombok.extern.slf4j.Slf4j; +import org.brapi.v2.model.pheno.BrAPIObservationUnit; +import org.breedinginsight.api.auth.ProgramSecured; +import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Slf4j +@Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) +@Secured(SecurityRule.IS_AUTHENTICATED) +public class BrAPIObservationUnitController { + + /* + TODO + - GET observationLevels + - GET observationUnits + */ + @Get("/observationunits") + public HttpResponse observationunitsGet(@PathVariable("programId") UUID programId, + @QueryValue("observationUnitDbId") String observationUnitDbId, + @QueryValue("observationUnitName") String observationUnitName, + @QueryValue("locationDbId") String locationDbId, + @QueryValue("seasonDbId") String seasonDbId, + @QueryValue("includeObservations") Boolean includeObservations, + @QueryValue("observationUnitLevelName") String observationUnitLevelName, + @QueryValue("observationUnitLevelOrder") String observationUnitLevelOrder, + @QueryValue("observationUnitLevelCode") String observationUnitLevelCode, + @QueryValue("observationUnitLevelRelationshipName") String observationUnitLevelRelationshipName, + @QueryValue("observationUnitLevelRelationshipOrder") String observationUnitLevelRelationshipOrder, + @QueryValue("observationUnitLevelRelationshipCode") String observationUnitLevelRelationshipCode, + @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId, + @QueryValue("commonCropName") String commonCropName, + @QueryValue("programDbId") String programDbId, + @QueryValue("trialDbId") String trialDbId, + @QueryValue("studyDbId") String studyDbId, + @QueryValue("germplasmDbId") String germplasmDbId, + @QueryValue("externalReferenceID") String externalReferenceID, + @QueryValue("externalReferenceId") String externalReferenceId, + @QueryValue("externalReferenceSource") String externalReferenceSource, + @QueryValue("page") Integer page, + @QueryValue("pageSize") Integer pageSize) { + return HttpResponse.notFound(); + } + + @Get("/observationunits/{observationUnitDbId}") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse observationunitsObservationUnitDbIdGet(@PathVariable("programId") UUID programId, @PathVariable("observationUnitDbId") String observationUnitDbId) { + return HttpResponse.notFound(); + } + + @Put("/observationunits/{observationUnitDbId}") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse observationunitsObservationUnitDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("observationUnitDbId") String observationUnitDbId, BrAPIObservationUnit body) { + return HttpResponse.notFound(); + } + + @Post("/observationunits") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse observationunitsPost(@PathVariable("programId") UUID programId, List body) { + return HttpResponse.notFound(); + } + + @Put("/observationunits") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse observationunitsPut(@PathVariable("programId") UUID programId, Map body) { + return HttpResponse.notFound(); + } + + @Get("/observationunits/table") + @Produces({"application/json", "text/csv", "text/tsv"}) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse observationunitsTableGet(@PathVariable("programId") UUID programId, + @Header("Accept") String accept, + @QueryValue("observationUnitDbId") String observationUnitDbId, + @QueryValue("observationVariableDbId") String observationVariableDbId, + @QueryValue("locationDbId") String locationDbId, + @QueryValue("seasonDbId") String seasonDbId, + @QueryValue("observationLevel") String observationLevel, + @QueryValue("programDbId") String programDbId, + @QueryValue("trialDbId") String trialDbId, + @QueryValue("studyDbId") String studyDbId, + @QueryValue("germplasmDbId") String germplasmDbId, + @QueryValue("observationUnitLevelName") String observationUnitLevelName, + @QueryValue("observationUnitLevelOrder") String observationUnitLevelOrder, + @QueryValue("observationUnitLevelCode") String observationUnitLevelCode, + @QueryValue("observationUnitLevelRelationshipName") String observationUnitLevelRelationshipName, + @QueryValue("observationUnitLevelRelationshipOrder") String observationUnitLevelRelationshipOrder, + @QueryValue("observationUnitLevelRelationshipCode") String observationUnitLevelRelationshipCode, + @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId) { + return HttpResponse.notFound(); + } +} diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java new file mode 100644 index 000000000..29dfa9e0b --- /dev/null +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java @@ -0,0 +1,93 @@ +/* + * 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.annotation.*; +import io.micronaut.security.annotation.Secured; +import io.micronaut.security.rules.SecurityRule; +import lombok.extern.slf4j.Slf4j; +import org.brapi.v2.model.pheno.BrAPIObservationVariable; +import org.breedinginsight.api.auth.ProgramSecured; +import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; + +import java.util.List; +import java.util.UUID; + +@Slf4j +@Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) +@Secured(SecurityRule.IS_AUTHENTICATED) +public class BrAPIObservationVariableController { + /* + TODO + - GET /variables + */ + + @Get("/variables") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse variablesGet(@PathVariable("programId") UUID programId, + @QueryValue("observationVariableDbId") String observationVariableDbId, + @QueryValue("observationVariableName") String observationVariableName, + @QueryValue("observationVariablePUI") String observationVariablePUI, + @QueryValue("traitClass") String traitClass, + @QueryValue("methodDbId") String methodDbId, + @QueryValue("methodName") String methodName, + @QueryValue("methodPUI") String methodPUI, + @QueryValue("scaleDbId") String scaleDbId, + @QueryValue("scaleName") String scaleName, + @QueryValue("scalePUI") String scalePUI, + @QueryValue("traitDbId") String traitDbId, + @QueryValue("traitName") String traitName, + @QueryValue("traitPUI") String traitPUI, + @QueryValue("ontologyDbId") String ontologyDbId, + @QueryValue("commonCropName") String commonCropName, + @QueryValue("programDbId") String programDbId, + @QueryValue("trialDbId") String trialDbId, + @QueryValue("studyDbId") String studyDbId, + @QueryValue("externalReferenceID") String externalReferenceID, + @QueryValue("externalReferenceId") String externalReferenceId, + @QueryValue("externalReferenceSource") String externalReferenceSource, + @QueryValue("page") Integer page, + @QueryValue("pageSize") Integer pageSize) { + return HttpResponse.notFound(); + } + + @Get("/variables/{observationVariableDbId}") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse variablesObservationVariableDbIdGet(@PathVariable("programId") UUID programId, + @PathVariable("observationVariableDbId") String observationVariableDbId) { + return HttpResponse.notFound(); + } + + @Put("/variables/{observationVariableDbId}") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse variablesObservationVariableDbIdPut(@PathVariable("programId") UUID programId, + @PathVariable("observationVariableDbId") String observationVariableDbId, + BrAPIObservationVariable body) { + //DO NOT IMPLEMENT - Users are only able to update traits via the DeltaBreed UI + return HttpResponse.notFound(); + } + + @Post("/variables") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse variablesPost(@PathVariable("programId") UUID programId, List body) { + //DO NOT IMPLEMENT - Users are only able to create new traits via the DeltaBreed UI + return HttpResponse.notFound(); + } +} diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java new file mode 100644 index 000000000..cd35d9d5b --- /dev/null +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java @@ -0,0 +1,136 @@ +/* + * 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.annotation.*; +import io.micronaut.security.annotation.Secured; +import io.micronaut.security.rules.SecurityRule; +import lombok.extern.slf4j.Slf4j; +import org.brapi.v2.model.pheno.BrAPIObservation; +import org.breedinginsight.api.auth.ProgramSecured; +import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Slf4j +@Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) +@Secured(SecurityRule.IS_AUTHENTICATED) +public class BrAPIObservationsController { + + @Get("/observations") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse observationsGet(@PathVariable("programId") UUID programId, + @QueryValue("observationDbId") String observationDbId, + @QueryValue("observationUnitDbId") String observationUnitDbId, + @QueryValue("observationVariableDbId") String observationVariableDbId, + @QueryValue("locationDbId") String locationDbId, + @QueryValue("seasonDbId") String seasonDbId, + @QueryValue("observationTimeStampRangeStart") Date observationTimeStampRangeStart, + @QueryValue("observationTimeStampRangeEnd") Date observationTimeStampRangeEnd, + @QueryValue("observationUnitLevelName") String observationUnitLevelName, + @QueryValue("observationUnitLevelOrder") String observationUnitLevelOrder, + @QueryValue("observationUnitLevelCode") String observationUnitLevelCode, + @QueryValue("observationUnitLevelRelationshipName") String observationUnitLevelRelationshipName, + @QueryValue("observationUnitLevelRelationshipOrder") String observationUnitLevelRelationshipOrder, + @QueryValue("observationUnitLevelRelationshipCode") String observationUnitLevelRelationshipCode, + @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId, + @QueryValue("commonCropName") String commonCropName, + @QueryValue("programDbId") String programDbId, + @QueryValue("trialDbId") String trialDbId, + @QueryValue("studyDbId") String studyDbId, + @QueryValue("germplasmDbId") String germplasmDbId, + @QueryValue("externalReferenceID") String externalReferenceID, + @QueryValue("externalReferenceId") String externalReferenceId, + @QueryValue("externalReferenceSource") String externalReferenceSource, + @QueryValue("page") Integer page, + @QueryValue("pageSize") Integer pageSize) { + return HttpResponse.notFound(); + } + + @Get("/observations/{observationDbId}") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse observationsObservationDbIdGet(@PathVariable("programId") UUID programId, + @PathVariable("observationDbId") String observationDbId) { + return HttpResponse.notFound(); + } + + @Put("/observations/{observationDbId}") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse observationsObservationDbIdPut(@PathVariable("programId") UUID programId, + @PathVariable("observationDbId") String observationDbId, + BrAPIObservation body) { + /* + DO NOT IMPLEMENT - users must create observations via file upload + TODO identify how observations uploaded via BrAPI will be separated from curated observations + */ + return HttpResponse.notFound(); + } + + @Post("/observations") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse observationsPost(@PathVariable("programId") UUID programId, List body) { + /* + DO NOT IMPLEMENT - users must create observations via file upload + TODO identify how observations uploaded via BrAPI will be separated from curated observations + */ + return HttpResponse.notFound(); + } + + @Put("/observations") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse observationsPut(@PathVariable("programId") UUID programId, Map body) { + /* + DO NOT IMPLEMENT - users must create observations via file upload + TODO identify how observations uploaded via BrAPI will be separated from curated observations + */ + return HttpResponse.notFound(); + } + + @Get("/observations/table") + @Produces({"application/json", "text/csv", "text/tsv"}) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse observationsTableGet(@PathVariable("programId") UUID programId, + @Header("Accept") String accept, + @QueryValue("observationUnitDbId") String observationUnitDbId, + @QueryValue("observationVariableDbId") String observationVariableDbId, + @QueryValue("locationDbId") String locationDbId, + @QueryValue("seasonDbId") String seasonDbId, + @QueryValue("observationLevel") String observationLevel, + @QueryValue("searchResultsDbId") String searchResultsDbId, + @QueryValue("observationTimeStampRangeStart") Date observationTimeStampRangeStart, + @QueryValue("observationTimeStampRangeEnd") Date observationTimeStampRangeEnd, + @QueryValue("programDbId") String programDbId, + @QueryValue("trialDbId") String trialDbId, + @QueryValue("studyDbId") String studyDbId, + @QueryValue("germplasmDbId") String germplasmDbId, + @QueryValue("observationUnitLevelName") String observationUnitLevelName, + @QueryValue("observationUnitLevelOrder") String observationUnitLevelOrder, + @QueryValue("observationUnitLevelCode") String observationUnitLevelCode, + @QueryValue("observationUnitLevelRelationshipName") String observationUnitLevelRelationshipName, + @QueryValue("observationUnitLevelRelationshipOrder") String observationUnitLevelRelationshipOrder, + @QueryValue("observationUnitLevelRelationshipCode") String observationUnitLevelRelationshipCode, + @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId) { + return HttpResponse.notFound(); + } +} diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIProgramsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIProgramsController.java new file mode 100644 index 000000000..309172319 --- /dev/null +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIProgramsController.java @@ -0,0 +1,106 @@ +/* + * 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.annotation.*; +import io.micronaut.security.annotation.Secured; +import io.micronaut.security.rules.SecurityRule; +import lombok.extern.slf4j.Slf4j; +import org.brapi.v2.model.core.BrAPIProgram; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; + +import java.util.List; +import java.util.UUID; + +@Slf4j +@Controller("/${micronaut.bi.api.version}") +@Secured(SecurityRule.IS_AUTHENTICATED) +public class BrAPIProgramsController { + /* + TODO + - GET programs + */ + + //START - endpoints at root BrAPI url + @Get(BrapiVersion.BRAPI_V2 + "/programs") + public HttpResponse rootProgramsGet(@QueryValue("abbreviation") String abbreviation, + @QueryValue("programType") String programType, + @QueryValue("commonCropName") String commonCropName, + @QueryValue("programDbId") String programDbId, + @QueryValue("programName") String programName, + @QueryValue("externalReferenceID") String externalReferenceID, + @QueryValue("externalReferenceId") String externalReferenceId, + @QueryValue("externalReferenceSource") String externalReferenceSource, + @QueryValue("page") Integer page, + @QueryValue("pageSize") Integer pageSize) { + return HttpResponse.notFound(); + } + + @Post(BrapiVersion.BRAPI_V2 + "/programs") + public HttpResponse rootProgramsPost(List body) { + //DO NOT IMPLEMENT - Users should only be able to create new programs via the DeltaBreed UI + return HttpResponse.notFound(); + } + + @Get(BrapiVersion.BRAPI_V2 + "/programs/{programDbId}") + public HttpResponse rootProgramsProgramDbIdGet(@PathVariable("programDbId") String programDbId) { + return HttpResponse.notFound(); + } + + @Put(BrapiVersion.BRAPI_V2 + "/programs/{programDbId}") + public HttpResponse rootProgramsProgramDbIdPut(@PathVariable("programDbId") String programDbId, BrAPIProgram body) { + //DO NOT IMPLEMENT - Users should only be able to update programs via the DeltaBreed UI + return HttpResponse.notFound(); + } + //END - endpoints at root BrAPI url + + + //START - endpoints for within the context of a program + @Get("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/programs") + public HttpResponse programsGet(@PathVariable("programId") UUID programId, @QueryValue("abbreviation") String abbreviation, + @QueryValue("programType") String programType, + @QueryValue("commonCropName") String commonCropName, + @QueryValue("programDbId") String programDbId, + @QueryValue("programName") String programName, + @QueryValue("externalReferenceID") String externalReferenceID, + @QueryValue("externalReferenceId") String externalReferenceId, + @QueryValue("externalReferenceSource") String externalReferenceSource, + @QueryValue("page") Integer page, + @QueryValue("pageSize") Integer pageSize) { + return HttpResponse.notFound(); + } + + @Post("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/programs") + public HttpResponse programsPost(@PathVariable("programId") UUID programId, List body) { + //DO NOT IMPLEMENT - Users should only be able to create new programs via the DeltaBreed UI + return HttpResponse.notFound(); + } + + @Get("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/programs/{programDbId}") + public HttpResponse programsProgramDbIdGet(@PathVariable("programId") UUID programId, @PathVariable("programDbId") String programDbId) { + return HttpResponse.notFound(); + } + + @Put("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/programs/{programDbId}") + public HttpResponse programsProgramDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("programDbId") String programDbId, BrAPIProgram body) { + //DO NOT IMPLEMENT - Users should only be able to update programs via the DeltaBreed UI + return HttpResponse.notFound(); + } + //END - endpoints for within the context of a program +} diff --git a/src/main/java/org/breedinginsight/brapi/v2/StudyController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIStudiesController.java similarity index 71% rename from src/main/java/org/breedinginsight/brapi/v2/StudyController.java rename to src/main/java/org/breedinginsight/brapi/v2/BrAPIStudiesController.java index 29aeb7dd3..4f64fba1c 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/StudyController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIStudiesController.java @@ -46,14 +46,14 @@ @Slf4j @Controller("/${micronaut.bi.api.version}") @Secured(SecurityRule.IS_AUTHENTICATED) -public class StudyController { +public class BrAPIStudiesController { private final BrAPIStudyService studyService; private final StudyQueryMapper studyQueryMapper; @Inject - public StudyController(BrAPIStudyService studyService, StudyQueryMapper studyQueryMapper) { + public BrAPIStudiesController(BrAPIStudyService studyService, StudyQueryMapper studyQueryMapper) { this.studyService = studyService; this.studyQueryMapper = studyQueryMapper; } @@ -80,4 +80,34 @@ public HttpResponse>>> getStudies( return HttpResponse.status(HttpStatus.UNPROCESSABLE_ENTITY, "Error parsing requested date format"); } } + + /* + TODO + - GET studies/{id} + */ + @Post("/studies") + @Consumes({"application/json"}) + @Produces({"application/json"}) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse studiesPost(@PathVariable("programId") UUID programId, List body) { + //DO NOT IMPLEMENT - Users are only able to create new studies via the DeltaBreed UI + return HttpResponse.notFound(); + } + + @Get("/studies/{studyDbId}") + @Produces({"application/json"}) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse studiesStudyDbIdGet(@PathVariable("programId") UUID programId, @PathVariable("studyDbId") String studyDbId) { + return HttpResponse.notFound(); + } + + @Put("/studies/{studyDbId}") + @Consumes({"application/json"}) + @Produces({"application/json"}) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse studiesStudyDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("studyDbId") String studyDbId, + BrAPIStudy body) { + //DO NOT IMPLEMENT - Users are only able to update studies via the DeltaBreed UI + return HttpResponse.notFound(); + } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java new file mode 100644 index 000000000..71b979641 --- /dev/null +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java @@ -0,0 +1,101 @@ +package org.breedinginsight.brapi.v2; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.*; +import io.micronaut.security.annotation.Secured; +import io.micronaut.security.rules.SecurityRule; +import lombok.extern.slf4j.Slf4j; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.core.BrAPITrial; +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.v2.model.request.query.ExperimentQuery; +import org.breedinginsight.brapi.v2.services.BrAPITrialService; +import org.breedinginsight.services.ProgramService; +import org.breedinginsight.services.exceptions.DoesNotExistException; +import org.breedinginsight.utilities.response.ResponseUtils; +import org.breedinginsight.utilities.response.mappers.ExperimentQueryMapper; +import javax.inject.Inject; +import javax.validation.Valid; +import java.util.*; + +@Slf4j +@Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) +@Secured(SecurityRule.IS_AUTHENTICATED) +public class BrAPITrialsController { + private final BrAPITrialService experimentService; + private final ExperimentQueryMapper experimentQueryMapper; + private final ProgramService programService; + + @Inject + public BrAPITrialsController(BrAPITrialService experimentService, ExperimentQueryMapper experimentQueryMapper, ProgramService programService) { + this.experimentService = experimentService; + this.experimentQueryMapper = experimentQueryMapper; + this.programService = programService; + } + + @Get("/trials{?queryParams*}") + @Produces(MediaType.APPLICATION_JSON) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse>>> getExperiments( + @PathVariable("programId") UUID programId, + @QueryValue @QueryValid(using = ExperimentQueryMapper.class) @Valid ExperimentQuery queryParams) { + try { + log.debug("fetching trials for program: " + programId); + + List experiments = experimentService.getExperiments(programId); + SearchRequest searchRequest = queryParams.constructSearchRequest(); + return ResponseUtils.getBrapiQueryResponse(experiments, experimentQueryMapper, queryParams, searchRequest); + } catch (ApiException e) { + log.info(e.getMessage(), e); + return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving experiments"); + } catch (DoesNotExistException e) { + log.info(e.getMessage(), e); + return HttpResponse.status(HttpStatus.NOT_FOUND, e.getMessage()); + } + } + + @Get("/trials/{trialId}") + @Produces(MediaType.APPLICATION_JSON) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse> getExperimentById( + @PathVariable("programId") UUID programId, + @PathVariable("trialId") UUID trialId, + @QueryValue(defaultValue = "false") Boolean stats){ + try { + String logMsg = "fetching trial id:" + trialId +" for program: " + programId; + if(stats){ + logMsg += " with stats"; + } + log.debug(logMsg); + Response response = new Response<>(experimentService.getTrialDataByUUID(programId, trialId, stats)); + return HttpResponse.ok(response); + } catch (DoesNotExistException e) { + log.info(e.getMessage(), e); + return HttpResponse.status(HttpStatus.NOT_FOUND, e.getMessage()); + } + } + + @Post("/trials") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse trialsPost(@PathVariable("programId") UUID programId, List body) { + //DO NOT IMPLEMENT - Users are only able to create new trials via the DeltaBreed UI + return HttpResponse.notFound(); + } + + + @Put("/trials/{trialDbId}") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse trialsTrialDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("trialDbId") String trialDbId, BrAPITrial body) { + //DO NOT IMPLEMENT - Users are only able to update trials via the DeltaBreed UI + return HttpResponse.notFound(); + } + +} diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java index 702a0ca14..3a485bd9a 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java @@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j; import okhttp3.*; import org.brapi.v2.model.core.BrAPIServerInfo; +import org.brapi.v2.model.core.BrAPIService; import org.brapi.v2.model.core.response.BrAPIServerInfoResponse; import org.breedinginsight.api.auth.AuthenticatedUser; import org.breedinginsight.api.auth.ProgramSecured; @@ -40,7 +41,7 @@ import javax.inject.Inject; import java.io.IOException; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; @Slf4j @@ -63,13 +64,107 @@ public BrAPIV2Controller(SecurityService securityService, ProgramService program @Secured(SecurityRule.IS_ANONYMOUS) public BrAPIServerInfoResponse serverinfo() { BrAPIServerInfo serverInfo = new BrAPIServerInfo(); + setBrAPIServerInfo(serverInfo); + serverInfo.setServerDescription("BrAPI endpoints are not implemented at the root of this domain. Please make BrAPI calls in the context of a program (ex: https://app.breedinginsight.net/v1/programs/{programId}/brapi/v2)"); + + serverInfo.setCalls( + new ServiceBuilder().versions("2.0", "2.1") + .setBase("serverinfo").GET().build() + .setBase("programs").GET().addPath("{programDbId}").GET().build() + ); + + return new BrAPIServerInfoResponse().result(serverInfo); + } + + @Get("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/serverinfo") + @Produces(MediaType.APPLICATION_JSON) + @Secured(SecurityRule.IS_ANONYMOUS) + public BrAPIServerInfoResponse programServerinfo(@PathVariable("programId") UUID programId) { + String programBrAPIBase = String.format("v1/programs/%s%s/", programId, BrapiVersion.BRAPI_V2); + + List programServices = new ServiceBuilder() + .versions("2.0", "2.1") + //CORE + .setBase("serverinfo").GET().build() + .setBase("commoncropnames").GET().build() + .setBase("lists").GET().POST().addPath("{listDbId}").GET().PUT().withSearch() + .setBase("locations").GET().addPath("{locationDbId}").GET().withSearch() + .setBase("people").GET().addPath("{personDbId}").GET().withSearch() + .setBase("programs").GET().addPath("{programDbId}").GET().withSearch() + .setBase("seasons").GET().addPath("{seasonDbId}").GET().build() + .setBase("studies").GET().addPath("{studyDbId}").GET().withSearch() + .setBase("studytypes").GET().build() + .setBase("trials").GET().addPath("{trialDbId}").GET().withSearch() + //GERMPLASM + .setBase("attributes").GET().addPath("{attributeDbId}").GET().setPath("categories").GET().withSearch() + .setBase("attributevalues").GET().addPath("{attributeValueDbId}").GET().withSearch() + .setBase("breedingmethods").GET().addPath("{breedingMethodDbId}").GET().build() + .setBase("crosses").GET().build() + .setBase("plannedcrosses").GET().build() + .setBase("crossingprojects").GET().addPath("{crossingProjectDbId}").GET().build() + .setBase("seedlots").GET().addPath("transactions").GET().setPath("{seedLotDbId}").GET().addPath("transactions").GET().build() + .setBase("germplasm").GET().addPath("{germplasmDbId}").GET().addPath("mcpd").GET().withSearch() + //PHENOTYPING + .setBase("events").GET().build() + .setBase("images").GET().addPath("{imageDbId}").GET().addPath("imagecontent").withSearch() + .setBase("ontologies").GET().build() + .setBase("traits").GET().addPath("{traitDbId}").GET().build() + .setBase("methods").GET().addPath("{methodDbId}").GET().build() + .setBase("scales").GET().addPath("{scaleDbId}").GET().build() + .setBase("variables").GET().addPath("{observationVariableDbId}").GET().withSearch() + .setBase("observationunits").GET().addPath("{observationUnitDbId}").GET().setPath("table").GET().withSearch() + .setBase("observations").GET().addPath("{observationDbId}").GET().setPath("table").GET().withSearch() + .setBase("observationlevels").GET().build() + //GENOTYPING - TODO +// .setBase("calls").GET().withSearch() +// .setBase("callsets").GET().addPath("{callSetDbId}").GET().addPath("calls").GET().withSearch() +// .setBase("maps").GET().addPath("{mapDbId}").GET().addPath("linkagegroups").GET().build() +// .setBase("markerpositions").GET().withSearch() +// .setBase("references").GET().addPath("{referenceDbId}").GET().addPath("bases").GET().withSearch() +// .setBase("referencesets").GET().addPath("{referenceSetDbId}").GET().withSearch() +// .setBase("samples").GET().addPath("{sampleDbId}").GET().withSearch() +// .setBase("variants").GET().addPath("{variantDbId}").GET().addPath("calls").GET().withSearch() +// .setBase("variantsets").GET().addPath("extract").setPath("{variantSetDbId}").GET() +// .addPath("calls").GET().setPath("callsets").GET().setPath("variants").GET().withSearch() +// .setBase("vendor").addPath("specifications").GET().setPath("plates").addPath("{submissionId}").build() +// .setBase("vendor/orders").GET().addPath("{orderId}").addPath("plates").GET().setPath("results").GET().setPath("status").GET().build() + + //V2.0 only + .versions("2.0") + .setBase("germplasm").addPath("{germplasmDbId}").addPath("pedigree").GET().setPath("progeny").GET().build() + .setBase("lists").addPath("{listDbId}").addPath("items").build() +// .setBase("samples").addPath("{sampleDbId}").build() //TODO + //V2.1 only + .versions("2.1") +// .setBase("allelematrix").GET().withSearch() //TODO +// .setBase("calls").build() //TODO + .setBase("delete").addPath("images").setPath("observations").build() + .setBase("lists").addPath("{listDbId}").addPath("data").POST().build() + .setBase("ontologies").addPath("{ontologyDbId}").GET().build() + .setBase("pedigree").GET().withSearch() +// .setBase("plates").GET().addPath("{plateDbId}").GET().withSearch() //TODO +// .setBase("samples").build() //TODO + .build(); + + for(BrAPIService service : programServices) { + service.setService(programBrAPIBase + service.getService()); + } + + BrAPIServerInfo programServerInfo = new BrAPIServerInfo(); + setBrAPIServerInfo(programServerInfo); + programServerInfo.setCalls(programServices); + + return new BrAPIServerInfoResponse().result(programServerInfo); + } + + private void setBrAPIServerInfo(BrAPIServerInfo serverInfo) { serverInfo.setOrganizationName("Breeding Insight"); serverInfo.setServerName("DeltaBreed"); serverInfo.setContactEmail("bidevteam@cornell.edu"); - serverInfo.setOrganizationURL("breedinginsight.org"); - serverInfo.setServerDescription("BrAPI endpoints are not implemented at the root of this domain. Please make BrAPI calls in the context of a program (ex: https://app.breedinginsight.net/v1/programs//brapi/v2)"); - - return new BrAPIServerInfoResponse().result(serverInfo); + serverInfo.setOrganizationURL("https://breedinginsight.org"); + serverInfo.setServerDescription("DeltaBreed - breeding data management system"); + serverInfo.setLocation("Cornell University, Ithaca, NY, USA"); + serverInfo.setDocumentationURL("https://brapi.org/specification"); } @Get("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/{+path}") @@ -154,7 +249,23 @@ private HttpResponse makeCall(Request brapiRequest) { } private HttpUrl getUrl(UUID programId, String path, HttpRequest request) { + var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); + + var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/" + path).newBuilder(); + request.getParameters() + .asMap() + .entrySet() + .stream() + .filter(param -> !param.getKey() + .equals("programId")) + .forEach(param -> param.getValue() + .forEach(val -> requestUrl.addQueryParameter(param.getKey(), val))); + + return requestUrl.build(); + } + + private String getProgramBrAPIBaseUrl(UUID programId) { ProgramBrAPIEndpoints programBrAPIEndpoints; try { programBrAPIEndpoints = programService.getBrapiEndpoints(programId); @@ -168,20 +279,7 @@ private HttpUrl getUrl(UUID programId, String path, HttpRequest request) { } var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; - String urlString = programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; - var requestUrl = HttpUrl.parse(urlString + "/" + path) - .newBuilder(); - - request.getParameters() - .asMap() - .entrySet() - .stream() - .filter(param -> !param.getKey() - .equals("programId")) - .forEach(param -> param.getValue() - .forEach(val -> requestUrl.addQueryParameter(param.getKey(), val))); - - return requestUrl.build(); + return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; } private void logCall(String path, HttpRequest request) { diff --git a/src/main/java/org/breedinginsight/brapi/v2/ServiceBuilder.java b/src/main/java/org/breedinginsight/brapi/v2/ServiceBuilder.java new file mode 100644 index 000000000..c5a0464a6 --- /dev/null +++ b/src/main/java/org/breedinginsight/brapi/v2/ServiceBuilder.java @@ -0,0 +1,80 @@ +package org.breedinginsight.brapi.v2; + +import org.brapi.v2.model.BrAPIWSMIMEDataTypes; +import org.brapi.v2.model.core.BrAPIService; +import org.brapi.v2.model.core.BrAPIService.MethodsEnum; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ServiceBuilder extends ArrayList{ + private static final long serialVersionUID = 1L; + private String path = ""; + private String base = ""; + private List methods = new ArrayList<>(); + private List versions = new ArrayList<>(); + + public ServiceBuilder setBase(String base) { + this.path = base; + this.base = base; + this.methods.clear(); + return this; + } + public ServiceBuilder setPath(String newPath) { + build(); + + String[] pathParts = this.path.split("/"); + String oldPath = pathParts[pathParts.length - 1].replaceAll("\\{", "\\\\{").replaceAll("\\}", "\\\\}"); + this.path = this.path.replaceFirst(oldPath, newPath); + this.methods.clear(); + return this; + } + public ServiceBuilder addPath(String path) { + build(); + this.path = this.path + '/' + path; + this.methods.clear(); + return this; + } + + public ServiceBuilder PUT() { + methods.add(MethodsEnum.PUT); + return this; + } + + public ServiceBuilder POST() { + methods.add(MethodsEnum.POST); + return this; + } + + public ServiceBuilder GET() { + methods.add(MethodsEnum.GET); + return this; + } + + public ServiceBuilder versions(String ... versions) { + this.versions = Arrays.asList(versions); + return this; + } + + public ServiceBuilder build() { + if(path != null && !path.isEmpty() && methods != null && !methods.isEmpty()) { + this.add(buildService(path, methods)); + } + return this; + } + public ServiceBuilder withSearch() { + build(); + this.add(buildService("search/" + base, Arrays.asList(MethodsEnum.POST))); + this.add(buildService("search/" + base + "/{searchResultsDbId}", Arrays.asList(MethodsEnum.GET))); + return this; + } + public BrAPIService buildService(String path, List methods) { + BrAPIService service = new BrAPIService(); + service.addDataTypesItem(BrAPIWSMIMEDataTypes.APPLICATION_JSON); + service.setMethods(new ArrayList<>(methods)); + service.setVersions(new ArrayList<>(this.versions)); + service.setService(path); + return service; + } +} diff --git a/src/main/java/org/breedinginsight/utilities/response/mappers/StudyQueryMapper.java b/src/main/java/org/breedinginsight/utilities/response/mappers/StudyQueryMapper.java index f15aa620f..4a12acb4e 100644 --- a/src/main/java/org/breedinginsight/utilities/response/mappers/StudyQueryMapper.java +++ b/src/main/java/org/breedinginsight/utilities/response/mappers/StudyQueryMapper.java @@ -37,6 +37,13 @@ public class StudyQueryMapper extends AbstractQueryMapper { private final Map> fields; public StudyQueryMapper() { + /* + TODO + - observationVariableDbId + - active + - programDbId + - germplasmDbId + */ fields = Map.ofEntries( Map.entry("studyType", BrAPIStudy::getStudyType), Map.entry("locationDbId", BrAPIStudy::getLocationDbId), From 6e49ae26a3a4b012b1b9f68be314100b02d82f43 Mon Sep 17 00:00:00 2001 From: timparsons Date: Mon, 25 Sep 2023 16:53:23 -0400 Subject: [PATCH 02/20] [BI-1921] fixing compilation error --- .../auth/OIDCDiscoveryController.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/breedinginsight/api/v1/controller/auth/OIDCDiscoveryController.java b/src/main/java/org/breedinginsight/api/v1/controller/auth/OIDCDiscoveryController.java index 4d58da165..8560cb445 100644 --- a/src/main/java/org/breedinginsight/api/v1/controller/auth/OIDCDiscoveryController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/auth/OIDCDiscoveryController.java @@ -35,18 +35,16 @@ @Secured(SecurityRule.IS_ANONYMOUS) public class OIDCDiscoveryController { - private static final String OIDC_CONFIG = """ - { - "issuer": "%s", - "authorization_endpoint": "%s/programs/%s/brapi/authorize", - "jwks_uri": "", - "token_endpoint": "", - "grant_types_supported": ["implicit"], - "response_types_supported": ["token"], - "subject_types_supported": ["public"], - "id_token_signing_alg_values_supported": [] - } - """; + private static final String OIDC_CONFIG = " {\n" + + " \"issuer\": \"%s\",\n" + + " \"authorization_endpoint\": \"%s/programs/%s/brapi/authorize\",\n" + + " \"jwks_uri\": \"\",\n" + + " \"token_endpoint\": \"\",\n" + + " \"grant_types_supported\": [\"implicit\"],\n" + + " \"response_types_supported\": [\"token\"],\n" + + " \"subject_types_supported\": [\"public\"],\n" + + " \"id_token_signing_alg_values_supported\": []\n" + + " }\n"; private final String webUrl; private final ProgramDAO programDAO; From 468ea83dff98907d348e8580c864c4c177984d13 Mon Sep 17 00:00:00 2001 From: timparsons Date: Wed, 4 Oct 2023 09:12:08 -0400 Subject: [PATCH 03/20] [BI-1921] Implementing various BrAPI endpoints to support Field Book --- .../api/v1/controller/TokenController.java | 4 +- .../v2/BrAPIObservationLevelsController.java | 143 ++++++++ .../v2/BrAPIObservationUnitController.java | 191 +++++++++-- .../BrAPIObservationVariableController.java | 321 ++++++++++++++++-- .../brapi/v2/BrAPIProgramsController.java | 142 ++++++-- .../brapi/v2/BrAPIStudiesController.java | 72 +++- .../brapi/v2/BrAPITrialsController.java | 37 +- .../brapi/v2/services/BrAPIStudyService.java | 13 +- .../brapi/v2/services/BrAPITrialService.java | 59 ++-- .../daos/BrAPIObservationUnitDAO.java | 150 ++++++-- .../brapps/importer/daos/BrAPIStudyDAO.java | 15 + .../processors/ObservationUnitProcessor.java | 3 + .../daos/impl/TraitDAOImpl.java | 26 +- .../org/breedinginsight/model/Dataset.java | 4 +- .../services/TraitService.java | 8 + 15 files changed, 993 insertions(+), 195 deletions(-) create mode 100644 src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsController.java diff --git a/src/main/java/org/breedinginsight/api/v1/controller/TokenController.java b/src/main/java/org/breedinginsight/api/v1/controller/TokenController.java index ca6053be7..6c396cb9d 100644 --- a/src/main/java/org/breedinginsight/api/v1/controller/TokenController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/TokenController.java @@ -52,7 +52,7 @@ public TokenController(SecurityService securityService, TokenService tokenServic @Get("/api-token{?returnUrl}") @Secured(SecurityRule.IS_AUTHENTICATED) - public HttpResponse apiToken(@QueryValue @Nullable String returnUrl) { + public HttpResponse apiToken(@QueryValue @Nullable String returnUrl) { AuthenticatedUser actingUser = securityService.getUser(); Optional token = tokenService.generateApiToken(actingUser); @@ -66,7 +66,7 @@ public HttpResponse apiToken(@QueryValue @Nullable String returnUrl) { } URI location = UriBuilder.of(returnUrl) .queryParam("status", 200) - .queryParam("token", apiToken.getAccessToken()) + .queryParam("access_token", apiToken.getAccessToken()) .build(); return HttpResponse.seeOther(location) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsController.java new file mode 100644 index 000000000..3fe481c73 --- /dev/null +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsController.java @@ -0,0 +1,143 @@ +/* + * 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.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.PathVariable; +import io.micronaut.http.annotation.QueryValue; +import io.micronaut.security.annotation.Secured; +import io.micronaut.security.rules.SecurityRule; +import lombok.extern.slf4j.Slf4j; +import org.brapi.client.v2.ApiResponse; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.client.v2.modules.phenotype.ObservationUnitsApi; +import org.brapi.v2.model.BrAPIIndexPagination; +import org.brapi.v2.model.BrAPIMetadata; +import org.brapi.v2.model.core.BrAPIStudy; +import org.brapi.v2.model.core.BrAPITrial; +import org.brapi.v2.model.pheno.BrAPIObservationUnit; +import org.brapi.v2.model.pheno.BrAPIObservationUnitHierarchyLevel; +import org.brapi.v2.model.pheno.BrAPIObservationUnitLevelRelationship; +import org.brapi.v2.model.pheno.response.BrAPIObservationLevelListResponse; +import org.brapi.v2.model.pheno.response.BrAPIObservationLevelListResponseResult; +import org.breedinginsight.api.auth.ProgramSecured; +import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; +import org.breedinginsight.brapps.importer.daos.BrAPIStudyDAO; +import org.breedinginsight.brapps.importer.daos.BrAPITrialDAO; +import org.breedinginsight.daos.ProgramDAO; +import org.breedinginsight.model.Program; +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.annotation.Nullable; +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Slf4j +@Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) +@Secured(SecurityRule.IS_AUTHENTICATED) +public class BrAPIObservationLevelsController { + + private final BrAPIEndpointProvider brAPIEndpointProvider; + private final ProgramDAO programDAO; + private final ProgramService programService; + private final BrAPITrialDAO trialDAO; + private final BrAPIStudyDAO studyDAO; + + @Inject + public BrAPIObservationLevelsController(BrAPIEndpointProvider brAPIEndpointProvider, ProgramDAO programDAO, ProgramService programService, BrAPITrialDAO trialDAO, BrAPIStudyDAO studyDAO) { + this.brAPIEndpointProvider = brAPIEndpointProvider; + this.programDAO = programDAO; + this.programService = programService; + this.trialDAO = trialDAO; + this.studyDAO = studyDAO; + } + + @Get("/observationlevels") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse observationlevelsGet(@PathVariable("programId") UUID programId, + @Nullable @QueryValue("trialDbId") String experimentId, + @Nullable @QueryValue("studyDbId") String environmentId, + @Nullable @QueryValue("page") Integer page, + @Nullable @QueryValue("pageSize") Integer pageSize) { + + log.debug("fetching observation levels for programId: " + programId); + + Optional program = programService.getById(programId); + if(program.isEmpty()) { + log.warn("Program id: " + programId + " not found"); + return HttpResponse.notFound(); + } + + String programDbId = program.get().getBrapiProgram().getProgramDbId(); + String studyDbId = null; + String trialDbId = null; + + if(environmentId != null) { + try { + Optional study = studyDAO.getStudyByEnvironmentId(UUID.fromString(environmentId), program.get()); + if(study.isPresent()) { + studyDbId = study.get().getStudyDbId(); + } else { + studyDbId = environmentId; + } + } catch (ApiException e) { + log.error(Utilities.generateApiExceptionLogMessage(e), "Error fetching environment"); + return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error finding observation levels"); + } + } else if(experimentId != null) { + try { + List trial = trialDAO.getTrialsByExperimentIds(List.of(UUID.fromString(experimentId)), program.get()); + if(trial.size() == 1) { + trialDbId = trial.get(0).getTrialDbId(); + } else { + trialDbId = experimentId; + } + } catch (ApiException e) { + log.error(Utilities.generateApiExceptionLogMessage(e), "Error fetching experiments"); + return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error finding observation levels"); + } + } + + ObservationUnitsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ObservationUnitsApi.class); + try { + ApiResponse response = api.observationlevelsGet(studyDbId, trialDbId, programDbId, page == null ? 0 : page, pageSize == null ? 1000 : pageSize); + BrAPIIndexPagination responsePagination = (BrAPIIndexPagination) response.getBody().getMetadata().getPagination(); + List levels = new ArrayList<>(); + if(response.getBody() != null) { + levels = response.getBody().getResult().getData(); + } + log.debug(String.format("found %d observation levels", levels.size())); + return HttpResponse.ok(new BrAPIObservationLevelListResponse().metadata(new BrAPIMetadata().pagination(responsePagination)) + .result(new BrAPIObservationLevelListResponseResult().data(levels))); + } catch (ApiException e) { + log.error(Utilities.generateApiExceptionLogMessage(e), "Error fetching observation levels"); + return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error finding observation levels"); + } + } +} diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java index 96b91df36..0c17e31e5 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java @@ -17,24 +17,54 @@ package org.breedinginsight.brapi.v2; +import io.micronaut.context.annotation.Property; import io.micronaut.http.HttpResponse; import io.micronaut.http.annotation.*; import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.BrAPIIndexPagination; +import org.brapi.v2.model.BrAPIMetadata; +import org.brapi.v2.model.BrAPIStatus; import org.brapi.v2.model.pheno.BrAPIObservationUnit; +import org.brapi.v2.model.pheno.response.BrAPIObservationUnitListResponse; +import org.brapi.v2.model.pheno.response.BrAPIObservationUnitListResponseResult; +import org.brapi.v2.model.pheno.response.BrAPIObservationUnitSingleResponse; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; import org.breedinginsight.brapi.v1.controller.BrapiVersion; +import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; +import org.breedinginsight.brapps.importer.daos.BrAPIObservationUnitDAO; +import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; +import org.breedinginsight.model.Program; +import org.breedinginsight.services.ProgramService; +import org.breedinginsight.utilities.Utilities; +import javax.annotation.Nullable; +import javax.inject.Inject; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; @Slf4j @Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @Secured(SecurityRule.IS_AUTHENTICATED) public class BrAPIObservationUnitController { + private final String referenceSource; + + private final BrAPIObservationUnitDAO observationUnitDAO; + + private final ProgramService programService; + + @Inject + public BrAPIObservationUnitController(@Property(name = "brapi.server.reference-source") String referenceSource, BrAPIObservationUnitDAO observationUnitDAO, ProgramService programService) { + this.referenceSource = referenceSource; + this.observationUnitDAO = observationUnitDAO; + this.programService = programService; + } /* TODO @@ -42,60 +72,139 @@ public class BrAPIObservationUnitController { - GET observationUnits */ @Get("/observationunits") - public HttpResponse observationunitsGet(@PathVariable("programId") UUID programId, - @QueryValue("observationUnitDbId") String observationUnitDbId, - @QueryValue("observationUnitName") String observationUnitName, - @QueryValue("locationDbId") String locationDbId, - @QueryValue("seasonDbId") String seasonDbId, - @QueryValue("includeObservations") Boolean includeObservations, - @QueryValue("observationUnitLevelName") String observationUnitLevelName, - @QueryValue("observationUnitLevelOrder") String observationUnitLevelOrder, - @QueryValue("observationUnitLevelCode") String observationUnitLevelCode, - @QueryValue("observationUnitLevelRelationshipName") String observationUnitLevelRelationshipName, - @QueryValue("observationUnitLevelRelationshipOrder") String observationUnitLevelRelationshipOrder, - @QueryValue("observationUnitLevelRelationshipCode") String observationUnitLevelRelationshipCode, - @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId, - @QueryValue("commonCropName") String commonCropName, - @QueryValue("programDbId") String programDbId, - @QueryValue("trialDbId") String trialDbId, - @QueryValue("studyDbId") String studyDbId, - @QueryValue("germplasmDbId") String germplasmDbId, - @QueryValue("externalReferenceID") String externalReferenceID, - @QueryValue("externalReferenceId") String externalReferenceId, - @QueryValue("externalReferenceSource") String externalReferenceSource, - @QueryValue("page") Integer page, - @QueryValue("pageSize") Integer pageSize) { - return HttpResponse.notFound(); + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse observationunitsGet(@PathVariable("programId") UUID programId, + @Nullable @QueryValue("observationUnitDbId") String observationUnitDbId, + @Nullable @QueryValue("observationUnitName") String observationUnitName, + @Nullable @QueryValue("locationDbId") String locationDbId, + @Nullable @QueryValue("seasonDbId") String seasonDbId, + @Nullable @QueryValue("includeObservations") Boolean includeObservations, + @Nullable @QueryValue("observationUnitLevelName") String observationUnitLevelName, + @Nullable @QueryValue("observationUnitLevelOrder") String observationUnitLevelOrder, + @Nullable @QueryValue("observationUnitLevelCode") String observationUnitLevelCode, + @Nullable @QueryValue("observationUnitLevelRelationshipName") String observationUnitLevelRelationshipName, + @Nullable @QueryValue("observationUnitLevelRelationshipOrder") String observationUnitLevelRelationshipOrder, + @Nullable @QueryValue("observationUnitLevelRelationshipCode") String observationUnitLevelRelationshipCode, + @Nullable @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId, + @Nullable @QueryValue("commonCropName") String commonCropName, + @Nullable @QueryValue("programDbId") String programDbId, + @Nullable @QueryValue("trialDbId") String trialDbId, + @Nullable @QueryValue("studyDbId") String studyDbId, + @Nullable @QueryValue("germplasmDbId") String germplasmDbId, + @Nullable @QueryValue("externalReferenceID") String externalReferenceID, + @Nullable @QueryValue("externalReferenceId") String externalReferenceId, + @Nullable @QueryValue("externalReferenceSource") String externalReferenceSource, + @Nullable @QueryValue("page") Integer page, + @Nullable @QueryValue("pageSize") Integer pageSize) { + + log.debug("observationunitsGet: fetching ous by filters"); + + Optional program = programService.getById(programId); + if(program.isEmpty()) { + log.warn("Program id: " + programId + " not found"); + return HttpResponse.notFound(); + } + + try { + Optional levelOrder = Optional.empty(); + Optional levelRelationshipOrder = Optional.empty(); + if(observationUnitLevelOrder != null) { + levelOrder = Optional.of(Integer.parseInt(observationUnitLevelOrder)); + } + if(observationUnitLevelRelationshipOrder != null) { + levelOrder = Optional.of(Integer.parseInt(observationUnitLevelRelationshipOrder)); + } + + List observationUnits = observationUnitDAO.getObservationUnits(program.get(), + Optional.ofNullable(observationUnitDbId), + Optional.ofNullable(observationUnitName), + Optional.ofNullable(locationDbId), + Optional.ofNullable(seasonDbId), + Optional.ofNullable(includeObservations), + Optional.ofNullable(observationUnitLevelName), + levelOrder, + Optional.ofNullable(observationUnitLevelCode), + Optional.ofNullable(observationUnitLevelRelationshipName), + levelRelationshipOrder, + Optional.ofNullable(observationUnitLevelRelationshipCode), + Optional.ofNullable(observationUnitLevelRelationshipDbId), + Optional.ofNullable(commonCropName), + Optional.ofNullable(trialDbId), + Optional.ofNullable(studyDbId), + Optional.ofNullable(germplasmDbId)) + .stream() + .peek(this::setDbIds) + .collect(Collectors.toList()); + + return HttpResponse.ok(new BrAPIObservationUnitListResponse().metadata(new BrAPIMetadata().pagination(new BrAPIIndexPagination().currentPage(0) + .totalPages(1) + .pageSize(observationUnits.size()) + .totalCount(observationUnits.size()))) + .result(new BrAPIObservationUnitListResponseResult().data(observationUnits))); + } catch (ApiException e) { + log.error(Utilities.generateApiExceptionLogMessage(e), e); + return HttpResponse.serverError(new BrAPIObservationUnitListResponse().metadata(new BrAPIMetadata().status(List.of(new BrAPIStatus().messageType(BrAPIStatus.MessageTypeEnum.ERROR) + .message("Error fetching observation units"))))); + } catch (Exception e) { + log.error("Error fetching OUs", e); + return HttpResponse.serverError(new BrAPIObservationUnitListResponse().metadata(new BrAPIMetadata().status(List.of(new BrAPIStatus().messageType(BrAPIStatus.MessageTypeEnum.ERROR) + .message("Error fetching observation units"))))); + } } @Get("/observationunits/{observationUnitDbId}") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse observationunitsObservationUnitDbIdGet(@PathVariable("programId") UUID programId, @PathVariable("observationUnitDbId") String observationUnitDbId) { - return HttpResponse.notFound(); + public HttpResponse observationunitsObservationUnitDbIdGet(@PathVariable("programId") UUID programId, @PathVariable("observationUnitDbId") String observationUnitDbId) { + log.debug("observationunitsObservationUnitDbIdGet: fetching ou by externalReferenceId: " + observationUnitDbId); + Optional program = programService.getById(programId); + if(program.isEmpty()) { + log.warn("Program id: " + programId + " not found"); + return HttpResponse.notFound(); + } + try { + List ous = observationUnitDAO.getObservationUnitsById(List.of(observationUnitDbId), program.get()); + if(ous.size() != 1) { + log.warn("did not find a single ou with externalReferenceId: " + observationUnitDbId); + return HttpResponse.notFound(); + } + setDbIds(ous.get(0)); + return HttpResponse.ok(new BrAPIObservationUnitSingleResponse().result(ous.get(0))); + } catch (ApiException e) { + log.error(Utilities.generateApiExceptionLogMessage(e), e); + return HttpResponse.serverError(new BrAPIObservationUnitSingleResponse().metadata(new BrAPIMetadata().status(List.of(new BrAPIStatus().messageType(BrAPIStatus.MessageTypeEnum.ERROR) + .message("Error fetching observation unit"))))); + } catch (Exception e) { + log.error("Error fetching OU", e); + return HttpResponse.serverError(new BrAPIObservationUnitSingleResponse().metadata(new BrAPIMetadata().status(List.of(new BrAPIStatus().messageType(BrAPIStatus.MessageTypeEnum.ERROR) + .message("Error fetching observation unit"))))); + } } @Put("/observationunits/{observationUnitDbId}") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse observationunitsObservationUnitDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("observationUnitDbId") String observationUnitDbId, BrAPIObservationUnit body) { + public HttpResponse observationunitsObservationUnitDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("observationUnitDbId") String observationUnitDbId, BrAPIObservationUnit body) { + //DO NOT IMPLEMENT - Users aren't yet able to update observation units return HttpResponse.notFound(); } @Post("/observationunits") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse observationunitsPost(@PathVariable("programId") UUID programId, List body) { + public HttpResponse observationunitsPost(@PathVariable("programId") UUID programId, List body) { + //DO NOT IMPLEMENT - Users are only able to create observation units via the DeltaBreed UI return HttpResponse.notFound(); } @Put("/observationunits") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse observationunitsPut(@PathVariable("programId") UUID programId, Map body) { + public HttpResponse observationunitsPut(@PathVariable("programId") UUID programId, Map body) { + //DO NOT IMPLEMENT - Users aren't yet able to update observation units return HttpResponse.notFound(); } @Get("/observationunits/table") @Produces({"application/json", "text/csv", "text/tsv"}) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse observationunitsTableGet(@PathVariable("programId") UUID programId, + public HttpResponse observationunitsTableGet(@PathVariable("programId") UUID programId, @Header("Accept") String accept, @QueryValue("observationUnitDbId") String observationUnitDbId, @QueryValue("observationVariableDbId") String observationVariableDbId, @@ -115,4 +224,26 @@ public HttpResponse observationunitsTableGet(@PathVariable("programId") UUID pro @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId) { return HttpResponse.notFound(); } + + private void setDbIds(BrAPIObservationUnit ou) { + ou.observationUnitDbId(Utilities.getExternalReference(ou.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.OBSERVATION_UNITS)) + .orElseThrow(() -> new IllegalStateException("No BI external reference found")) + .getReferenceID()); + ou.studyDbId(Utilities.getExternalReference(ou.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.STUDIES)) + .orElseThrow(() -> new IllegalStateException("No BI external reference found")) + .getReferenceID()); + ou.trialDbId(Utilities.getExternalReference(ou.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.TRIALS)) + .orElseThrow(() -> new IllegalStateException("No BI external reference found")) + .getReferenceID()); + ou.programDbId(Utilities.getExternalReference(ou.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.PROGRAMS)) + .orElseThrow(() -> new IllegalStateException("No BI external reference found")) + .getReferenceID()); + if (ou.getAdditionalInfo().has(BrAPIAdditionalInfoFields.GERMPLASM_UUID)) { + ou.setGermplasmDbId(ou.getAdditionalInfo() + .get(BrAPIAdditionalInfoFields.GERMPLASM_UUID) + .getAsString()); + } + + //TODO update locationDbId + } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java index 29dfa9e0b..fd7b93b71 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java @@ -17,67 +17,176 @@ package org.breedinginsight.brapi.v2; +import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; import io.micronaut.http.annotation.*; import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; -import org.brapi.v2.model.pheno.BrAPIObservationVariable; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.BrAPIExternalReference; +import org.brapi.v2.model.BrAPIIndexPagination; +import org.brapi.v2.model.BrAPIMetadata; +import org.brapi.v2.model.BrAPIOntologyReference; +import org.brapi.v2.model.core.BrAPIStudy; +import org.brapi.v2.model.core.BrAPITrial; +import org.brapi.v2.model.pheno.*; +import org.brapi.v2.model.pheno.response.BrAPIObservationVariableListResponse; +import org.brapi.v2.model.pheno.response.BrAPIObservationVariableListResponseResult; +import org.brapi.v2.model.pheno.response.BrAPIObservationVariableSingleResponse; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; import org.breedinginsight.brapi.v1.controller.BrapiVersion; +import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; +import org.breedinginsight.brapi.v2.services.BrAPITrialService; +import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; +import org.breedinginsight.model.Program; +import org.breedinginsight.model.Trait; +import org.breedinginsight.services.OntologyService; +import org.breedinginsight.services.ProgramService; +import org.breedinginsight.services.TraitService; +import org.breedinginsight.services.exceptions.DoesNotExistException; +import org.breedinginsight.utilities.Utilities; +import org.jetbrains.annotations.NotNull; -import java.util.List; -import java.util.UUID; +import javax.annotation.Nullable; +import javax.inject.Inject; +import java.util.*; +import java.util.stream.Collectors; @Slf4j @Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @Secured(SecurityRule.IS_AUTHENTICATED) public class BrAPIObservationVariableController { - /* - TODO - - GET /variables - */ + private final String referenceSource; + + private final OntologyService ontologyService; + private final TraitService traitService; + + private final BrAPITrialService trialService; + + private final ProgramService programService; + + @Inject + public BrAPIObservationVariableController(OntologyService ontologyService, + TraitService traitService, + BrAPITrialService trialService, + ProgramService programService, + @Property(name = "brapi.server.reference-source") String referenceSource) { + this.ontologyService = ontologyService; + this.traitService = traitService; + this.trialService = trialService; + this.programService = programService; + this.referenceSource = referenceSource; + } @Get("/variables") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse variablesGet(@PathVariable("programId") UUID programId, - @QueryValue("observationVariableDbId") String observationVariableDbId, - @QueryValue("observationVariableName") String observationVariableName, - @QueryValue("observationVariablePUI") String observationVariablePUI, - @QueryValue("traitClass") String traitClass, - @QueryValue("methodDbId") String methodDbId, - @QueryValue("methodName") String methodName, - @QueryValue("methodPUI") String methodPUI, - @QueryValue("scaleDbId") String scaleDbId, - @QueryValue("scaleName") String scaleName, - @QueryValue("scalePUI") String scalePUI, - @QueryValue("traitDbId") String traitDbId, - @QueryValue("traitName") String traitName, - @QueryValue("traitPUI") String traitPUI, - @QueryValue("ontologyDbId") String ontologyDbId, - @QueryValue("commonCropName") String commonCropName, - @QueryValue("programDbId") String programDbId, - @QueryValue("trialDbId") String trialDbId, - @QueryValue("studyDbId") String studyDbId, - @QueryValue("externalReferenceID") String externalReferenceID, - @QueryValue("externalReferenceId") String externalReferenceId, - @QueryValue("externalReferenceSource") String externalReferenceSource, - @QueryValue("page") Integer page, - @QueryValue("pageSize") Integer pageSize) { - return HttpResponse.notFound(); + public HttpResponse variablesGet(@PathVariable("programId") UUID programId, + @Nullable @QueryValue("observationVariableDbId") String observationVariableDbId, + @Nullable @QueryValue("observationVariableName") String observationVariableName, + @Nullable @QueryValue("observationVariablePUI") String observationVariablePUI, + @Nullable @QueryValue("traitClass") String traitClass, + @Nullable @QueryValue("methodDbId") String methodDbId, + @Nullable @QueryValue("methodName") String methodName, + @Nullable @QueryValue("methodPUI") String methodPUI, + @Nullable @QueryValue("scaleDbId") String scaleDbId, + @Nullable @QueryValue("scaleName") String scaleName, + @Nullable @QueryValue("scalePUI") String scalePUI, + @Nullable @QueryValue("traitDbId") String traitDbId, + @Nullable @QueryValue("traitName") String traitName, + @Nullable @QueryValue("traitPUI") String traitPUI, + @Nullable @QueryValue("ontologyDbId") String ontologyDbId, + @Nullable @QueryValue("commonCropName") String commonCropName, +// @QueryValue("programDbId") Optional<@Nullable String> programDbId, //this is + @Nullable @QueryValue("trialDbId") String experimentId, + @Nullable @QueryValue("studyDbId") String environmentId, + @Nullable @QueryValue("externalReferenceID") String externalReferenceID, + @Nullable @QueryValue("externalReferenceId") String externalReferenceId, + @Nullable @QueryValue("externalReferenceSource") String externalReferenceSource, + @Nullable @QueryValue("page") Integer page, + @Nullable @QueryValue("pageSize") Integer pageSize, HttpRequest request) { + + try { + List programTraits; + if (observationVariablePUI != null || methodPUI != null || scalePUI != null || traitPUI != null || commonCropName != null || externalReferenceID != null || externalReferenceId != null || externalReferenceSource != null) { + log.debug("unsupported variable filters, returning"); + programTraits = new ArrayList<>(); + } else if(environmentId != null || experimentId != null) { + programTraits = getBrAPIObservationVariablesForExperiment(programId, Optional.ofNullable(experimentId), Optional.ofNullable(environmentId)); + } else { + log.debug("fetching variables for the program: " + programId); + programTraits = ontologyService.getTraitsByProgramId(programId, true); + + } + + List filteredObsVars = filterVariables(programTraits, + Optional.ofNullable(observationVariableDbId), + Optional.ofNullable(observationVariableName), + Optional.ofNullable(traitClass), + Optional.ofNullable(methodDbId), + Optional.ofNullable(methodName), + Optional.ofNullable(scaleDbId), + Optional.ofNullable(scaleName), + Optional.ofNullable(traitDbId), + Optional.ofNullable(traitName), + Optional.ofNullable(ontologyDbId)); + + BrAPIObservationVariableListResponse response = new BrAPIObservationVariableListResponse() + .metadata(new BrAPIMetadata() + .pagination(new BrAPIIndexPagination() + .currentPage(0) + .totalPages(1) + .pageSize(filteredObsVars.size()) + .totalCount(filteredObsVars.size()))) + .result(new BrAPIObservationVariableListResponseResult().data(filteredObsVars)); + + return HttpResponse.ok(response); + } catch (DoesNotExistException e) { + log.warn("Couldn't find object", e); + return HttpResponse.notFound(); + } catch (ApiException e) { + log.error(Utilities.generateApiExceptionLogMessage(e), e); + return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "error fetching variables"); + } } @Get("/variables/{observationVariableDbId}") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse variablesObservationVariableDbIdGet(@PathVariable("programId") UUID programId, + public HttpResponse variablesObservationVariableDbIdGet(@PathVariable("programId") UUID programId, @PathVariable("observationVariableDbId") String observationVariableDbId) { - return HttpResponse.notFound(); + log.debug("fetching variable: " + observationVariableDbId); + UUID traitId; + try { + traitId = UUID.fromString(observationVariableDbId); + } catch (IllegalArgumentException e) { + return HttpResponse.notFound(); + } + + try { + Optional trait = traitService.getById(programId, traitId); + + if(trait.isEmpty()) { + return HttpResponse.notFound(); + } + + BrAPIObservationVariableSingleResponse response = new BrAPIObservationVariableSingleResponse() + .metadata(new BrAPIMetadata()) + .result(convertToBrAPI(trait.get())); + + return HttpResponse.ok(response); + } catch (DoesNotExistException e) { + return HttpResponse.notFound(); + } } @Put("/variables/{observationVariableDbId}") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse variablesObservationVariableDbIdPut(@PathVariable("programId") UUID programId, + public HttpResponse variablesObservationVariableDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("observationVariableDbId") String observationVariableDbId, BrAPIObservationVariable body) { //DO NOT IMPLEMENT - Users are only able to update traits via the DeltaBreed UI @@ -86,8 +195,148 @@ public HttpResponse variablesObservationVariableDbIdPut(@PathVariable("programId @Post("/variables") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse variablesPost(@PathVariable("programId") UUID programId, List body) { + public HttpResponse variablesPost(@PathVariable("programId") UUID programId, List body) { //DO NOT IMPLEMENT - Users are only able to create new traits via the DeltaBreed UI return HttpResponse.notFound(); } + + private BrAPIObservationVariable convertToBrAPI(Trait trait) { + BrAPIOntologyReference brAPIOntologyReference = new BrAPIOntologyReference().ontologyDbId(trait.getProgramOntologyId() + .toString()); + String status = trait.getActive() ? "active" : "inactive"; + return new BrAPIObservationVariable().observationVariableDbId(trait.getId().toString()) + .observationVariableName(trait.getObservationVariableName()) + .defaultValue(trait.getDefaultValue()) + .status(status) + .synonyms(trait.getSynonyms()) + .trait(new BrAPITrait().ontologyReference(brAPIOntologyReference) + .traitName(trait.getObservationVariableName()) + .traitDbId(trait.getId().toString()) + .entity(trait.getEntity()) + .attribute(trait.getAttribute()) + .status(status) + .synonyms(trait.getSynonyms())) + .method(new BrAPIMethod().ontologyReference(brAPIOntologyReference) + .methodDbId(trait.getMethod().getId().toString()) + .description(trait.getMethod().getDescription()) + .formula(trait.getMethod().getFormula())) + .scale(new BrAPIScale().ontologyReference(brAPIOntologyReference) + .scaleDbId(trait.getScale().getId().toString()) + .scaleName(trait.getScale().getScaleName()) + .dataType(BrAPITraitDataType.fromValue(trait.getScale().getDataType().getLiteral())) + .decimalPlaces(trait.getScale().getDecimalPlaces()) + .validValues(new BrAPIScaleValidValues().max(trait.getScale().getValidValueMax()) + .min(trait.getScale().getValidValueMin()) + .categories(trait.getScale().getCategories()))); + } + + @NotNull + private List filterVariables(List programTraits, + Optional observationVariableDbId, + Optional observationVariableName, + Optional traitClass, + Optional methodDbId, + Optional methodName, + Optional scaleDbId, + Optional scaleName, + Optional traitDbId, + Optional traitName, + Optional ontologyDbId) throws DoesNotExistException { + log.debug("filtering variables:\n" + + "observationVariableDbId: " + observationVariableDbId + "\n" + + "observationVariableName: " + observationVariableName + "\n" + + "traitClass: " + traitClass + "\n" + + "methodDbId: " + methodDbId + "\n" + + "methodName: " + methodName + "\n" + + "scaleDbId: " + scaleDbId + "\n" + + "scaleName: " + scaleName + "\n" + + "traitDbId: " + traitDbId + "\n" + + "traitName: " + traitName + "\n" + + "ontologyDbId: " + ontologyDbId); + + return programTraits.stream() + .filter(trait -> { + boolean matches = true; + + Map, String>> filterParams = new HashMap<>(); + filterParams.put("observationVariableDbId", + Pair.of(observationVariableDbId, + trait.getId() + .toString())); + filterParams.put("observationVariableName", Pair.of(observationVariableName, trait.getObservationVariableName())); + filterParams.put("traitClass", Pair.of(traitClass, trait.getTraitClass())); + filterParams.put("methodDbId", + Pair.of(methodDbId, + trait.getMethodId() + .toString())); + filterParams.put("methodName", + Pair.of(methodName, + trait.getMethod() + .getDescription())); + filterParams.put("scaleDbId", + Pair.of(scaleDbId, + trait.getScale() + .getId() + .toString())); + filterParams.put("scaleName", + Pair.of(scaleName, + trait.getScale() + .getScaleName())); + filterParams.put("traitDbId", + Pair.of(traitDbId, + trait.getId() + .toString())); + filterParams.put("traitName", Pair.of(traitName, trait.getObservationVariableName())); + filterParams.put("ontologyDbId", + Pair.of(ontologyDbId, + trait.getProgramOntologyId() + .toString())); + + for (Map.Entry, String>> filter : filterParams.entrySet()) { + if (filter.getValue().getLeft().isPresent()) { + log.debug("filtering traits by: " + filter.getKey()); + matches = StringUtils.equals(filter.getValue() + .getLeft() + .get(), + filter.getValue() + .getRight()); + } + if (!matches) { + break; + } + } + + return matches; + }) + .map(this::convertToBrAPI) + .collect(Collectors.toList()); + } + + private List getBrAPIObservationVariablesForExperiment(UUID programId, Optional experimentId, Optional environmentId) throws DoesNotExistException, ApiException { + log.debug(String.format("fetching variables for experiment. expId: %s, envId: %s ", experimentId.orElse(""), environmentId.orElse(""))); + Optional program = programService.getById(programId); + if(program.isEmpty()) { + throw new DoesNotExistException("Could not find program: " + programId); + } + UUID expId; + if(experimentId.isPresent()) { + expId = UUID.fromString(experimentId.get()); + } else { + UUID envId = UUID.fromString(environmentId.get()); + BrAPIStudy environment = trialService.getEnvironment(program.get(), envId); + expId = UUID.fromString(Utilities.getExternalReference(environment.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.TRIALS)).get().getReferenceID()); + } + + BrAPITrial experiment = trialService.getExperiment(program.get(), expId); + if(experiment + .getAdditionalInfo().getAsJsonObject() + .has(BrAPIAdditionalInfoFields.OBSERVATION_DATASET_ID)) { + String obsDatasetId = experiment + .getAdditionalInfo().getAsJsonObject() + .get(BrAPIAdditionalInfoFields.OBSERVATION_DATASET_ID).getAsString(); + return trialService.getDatasetObsVars(obsDatasetId, program.get()); + } + + return new ArrayList<>(); + } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIProgramsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIProgramsController.java index 309172319..dabbebc45 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIProgramsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIProgramsController.java @@ -22,16 +22,40 @@ import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; +import org.brapi.v2.model.BrAPIIndexPagination; +import org.brapi.v2.model.BrAPIMetadata; import org.brapi.v2.model.core.BrAPIProgram; +import org.brapi.v2.model.core.response.BrAPIProgramListResponse; +import org.brapi.v2.model.core.response.BrAPIProgramListResponseResult; +import org.brapi.v2.model.core.response.BrAPIProgramSingleResponse; +import org.breedinginsight.api.auth.ProgramSecured; +import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; +import org.breedinginsight.api.auth.SecurityService; import org.breedinginsight.brapi.v1.controller.BrapiVersion; +import org.breedinginsight.model.Program; +import org.breedinginsight.services.ProgramService; +import javax.annotation.Nullable; +import javax.inject.Inject; import java.util.List; +import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; @Slf4j @Controller("/${micronaut.bi.api.version}") @Secured(SecurityRule.IS_AUTHENTICATED) public class BrAPIProgramsController { + + private final SecurityService securityService; + private final ProgramService programService; + + @Inject + public BrAPIProgramsController(SecurityService securityService, ProgramService programService) { + this.securityService = securityService; + this.programService = programService; + } + /* TODO - GET programs @@ -39,32 +63,61 @@ public class BrAPIProgramsController { //START - endpoints at root BrAPI url @Get(BrapiVersion.BRAPI_V2 + "/programs") - public HttpResponse rootProgramsGet(@QueryValue("abbreviation") String abbreviation, - @QueryValue("programType") String programType, - @QueryValue("commonCropName") String commonCropName, - @QueryValue("programDbId") String programDbId, - @QueryValue("programName") String programName, - @QueryValue("externalReferenceID") String externalReferenceID, - @QueryValue("externalReferenceId") String externalReferenceId, - @QueryValue("externalReferenceSource") String externalReferenceSource, - @QueryValue("page") Integer page, - @QueryValue("pageSize") Integer pageSize) { - return HttpResponse.notFound(); + public HttpResponse rootProgramsGet( + @Nullable @QueryValue("abbreviation") String abbreviation, + @Nullable @QueryValue("programType") String programType, + @Nullable @QueryValue("commonCropName") String commonCropName, + @Nullable @QueryValue("programDbId") String programDbId, + @Nullable @QueryValue("programName") String programName, + @Nullable @QueryValue("externalReferenceID") String externalReferenceID, + @Nullable @QueryValue("externalReferenceId") String externalReferenceId, + @Nullable @QueryValue("externalReferenceSource") String externalReferenceSource, + @Nullable @QueryValue("page") Integer page, + @Nullable @QueryValue("pageSize") Integer pageSize) { + + Optional abbreviationOptional = Optional.ofNullable(abbreviation); + Optional programDbIdOptional = Optional.ofNullable(programDbId); + Optional programNameOptional = Optional.ofNullable(programName); + + List programs = programService.getAll(securityService.getUser()).stream().filter(program -> { + boolean matches = abbreviationOptional.map(abbr -> abbr.equals(program.getKey())).orElse(true); + matches = matches && programDbIdOptional.map(id -> id.equals(program.getId().toString())).orElse(true); + return matches && programNameOptional.map(name -> name.equals(program.getName())).orElse(true); + }).map(this::convertToBrAPI).collect(Collectors.toList()); + + return HttpResponse.ok(new BrAPIProgramListResponse().metadata(new BrAPIMetadata().pagination(new BrAPIIndexPagination().currentPage(0) + .totalPages(1) + .totalCount(programs.size()) + .pageSize(programs.size()))) + .result(new BrAPIProgramListResponseResult().data(programs))); } @Post(BrapiVersion.BRAPI_V2 + "/programs") - public HttpResponse rootProgramsPost(List body) { + public HttpResponse rootProgramsPost(List body) { //DO NOT IMPLEMENT - Users should only be able to create new programs via the DeltaBreed UI return HttpResponse.notFound(); } @Get(BrapiVersion.BRAPI_V2 + "/programs/{programDbId}") - public HttpResponse rootProgramsProgramDbIdGet(@PathVariable("programDbId") String programDbId) { - return HttpResponse.notFound(); + public HttpResponse rootProgramsProgramDbIdGet(@PathVariable("programDbId") String programDbId) { + HttpResponse brAPIProgramListResponseHttpResponse = this.rootProgramsGet(null, null, null, + programDbId, + null, + null, + null, + null, + null, + null); + Optional program = Optional.ofNullable(brAPIProgramListResponseHttpResponse.body()) + .orElse(new BrAPIProgramListResponse()) + .getResult() + .getData().stream().findFirst(); + + return HttpResponse.ok(new BrAPIProgramSingleResponse().result(program.orElse(null))); } @Put(BrapiVersion.BRAPI_V2 + "/programs/{programDbId}") - public HttpResponse rootProgramsProgramDbIdPut(@PathVariable("programDbId") String programDbId, BrAPIProgram body) { + public HttpResponse rootProgramsProgramDbIdPut(@PathVariable("programDbId") String programDbId, BrAPIProgram body) { //DO NOT IMPLEMENT - Users should only be able to update programs via the DeltaBreed UI return HttpResponse.notFound(); } @@ -73,34 +126,63 @@ public HttpResponse rootProgramsProgramDbIdPut(@PathVariable("programDbId") Stri //START - endpoints for within the context of a program @Get("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/programs") - public HttpResponse programsGet(@PathVariable("programId") UUID programId, @QueryValue("abbreviation") String abbreviation, - @QueryValue("programType") String programType, - @QueryValue("commonCropName") String commonCropName, - @QueryValue("programDbId") String programDbId, - @QueryValue("programName") String programName, - @QueryValue("externalReferenceID") String externalReferenceID, - @QueryValue("externalReferenceId") String externalReferenceId, - @QueryValue("externalReferenceSource") String externalReferenceSource, - @QueryValue("page") Integer page, - @QueryValue("pageSize") Integer pageSize) { - return HttpResponse.notFound(); + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse programsGet(@PathVariable("programId") UUID programId, + @QueryValue("abbreviation") Optional abbreviation, + @QueryValue("programType") Optional programType, + @QueryValue("commonCropName") Optional commonCropName, + @QueryValue("programDbId") Optional programDbId, + @QueryValue("programName") Optional programName, + @QueryValue("externalReferenceID") Optional externalReferenceID, + @QueryValue("externalReferenceId") Optional externalReferenceId, + @QueryValue("externalReferenceSource") Optional externalReferenceSource, + @QueryValue("page") Optional page, + @QueryValue("pageSize") Optional pageSize) { + + List programs = programService.getById(programId).stream().filter(program -> { + boolean matches = abbreviation.map(abbr -> abbr.equals(program.getKey())).orElse(true); + matches = matches && programDbId.map(id -> id.equals(program.getId().toString())).orElse(true); + return matches && programName.map(name -> name.equals(program.getName())).orElse(true); + }).map(this::convertToBrAPI).collect(Collectors.toList()); + + return HttpResponse.ok(new BrAPIProgramListResponse().metadata(new BrAPIMetadata().pagination(new BrAPIIndexPagination().currentPage(0) + .totalPages(1) + .totalCount(programs.size()) + .pageSize(programs.size()))) + .result(new BrAPIProgramListResponseResult().data(programs))); } @Post("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/programs") - public HttpResponse programsPost(@PathVariable("programId") UUID programId, List body) { + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse programsPost(@PathVariable("programId") UUID programId, List body) { //DO NOT IMPLEMENT - Users should only be able to create new programs via the DeltaBreed UI return HttpResponse.notFound(); } @Get("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/programs/{programDbId}") - public HttpResponse programsProgramDbIdGet(@PathVariable("programId") UUID programId, @PathVariable("programDbId") String programDbId) { - return HttpResponse.notFound(); + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse programsProgramDbIdGet(@PathVariable("programId") UUID programId, @PathVariable("programDbId") String programDbId) { + Optional program = programService.getById(programId) + .stream() + .filter(p -> programDbId.equals(programId.toString())) + .map(this::convertToBrAPI) + .findFirst(); + + return HttpResponse.ok(new BrAPIProgramSingleResponse().result(program.orElse(null))); } @Put("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/programs/{programDbId}") - public HttpResponse programsProgramDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("programDbId") String programDbId, BrAPIProgram body) { + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) + public HttpResponse programsProgramDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("programDbId") String programDbId, BrAPIProgram body) { //DO NOT IMPLEMENT - Users should only be able to update programs via the DeltaBreed UI return HttpResponse.notFound(); } //END - endpoints for within the context of a program + + private BrAPIProgram convertToBrAPI(Program program) { + return new BrAPIProgram().programName(program.getName()) + .programDbId(program.getId().toString()) + .abbreviation(program.getKey()) + .commonCropName(program.getSpecies().getCommonName()); + } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIStudiesController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIStudiesController.java index 4f64fba1c..ea6f03a52 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIStudiesController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIStudiesController.java @@ -17,6 +17,7 @@ package org.breedinginsight.brapi.v2; +import io.micronaut.context.annotation.Property; import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; import io.micronaut.http.MediaType; @@ -25,7 +26,10 @@ import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.BrAPIMetadata; +import org.brapi.v2.model.BrAPIStatus; import org.brapi.v2.model.core.BrAPIStudy; +import org.brapi.v2.model.core.response.BrAPIStudySingleResponse; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; import org.breedinginsight.api.model.v1.request.query.SearchRequest; @@ -35,30 +39,40 @@ import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapi.v2.model.request.query.StudyQuery; import org.breedinginsight.brapi.v2.services.BrAPIStudyService; +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.ResponseUtils; import org.breedinginsight.utilities.response.mappers.StudyQueryMapper; import javax.inject.Inject; import javax.validation.Valid; import java.util.List; +import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; @Slf4j -@Controller("/${micronaut.bi.api.version}") +@Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @Secured(SecurityRule.IS_AUTHENTICATED) public class BrAPIStudiesController { + private final String referenceSource; private final BrAPIStudyService studyService; private final StudyQueryMapper studyQueryMapper; + private final ProgramService programService; @Inject - public BrAPIStudiesController(BrAPIStudyService studyService, StudyQueryMapper studyQueryMapper) { + public BrAPIStudiesController(BrAPIStudyService studyService, StudyQueryMapper studyQueryMapper, @Property(name = "brapi.server.reference-source") String referenceSource, ProgramService programService) { this.studyService = studyService; this.studyQueryMapper = studyQueryMapper; + this.referenceSource = referenceSource; + this.programService = programService; } - @Get("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/studies{?queryParams*}") + @Get("/studies{?queryParams*}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) public HttpResponse>>> getStudies( @@ -67,7 +81,10 @@ public HttpResponse>>> getStudies( try { log.debug("fetching studies for program: " + programId); - List studies = studyService.getStudies(programId); + List studies = studyService.getStudies(programId) + .stream() + .peek(this::setDbIds) + .collect(Collectors.toList()); queryParams.setSortField(studyQueryMapper.getDefaultSortField()); queryParams.setSortOrder(studyQueryMapper.getDefaultSortOrder()); SearchRequest searchRequest = queryParams.constructSearchRequest(); @@ -81,33 +98,54 @@ public HttpResponse>>> getStudies( } } - /* - TODO - - GET studies/{id} - */ @Post("/studies") - @Consumes({"application/json"}) - @Produces({"application/json"}) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse studiesPost(@PathVariable("programId") UUID programId, List body) { + public HttpResponse studiesPost(@PathVariable("programId") UUID programId, List body) { //DO NOT IMPLEMENT - Users are only able to create new studies via the DeltaBreed UI return HttpResponse.notFound(); } @Get("/studies/{studyDbId}") - @Produces({"application/json"}) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse studiesStudyDbIdGet(@PathVariable("programId") UUID programId, @PathVariable("studyDbId") String studyDbId) { - return HttpResponse.notFound(); + public HttpResponse studiesStudyDbIdGet(@PathVariable("programId") UUID programId, @PathVariable("studyDbId") String environmentId) { + Optional program = programService.getById(programId); + if(program.isEmpty()) { + log.warn("program id: " + programId + " not found"); + return HttpResponse.notFound(); + } + try { + Optional study = studyService.getStudyByEnvironmentId(program.get(), UUID.fromString(environmentId)); + if(study.isPresent()) { + setDbIds(study.get()); + return HttpResponse.ok(new BrAPIStudySingleResponse().result(study.get())); + } else { + log.warn("studyDbId: " + environmentId + " not found"); + return HttpResponse.notFound(); + } + } catch (ApiException e) { + log.error(Utilities.generateApiExceptionLogMessage(e), e); + return HttpResponse.serverError(new BrAPIStudySingleResponse().metadata(new BrAPIMetadata().addStatusItem(new BrAPIStatus().message("Error fetching study") + .messageType(BrAPIStatus.MessageTypeEnum.ERROR)))); + } } @Put("/studies/{studyDbId}") - @Consumes({"application/json"}) - @Produces({"application/json"}) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse studiesStudyDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("studyDbId") String studyDbId, + public HttpResponse studiesStudyDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("studyDbId") String studyDbId, BrAPIStudy body) { //DO NOT IMPLEMENT - Users are only able to update studies via the DeltaBreed UI return HttpResponse.notFound(); } + + private void setDbIds(BrAPIStudy study) { + study.studyDbId(Utilities.getExternalReference(study.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.STUDIES)) + .orElseThrow(() -> new IllegalStateException("No BI external reference found")) + .getReferenceID()); + + study.trialDbId(Utilities.getExternalReference(study.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.TRIALS)) + .orElseThrow(() -> new IllegalStateException("No BI external reference found")) + .getReferenceID()); + + //TODO update locationDbId + } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java index 71b979641..77dbf2b90 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java @@ -1,5 +1,6 @@ package org.breedinginsight.brapi.v2; +import io.micronaut.context.annotation.Property; import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; import io.micronaut.http.MediaType; @@ -8,7 +9,9 @@ import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.core.BrAPIStudy; import org.brapi.v2.model.core.BrAPITrial; +import org.brapi.v2.model.core.response.BrAPITrialSingleResponse; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; import org.breedinginsight.api.model.v1.request.query.SearchRequest; @@ -18,27 +21,31 @@ import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapi.v2.model.request.query.ExperimentQuery; import org.breedinginsight.brapi.v2.services.BrAPITrialService; +import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.exceptions.DoesNotExistException; +import org.breedinginsight.utilities.Utilities; import org.breedinginsight.utilities.response.ResponseUtils; import org.breedinginsight.utilities.response.mappers.ExperimentQueryMapper; import javax.inject.Inject; import javax.validation.Valid; import java.util.*; +import java.util.stream.Collectors; @Slf4j @Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @Secured(SecurityRule.IS_AUTHENTICATED) public class BrAPITrialsController { + + private final String referenceSource; private final BrAPITrialService experimentService; private final ExperimentQueryMapper experimentQueryMapper; - private final ProgramService programService; @Inject - public BrAPITrialsController(BrAPITrialService experimentService, ExperimentQueryMapper experimentQueryMapper, ProgramService programService) { + public BrAPITrialsController(BrAPITrialService experimentService, ExperimentQueryMapper experimentQueryMapper, @Property(name = "brapi.server.reference-source") String referenceSource) { this.experimentService = experimentService; this.experimentQueryMapper = experimentQueryMapper; - this.programService = programService; + this.referenceSource = referenceSource; } @Get("/trials{?queryParams*}") @@ -50,7 +57,7 @@ public HttpResponse>>> getExperiments( try { log.debug("fetching trials for program: " + programId); - List experiments = experimentService.getExperiments(programId); + List experiments = experimentService.getExperiments(programId).stream().peek(this::setDbIds).collect(Collectors.toList()); SearchRequest searchRequest = queryParams.constructSearchRequest(); return ResponseUtils.getBrapiQueryResponse(experiments, experimentQueryMapper, queryParams, searchRequest); } catch (ApiException e) { @@ -65,7 +72,7 @@ public HttpResponse>>> getExperiments( @Get("/trials/{trialId}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse> getExperimentById( + public HttpResponse getExperimentById( @PathVariable("programId") UUID programId, @PathVariable("trialId") UUID trialId, @QueryValue(defaultValue = "false") Boolean stats){ @@ -75,8 +82,9 @@ public HttpResponse> getExperimentById( logMsg += " with stats"; } log.debug(logMsg); - Response response = new Response<>(experimentService.getTrialDataByUUID(programId, trialId, stats)); - return HttpResponse.ok(response); + BrAPITrial trial = experimentService.getTrialDataByUUID(programId, trialId, stats); + setDbIds(trial); + return HttpResponse.ok(new BrAPITrialSingleResponse().result(trial)); } catch (DoesNotExistException e) { log.info(e.getMessage(), e); return HttpResponse.status(HttpStatus.NOT_FOUND, e.getMessage()); @@ -85,7 +93,7 @@ public HttpResponse> getExperimentById( @Post("/trials") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse trialsPost(@PathVariable("programId") UUID programId, List body) { + public HttpResponse trialsPost(@PathVariable("programId") UUID programId, List body) { //DO NOT IMPLEMENT - Users are only able to create new trials via the DeltaBreed UI return HttpResponse.notFound(); } @@ -93,9 +101,20 @@ public HttpResponse trialsPost(@PathVariable("programId") UUID programId, List trialsTrialDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("trialDbId") String trialDbId, BrAPITrial body) { //DO NOT IMPLEMENT - Users are only able to update trials via the DeltaBreed UI return HttpResponse.notFound(); } + private void setDbIds(BrAPITrial trial) { + trial.trialDbId(Utilities.getExternalReference(trial.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.TRIALS)) + .orElseThrow(() -> new IllegalStateException("No BI external reference found")) + .getReferenceID()); + trial.programDbId(Utilities.getExternalReference(trial.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.PROGRAMS)) + .orElseThrow(() -> new IllegalStateException("No BI external reference found")) + .getReferenceID()); + + //TODO update locationDbId + } + } diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIStudyService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIStudyService.java index f2069e5bb..db5e28351 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIStudyService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIStudyService.java @@ -17,15 +17,16 @@ package org.breedinginsight.brapi.v2.services; -import io.micronaut.http.server.exceptions.InternalServerException; import lombok.extern.slf4j.Slf4j; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.core.BrAPIStudy; import org.breedinginsight.brapps.importer.daos.BrAPIStudyDAO; +import org.breedinginsight.model.Program; import javax.inject.Inject; import javax.inject.Singleton; import java.util.List; +import java.util.Optional; import java.util.UUID; @Slf4j @@ -40,11 +41,11 @@ public BrAPIStudyService(BrAPIStudyDAO studyDAO) { } public List getStudies(UUID programId) throws ApiException { - try { - return studyDAO.getStudies(programId); - } catch (ApiException e) { - throw new InternalServerException(e.getMessage(), e); - } + return studyDAO.getStudies(programId); + } + + public Optional getStudyByEnvironmentId(Program program, UUID environmentId) throws ApiException { + return studyDAO.getStudyByEnvironmentId(environmentId, program); } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java index 95d34cd32..f1939996c 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java @@ -22,11 +22,13 @@ import org.breedinginsight.brapps.importer.model.imports.experimentObservation.ExperimentObservation.Columns; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.brapps.importer.services.FileMappingUtil; +import org.breedinginsight.dao.db.enums.DataType; import org.breedinginsight.model.BrAPIConstants; import org.breedinginsight.model.Column; import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.Program; import org.breedinginsight.model.*; +import org.breedinginsight.services.TraitService; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.services.parsers.experiment.ExperimentFileColumns; import org.breedinginsight.utilities.IntOrderComparator; @@ -54,7 +56,9 @@ public class BrAPITrialService { private final BrAPITrialDAO trialDAO; private final BrAPIObservationDAO observationDAO; private final BrAPIListDAO listDAO; - private final BrAPIObservationVariableDAO obsVarDAO; +// private final BrAPIObservationVariableDAO obsVarDAO; + + private final TraitService traitService; private final BrAPIStudyDAO studyDAO; private final BrAPISeasonDAO seasonDAO; private final BrAPIObservationUnitDAO ouDAO; @@ -67,7 +71,8 @@ public BrAPITrialService(@Property(name = "brapi.server.reference-source") Strin BrAPITrialDAO trialDAO, BrAPIObservationDAO observationDAO, BrAPIListDAO listDAO, - BrAPIObservationVariableDAO obsVarDAO, +// BrAPIObservationVariableDAO obsVarDAO, + TraitService traitService, BrAPIStudyDAO studyDAO, BrAPISeasonDAO seasonDAO, BrAPIObservationUnitDAO ouDAO, @@ -78,7 +83,8 @@ public BrAPITrialService(@Property(name = "brapi.server.reference-source") Strin this.trialDAO = trialDAO; this.observationDAO = observationDAO; this.listDAO = listDAO; - this.obsVarDAO = obsVarDAO; +// this.obsVarDAO = obsVarDAO; + this.traitService = traitService; this.studyDAO = studyDAO; this.seasonDAO = seasonDAO; this.ouDAO = ouDAO; @@ -123,7 +129,7 @@ public DownloadFile exportObservations( DownloadFile downloadFile; boolean isDataset = false; List dataset = new ArrayList<>(); - List obsVars = new ArrayList<>(); + List obsVars = new ArrayList<>(); Map> rowByOUId = new HashMap<>(); Map studyByDbId = new HashMap<>(); Map studyDbIdByOUId = new HashMap<>(); @@ -301,9 +307,9 @@ public Dataset getDatasetData(Program program, UUID experimentId, UUID datsetId, log.debug("fetching observationUnits for dataset: " + datsetId); List datasetOUs = ouDAO.getObservationUnitsForDataset(datsetId.toString(), program); log.debug("fetching dataset variables dataset: " + datsetId); - List datasetObsVars = getDatasetObsVars(datsetId.toString(), program); + List datasetObsVars = getDatasetObsVars(datsetId.toString(), program); List ouDbIds = datasetOUs.stream().map(BrAPIObservationUnit::getObservationUnitDbId).collect(Collectors.toList()); - List obsVarDbIds = datasetObsVars.stream().map(BrAPIObservationVariable::getObservationVariableDbId).collect(Collectors.toList()); + List obsVarDbIds = datasetObsVars.stream().map(Trait::getObservationVariableDbId).collect(Collectors.toList()); log.debug("fetching observations for dataset: " + datsetId); List data = observationDAO.getObservationsByObservationUnitsAndVariables(ouDbIds, obsVarDbIds, program); log.debug("building dataset object for dataset: " + datsetId); @@ -333,10 +339,10 @@ private void addBrAPIObsToRecords( Map studyByDbId, Map> rowByOUId, boolean includeTimestamp, - List obsVars, + List obsVars, Map studyDbIdByOUId, Map programGermplasmByDbId) throws ApiException, DoesNotExistException { - Map varByDbId = new HashMap<>(); + Map varByDbId = new HashMap<>(); obsVars.forEach(var -> varByDbId.put(var.getObservationVariableDbId(), var)); for (BrAPIObservation obs: dataset) { @@ -345,7 +351,7 @@ private void addBrAPIObsToRecords( String ouId = getOUId(ou); // get observation variable for BrAPI observation - BrAPIObservationVariable var = varByDbId.get(obs.getObservationVariableDbId()); + Trait var = varByDbId.get(obs.getObservationVariableDbId()); // if there is a row with that ouId then just add the obs var data and timestamp to the row if (rowByOUId.get(ouId) != null) { @@ -385,11 +391,11 @@ private void addObsVarDataToRow( Map row, BrAPIObservation obs, boolean includeTimestamp, - BrAPIObservationVariable var, + Trait var, Program program) { String varName = Utilities.removeProgramKey(obs.getObservationVariableName(), program.getKey()); - if (var.getScale().getDataType().equals(BrAPITraitDataType.NUMERICAL) || - var.getScale().getDataType().equals(BrAPITraitDataType.DURATION)) { + if (var.getScale().getDataType().equals(DataType.NUMERICAL) || + var.getScale().getDataType().equals(DataType.DURATION)) { row.put(varName, Double.parseDouble(obs.getValue())); } else { row.put(varName, obs.getValue()); @@ -401,24 +407,26 @@ private void addObsVarDataToRow( } } - public List getDatasetObsVars(String datasetId, Program program) throws ApiException, DoesNotExistException { + public List getDatasetObsVars(String datasetId, Program program) throws ApiException, DoesNotExistException { List lists = listDAO.getListByTypeAndExternalRef( BrAPIListTypes.OBSERVATIONVARIABLES, program.getId(), String.format("%s/%s", referenceSource, ExternalReferenceSource.DATASET.getName()), UUID.fromString(datasetId)); if (lists == null || lists.isEmpty()) { - throw new DoesNotExistException("Dataset observation variables list not returned from BrAPI service"); +// throw new DoesNotExistException("Dataset observation variables list not returned from BrAPI service"); + log.warn(String.format("Dataset %s observation variables list not returned from BrAPI service", datasetId)); + return new ArrayList<>(); } String listDbId = lists.get(0).getListDbId(); BrAPIListsSingleResponse list = listDAO.getListById(listDbId, program.getId()); - List obsVarNames = list.getResult().getData(); + List obsVarNames = list.getResult().getData().stream().map(var -> Utilities.removeProgramKey(var, program.getKey())).collect(Collectors.toList()); log.debug("Searching for dataset obsVars: " + obsVarNames); - List obsVars = obsVarDAO.getVariableByName(obsVarNames, program.getId()); + List obsVars = traitService.getByName(program.getId(), obsVarNames); log.debug(String.format("Found %d obsVars", obsVars.size())); // sort the obsVars to match the order stored in the dataset list - return fileMappingUtil.sortByField(obsVarNames, obsVars, BrAPIObservationVariable::getObservationVariableName); + return fileMappingUtil.sortByField(obsVarNames, obsVars, Trait::getObservationVariableName); } public BrAPITrial getExperiment(Program program, UUID experimentId) throws ApiException { @@ -500,14 +508,14 @@ private Map createExportRow( private void addObsVarColumns( List columns, - List obsVars, + List obsVars, boolean includeTimestamps, Program program) { - for (BrAPIObservationVariable var: obsVars) { + for (Trait var: obsVars) { Column obsVarColumn = new Column(); obsVarColumn.setDataType(Column.ColumnDataType.STRING); - if (var.getScale().getDataType().equals(BrAPITraitDataType.NUMERICAL) || - var.getScale().getDataType().equals(BrAPITraitDataType.DURATION)) { + if (var.getScale().getDataType().equals(DataType.NUMERICAL) || + var.getScale().getDataType().equals(DataType.DURATION)) { obsVarColumn.setDataType(Column.ColumnDataType.DOUBLE); } String varName = Utilities.removeProgramKey(var.getObservationVariableName(), program.getKey()); @@ -564,4 +572,13 @@ private void sortDefaultForExportRows(@NotNull List> exportR exportRows.sort(envComparator.thenComparing(expUnitIdComparator)); } + + public BrAPIStudy getEnvironment(Program program, UUID envId) throws ApiException { + List environments = studyDAO.getStudiesByEnvironmentIds(List.of(envId), program); + if (environments.isEmpty()) { + throw new RuntimeException("A study with given experiment id was not returned"); + } + + return environments.get(0); + } } diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java index 21fe0dbc2..f8ed51f25 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java +++ b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java @@ -26,16 +26,13 @@ import io.micronaut.http.server.exceptions.InternalServerException; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.modules.phenotype.ObservationUnitsApi; -import org.brapi.v2.model.BrAPIExternalReference; import org.brapi.v2.model.germ.BrAPIGermplasm; import org.brapi.v2.model.pheno.BrAPIObservationUnit; +import org.brapi.v2.model.pheno.BrAPIObservationUnitLevelRelationship; import org.brapi.v2.model.pheno.request.BrAPIObservationUnitSearchRequest; import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; import org.brapi.v2.model.pheno.BrAPIObservationTreatment; -import org.brapi.v2.model.pheno.BrAPIObservationUnit; -import org.brapi.v2.model.pheno.request.BrAPIObservationUnitSearchRequest; -import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.daos.ProgramDAO; @@ -51,6 +48,8 @@ import javax.validation.constraints.NotNull; import java.lang.reflect.Type; import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; @Singleton public class BrAPIObservationUnitDAO { @@ -92,17 +91,18 @@ public List getObservationUnitByName(List observat observationUnitSearchRequest.programDbIds(List.of(program.getBrapiProgram().getProgramDbId())); observationUnitSearchRequest.observationUnitNames(observationUnitNames); - return searchObservationUnitsAndProcess(observationUnitSearchRequest, program.getId(), false); + return searchObservationUnitsAndProcess(observationUnitSearchRequest, program, false); } /** * Create observation units, mutates brAPIObservationUnitList */ - public List createBrAPIObservationUnits(List brAPIObservationUnitList, UUID programId, ImportUpload upload) throws ApiException { + public List createBrAPIObservationUnits(List brAPIObservationUnitList, UUID programId, ImportUpload upload) throws ApiException, DoesNotExistException { + Program program = programService.getById(programId).orElseThrow(() -> new DoesNotExistException("Program id does not exist")); ObservationUnitsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ObservationUnitsApi.class); preprocessObservationUnits(brAPIObservationUnitList); List ous = brAPIDAOUtil.post(brAPIObservationUnitList, upload, api::observationunitsPost, importDAO::update); - processObservationUnits(programId, ous, false); + processObservationUnits(program, ous, false); return ous; } @@ -130,7 +130,7 @@ public List getObservationUnitsById(Collection obs observationUnitSearchRequest.externalReferenceIDs(new ArrayList<>(observationUnitExternalIds)); observationUnitSearchRequest.externalReferenceSources(List.of(String.format("%s/%s", referenceSource, ExternalReferenceSource.OBSERVATION_UNITS.getName()))); - return searchObservationUnitsAndProcess(observationUnitSearchRequest, program.getId(), false); + return searchObservationUnitsAndProcess(observationUnitSearchRequest, program, false); } public List getObservationUnitsForStudyDbId(@NotNull String studyDbId, Program program) throws ApiException { @@ -139,13 +139,24 @@ public List getObservationUnitsForStudyDbId(@NotNull Strin .getProgramDbId())); observationUnitSearchRequest.studyDbIds(List.of(studyDbId)); - return searchObservationUnitsAndProcess(observationUnitSearchRequest, program.getId(), false); + return searchObservationUnitsAndProcess(observationUnitSearchRequest, program, false); } public List getObservationUnitsForTrialDbId(@NotNull UUID programId, @NotNull String trialDbId) throws ApiException, DoesNotExistException { return getObservationUnitsForTrialDbId(programId, trialDbId, false); } + public List getObservationUnitsForTrialDbId(@NotNull UUID programId, @NotNull String trialDbId, boolean withGID) throws ApiException, DoesNotExistException { + Program program = programService.getById(programId).orElseThrow(() -> new DoesNotExistException("Program id does not exist")); + + BrAPIObservationUnitSearchRequest observationUnitSearchRequest = new BrAPIObservationUnitSearchRequest(); + observationUnitSearchRequest.programDbIds(List.of(program.getBrapiProgram() + .getProgramDbId())); + observationUnitSearchRequest.trialDbIds(List.of(trialDbId)); + + return searchObservationUnitsAndProcess(observationUnitSearchRequest, program, withGID); + } + public List getObservationUnitsForDataset(@NotNull String datasetId, @NotNull Program program) throws ApiException { String datasetReferenceSource = Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.DATASET); BrAPIObservationUnitSearchRequest ouSearchRequest = new BrAPIObservationUnitSearchRequest(); @@ -155,15 +166,99 @@ public List getObservationUnitsForDataset(@NotNull String return searchObservationUnitsAndProcess(ouSearchRequest, program.getId(), true); } - public List getObservationUnitsForTrialDbId(@NotNull UUID programId, @NotNull String trialDbId, boolean withGID) throws ApiException, DoesNotExistException { - Program program = programService.getById(programId).orElseThrow(() -> new DoesNotExistException("Program id does not exist")); - + public List getObservationUnits(Program program, + Optional observationUnitId, + Optional observationUnitName, + Optional locationDbId, + Optional seasonDbId, + Optional includeObservations, + Optional observationUnitLevelName, + Optional observationUnitLevelOrder, + Optional observationUnitLevelCode, + Optional observationUnitLevelRelationshipName, + Optional observationUnitLevelRelationshipOrder, + Optional observationUnitLevelRelationshipCode, + Optional observationUnitLevelRelationshipDbId, + Optional commonCropName, + Optional experimentId, + Optional environmentId, + Optional germplasmId +// , Integer page, +// Integer pageSize + ) throws ApiException { BrAPIObservationUnitSearchRequest observationUnitSearchRequest = new BrAPIObservationUnitSearchRequest(); observationUnitSearchRequest.programDbIds(List.of(program.getBrapiProgram() - .getProgramDbId())); - observationUnitSearchRequest.trialDbIds(List.of(trialDbId)); + .getProgramDbId())); + //TODO add pagination support +// .page(page) +// .pageSize(pageSize); - return searchObservationUnitsAndProcess(observationUnitSearchRequest, programId, withGID); + List xrefIds = new ArrayList<>(); + List xrefSources = new ArrayList<>(); + BrAPIObservationUnitLevelRelationship level = new BrAPIObservationUnitLevelRelationship(); + AtomicBoolean levelFilter = new AtomicBoolean(false); + BrAPIObservationUnitLevelRelationship relationship = new BrAPIObservationUnitLevelRelationship(); + AtomicBoolean relationshipFilter = new AtomicBoolean(false); + + observationUnitId.ifPresent(ouId -> addXRefFilter(ouId, ExternalReferenceSource.OBSERVATION_UNITS, xrefIds, xrefSources)); + observationUnitName.ifPresent(name -> observationUnitSearchRequest.setObservationUnitNames(List.of(Utilities.appendProgramKey(name, program.getKey())))); + locationDbId.ifPresent(dbid -> observationUnitSearchRequest.setLocationDbIds(List.of(dbid))); + seasonDbId.ifPresent(dbid -> observationUnitSearchRequest.setSeasonDbIds(List.of(dbid))); + includeObservations.ifPresent(observationUnitSearchRequest::includeObservations); + addLevelFilter(observationUnitLevelName, observationUnitLevelOrder, observationUnitLevelCode, level, levelFilter); + addLevelFilter(observationUnitLevelRelationshipName, observationUnitLevelRelationshipOrder, observationUnitLevelRelationshipCode, relationship, relationshipFilter); + experimentId.ifPresent(expId -> addXRefFilter(expId, ExternalReferenceSource.TRIALS, xrefIds, xrefSources)); + environmentId.ifPresent(envId -> addXRefFilter(envId, ExternalReferenceSource.STUDIES, xrefIds, xrefSources)); +// germplasmId.ifPresent(germId -> { +// xrefIds.add(germId); +// xrefSources.add(Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.)); +// }); + + if(!xrefIds.isEmpty()) { + observationUnitSearchRequest.externalReferenceIDs(xrefIds); + } + if(!xrefSources.isEmpty()) { + observationUnitSearchRequest.externalReferenceSources(xrefSources); + } + + return searchObservationUnitsAndProcess(observationUnitSearchRequest, program, true).stream().filter(ou -> { + //xref search does an OR, so we need to convert the searching for ouId/expId/envId to be an AND + boolean matches = observationUnitId.map(id -> id.equals(Utilities.getExternalReference(ou.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.OBSERVATION_UNITS)) + .get() + .getReferenceID())) + .orElse(true); + matches = matches && experimentId.map(id -> id.equals(Utilities.getExternalReference(ou.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.TRIALS)) + .get() + .getReferenceID())) + .orElse(true); + matches = matches && environmentId.map(id -> id.equals(Utilities.getExternalReference(ou.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.STUDIES)) + .get() + .getReferenceID())) + .orElse(true); + + //adding filter for germplasmDbId because we can't easily search that in the stored data object + return matches && germplasmId.map(id -> id.equals(ou.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_UUID).getAsString())).orElse(true); + }).collect(Collectors.toList()); + } + + private void addXRefFilter(String ouId, ExternalReferenceSource externalReferenceSource, List xrefIds, List xrefSources) { + xrefIds.add(ouId); + xrefSources.add(Utilities.generateReferenceSource(referenceSource, externalReferenceSource)); + } + + private void addLevelFilter(Optional observationUnitLevelName, Optional observationUnitLevelOrder, Optional observationUnitLevelCode, BrAPIObservationUnitLevelRelationship level, AtomicBoolean levelFilter) { + observationUnitLevelName.ifPresent(name -> { + levelFilter.set(true); + level.setLevelName(name); + }); + observationUnitLevelOrder.ifPresent(order -> { + levelFilter.set(true); + level.setLevelOrder(order); + }); + observationUnitLevelCode.ifPresent(code -> { + levelFilter.set(true); + level.setLevelCode(code); + }); } @@ -171,24 +266,24 @@ public List getObservationUnitsForTrialDbId(@NotNull UUID * Perform observation unit search and process returned observation units to handle any modifications to the data * to be returned by bi-api */ - private List searchObservationUnitsAndProcess(BrAPIObservationUnitSearchRequest request, UUID programId, boolean withGID) throws ApiException { + private List searchObservationUnitsAndProcess(BrAPIObservationUnitSearchRequest request, Program program, boolean withGID) throws ApiException { - ObservationUnitsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ObservationUnitsApi.class); + ObservationUnitsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(program.getId()), ObservationUnitsApi.class); List brapiObservationUnits = brAPIDAOUtil.search(api::searchObservationunitsPost, api::searchObservationunitsSearchResultsDbIdGet, request); - processObservationUnits(programId, brapiObservationUnits, withGID); + processObservationUnits(program, brapiObservationUnits, withGID); return brapiObservationUnits; } - private void processObservationUnits(UUID programId, List brapiObservationUnits, boolean withGID) throws ApiException { + private void processObservationUnits(Program program, List brapiObservationUnits, boolean withGID) throws ApiException { HashMap germplasmByDbId = new HashMap<>(); if( withGID ){ // Load germplasm for program into map. // TODO: if we use redis search, that may be more efficient than loading all germplasm for the program. - this.germplasmService.getGermplasm(programId).forEach((germplasm -> germplasmByDbId.put(germplasm.getGermplasmDbId(), germplasm))); + this.germplasmService.getGermplasm(program.getId()).forEach((germplasm -> germplasmByDbId.put(germplasm.getGermplasmDbId(), germplasm))); } // if has treatments in additionalInfo, copy to treatments property @@ -203,8 +298,23 @@ private void processObservationUnits(UUID programId, List if( withGID ){ BrAPIGermplasm germplasm = germplasmByDbId.get(ou.getGermplasmDbId()); ou.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GID, germplasm.getAccessionNumber()); + ou.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_UUID, + Utilities.getExternalReference(germplasm.getExternalReferences(), referenceSource) + .orElseThrow(() -> new IllegalStateException("Germplasm UUID not found")) + .getReferenceID()); } } + ou.setObservationUnitName(Utilities.removeProgramKeyAndUnknownAdditionalData(ou.getObservationUnitName(), program.getKey())); + ou.setGermplasmName(Utilities.removeProgramKeyAndUnknownAdditionalData(ou.getGermplasmName(), program.getKey())); + ou.setLocationName(Utilities.removeProgramKey(ou.getLocationName(), program.getKey())); + ou.setProgramName(ou.getProgramName().replaceAll("\\("+program.getKey()+"\\)", "").trim()); + ou.setTrialName(Utilities.removeProgramKey(ou.getTrialName(), program.getKey())); + ou.setStudyName(Utilities.removeProgramKeyAndUnknownAdditionalData(ou.getStudyName(), program.getKey())); + ou.getObservationUnitPosition() + .getObservationLevel() + .setLevelCode(Utilities.removeProgramKeyAndUnknownAdditionalData(ou.getObservationUnitPosition() + .getObservationLevel() + .getLevelCode(), program.getKey())); } } diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIStudyDAO.java b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIStudyDAO.java index cae09d9fb..e4831dd6b 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIStudyDAO.java +++ b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIStudyDAO.java @@ -151,6 +151,15 @@ public List getStudiesByExperimentID(@NotNull UUID experimentID, Pro ); } + public List getStudiesByEnvironmentIds(@NotNull Collection environmentIds, Program program ) throws ApiException { + return programStudyCache.get(program.getId()) + .entrySet() + .stream() + .filter(entry -> environmentIds.contains(UUID.fromString(entry.getKey()))) + .map(Map.Entry::getValue) + .collect(Collectors.toList()); + } + public List createBrAPIStudies(List brAPIStudyList, UUID programId, ImportUpload upload) throws ApiException { StudiesApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), StudiesApi.class); List createdStudies = new ArrayList<>(); @@ -208,6 +217,12 @@ public Optional getStudyByDbId(String studyDbId, Program program) th return Utilities.getSingleOptional(studies); } + public Optional getStudyByEnvironmentId(UUID environmentId, Program program) throws ApiException { + List studies = getStudiesByEnvironmentIds(List.of(environmentId), program); + + return Utilities.getSingleOptional(studies); + } + /** * Process study into a format for display diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/ObservationUnitProcessor.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/ObservationUnitProcessor.java index daad1eecd..e931a5584 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/ObservationUnitProcessor.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/ObservationUnitProcessor.java @@ -34,6 +34,7 @@ import org.breedinginsight.brapps.importer.model.response.PendingImportObject; import org.breedinginsight.model.Program; import org.breedinginsight.model.User; +import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.services.exceptions.ValidatorException; import tech.tablesaw.api.Table; @@ -144,6 +145,8 @@ public void postBrapiData(Map mappedBrAPIImport, Program createdObservationUnits.addAll(brAPIObservationUnitDAO.createBrAPIObservationUnits(observationUnits, program.getId(), upload)); } catch (ApiException e) { throw new InternalServerException(e.toString(), e); + } catch (DoesNotExistException e) { + throw new InternalServerException(e.toString(), e); } // Update our records diff --git a/src/main/java/org/breedinginsight/daos/impl/TraitDAOImpl.java b/src/main/java/org/breedinginsight/daos/impl/TraitDAOImpl.java index 2dba86807..293a7918c 100644 --- a/src/main/java/org/breedinginsight/daos/impl/TraitDAOImpl.java +++ b/src/main/java/org/breedinginsight/daos/impl/TraitDAOImpl.java @@ -614,32 +614,14 @@ private SelectOnConditionStep getTraitSql(BiUserTable createdByTableAlia @Override public List getTraitsByTraitName(UUID programId, List traits){ - String[] names = traits.stream() + List names = traits.stream() .filter(trait -> trait.getObservationVariableName() != null) .map(trait -> trait.getObservationVariableName().toLowerCase()) - .collect(Collectors.toList()).toArray(String[]::new); + .collect(Collectors.toList()); List traitResults = new ArrayList<>(); - if (names.length > 0){ - - Result records = dsl.select() - .from(TRAIT) - .join(PROGRAM_ONTOLOGY).on(TRAIT.PROGRAM_ONTOLOGY_ID.eq(PROGRAM_ONTOLOGY.ID)) - .join(PROGRAM).on(PROGRAM_ONTOLOGY.PROGRAM_ID.eq(PROGRAM.ID)) - .join(SCALE).on(TRAIT.SCALE_ID.eq(SCALE.ID)) - .join(METHOD).on(TRAIT.METHOD_ID.eq(METHOD.ID)) - .where(PROGRAM.ID.eq(programId)) - .and(lower(TRAIT.OBSERVATION_VARIABLE_NAME).in(names)) - .fetch(); - - for (Record record: records) { - Trait trait = Trait.parseSqlRecord(record); - Scale scale = Scale.parseSqlRecord(record); - Method method = Method.parseSqlRecord(record); - trait.setScale(scale); - trait.setMethod(method); - traitResults.add(trait); - } + if (!names.isEmpty()) { + traitResults = getTraitsFullByProgramId(programId).stream().filter(trait -> names.contains(trait.getObservationVariableName())).collect(Collectors.toList()); } return traitResults; diff --git a/src/main/java/org/breedinginsight/model/Dataset.java b/src/main/java/org/breedinginsight/model/Dataset.java index 36bba2121..638c946f1 100644 --- a/src/main/java/org/breedinginsight/model/Dataset.java +++ b/src/main/java/org/breedinginsight/model/Dataset.java @@ -14,7 +14,7 @@ public class Dataset { public JsonObject additionalInfo; public List data; public List observationUnits; - public List observationVariables; + public List observationVariables; public enum DatasetStat { OBSERVATION_UNITS("observationUnits"), @@ -34,7 +34,7 @@ public Dataset( String experimentId, List data, List observationUnits, - List observationVariables) { + List observationVariables) { this.experimentId = experimentId; this.additionalInfo = new JsonObject(); this.data = data; diff --git a/src/main/java/org/breedinginsight/services/TraitService.java b/src/main/java/org/breedinginsight/services/TraitService.java index ff872791f..b4fcaa060 100644 --- a/src/main/java/org/breedinginsight/services/TraitService.java +++ b/src/main/java/org/breedinginsight/services/TraitService.java @@ -475,4 +475,12 @@ public List getAllTraitTags(UUID programId) { tags.add(FAVORITES_TAG); return new ArrayList<>(tags); } + + public List getByName(UUID programId, List names) throws DoesNotExistException { + if (!programService.exists(programId)) { + throw new DoesNotExistException("Program does not exist"); + } + + return traitDAO.getTraitsByTraitName(programId, names.stream().map(name -> Trait.builder().observationVariableName(name).build()).collect(Collectors.toList())); + } } From d34d5b120d60e57588d69c3e29b5f0e607723ee2 Mon Sep 17 00:00:00 2001 From: timparsons Date: Thu, 5 Oct 2023 15:22:34 -0400 Subject: [PATCH 04/20] [BI-1921] Fixing trait fetch bug --- src/main/java/org/breedinginsight/daos/impl/TraitDAOImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/breedinginsight/daos/impl/TraitDAOImpl.java b/src/main/java/org/breedinginsight/daos/impl/TraitDAOImpl.java index 293a7918c..c61702622 100644 --- a/src/main/java/org/breedinginsight/daos/impl/TraitDAOImpl.java +++ b/src/main/java/org/breedinginsight/daos/impl/TraitDAOImpl.java @@ -621,7 +621,7 @@ public List getTraitsByTraitName(UUID programId, List traits){ List traitResults = new ArrayList<>(); if (!names.isEmpty()) { - traitResults = getTraitsFullByProgramId(programId).stream().filter(trait -> names.contains(trait.getObservationVariableName())).collect(Collectors.toList()); + traitResults = getTraitsFullByProgramId(programId).stream().filter(trait -> names.contains(trait.getObservationVariableName().toLowerCase())).collect(Collectors.toList()); } return traitResults; From 3c4536956927aa806090e737339a90afa864401f Mon Sep 17 00:00:00 2001 From: timparsons Date: Thu, 12 Oct 2023 11:56:03 -0400 Subject: [PATCH 05/20] [BI-1921] filtering out REP and BLOCK levels from the /observationlevels response --- .../brapi/v2/BrAPIObservationLevelsController.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsController.java index 3fe481c73..d5616a793 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsController.java @@ -33,9 +33,7 @@ import org.brapi.v2.model.BrAPIMetadata; import org.brapi.v2.model.core.BrAPIStudy; import org.brapi.v2.model.core.BrAPITrial; -import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.brapi.v2.model.pheno.BrAPIObservationUnitHierarchyLevel; -import org.brapi.v2.model.pheno.BrAPIObservationUnitLevelRelationship; import org.brapi.v2.model.pheno.response.BrAPIObservationLevelListResponse; import org.brapi.v2.model.pheno.response.BrAPIObservationLevelListResponseResult; import org.breedinginsight.api.auth.ProgramSecured; @@ -44,11 +42,10 @@ import org.breedinginsight.brapps.importer.daos.BrAPIStudyDAO; import org.breedinginsight.brapps.importer.daos.BrAPITrialDAO; import org.breedinginsight.daos.ProgramDAO; +import org.breedinginsight.model.BrAPIConstants; import org.breedinginsight.model.Program; 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.annotation.Nullable; @@ -57,6 +54,7 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; @Slf4j @Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @@ -130,7 +128,8 @@ public HttpResponse observationlevelsGet(@Pat BrAPIIndexPagination responsePagination = (BrAPIIndexPagination) response.getBody().getMetadata().getPagination(); List levels = new ArrayList<>(); if(response.getBody() != null) { - levels = response.getBody().getResult().getData(); + levels = response.getBody().getResult().getData().stream().filter(level -> !level.getLevelName().equals(BrAPIConstants.REPLICATE.getValue()) && !level.getLevelName().equals(BrAPIConstants.BLOCK.getValue())).collect( + Collectors.toList()); } log.debug(String.format("found %d observation levels", levels.size())); return HttpResponse.ok(new BrAPIObservationLevelListResponse().metadata(new BrAPIMetadata().pagination(responsePagination)) From 9c53b630d412ab19be5bb15d8d86e640e59fac60 Mon Sep 17 00:00:00 2001 From: timparsons Date: Thu, 12 Oct 2023 11:57:01 -0400 Subject: [PATCH 06/20] [BI-1921] Updating trait response to have the trait name be the first synonym Apps like Field Book use the first synonym as the displayed name of the synonym --- .../BrAPIObservationVariableController.java | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java index fd7b93b71..539674fd3 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java @@ -28,7 +28,6 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.v2.model.BrAPIExternalReference; import org.brapi.v2.model.BrAPIIndexPagination; import org.brapi.v2.model.BrAPIMetadata; import org.brapi.v2.model.BrAPIOntologyReference; @@ -102,7 +101,7 @@ public HttpResponse variablesGet(@PathVari @Nullable @QueryValue("traitPUI") String traitPUI, @Nullable @QueryValue("ontologyDbId") String ontologyDbId, @Nullable @QueryValue("commonCropName") String commonCropName, -// @QueryValue("programDbId") Optional<@Nullable String> programDbId, //this is +// @QueryValue("programDbId") Optional<@Nullable String> programDbId, //this is redundant @Nullable @QueryValue("trialDbId") String experimentId, @Nullable @QueryValue("studyDbId") String environmentId, @Nullable @QueryValue("externalReferenceID") String externalReferenceID, @@ -204,18 +203,19 @@ private BrAPIObservationVariable convertToBrAPI(Trait trait) { BrAPIOntologyReference brAPIOntologyReference = new BrAPIOntologyReference().ontologyDbId(trait.getProgramOntologyId() .toString()); String status = trait.getActive() ? "active" : "inactive"; + List synonyms = prepSynonyms(trait); return new BrAPIObservationVariable().observationVariableDbId(trait.getId().toString()) .observationVariableName(trait.getObservationVariableName()) .defaultValue(trait.getDefaultValue()) .status(status) - .synonyms(trait.getSynonyms()) + .synonyms(synonyms) .trait(new BrAPITrait().ontologyReference(brAPIOntologyReference) .traitName(trait.getObservationVariableName()) .traitDbId(trait.getId().toString()) .entity(trait.getEntity()) .attribute(trait.getAttribute()) .status(status) - .synonyms(trait.getSynonyms())) + .synonyms(synonyms)) .method(new BrAPIMethod().ontologyReference(brAPIOntologyReference) .methodDbId(trait.getMethod().getId().toString()) .description(trait.getMethod().getDescription()) @@ -230,6 +230,36 @@ private BrAPIObservationVariable convertToBrAPI(Trait trait) { .categories(trait.getScale().getCategories()))); } + /** + * Create a list of synonyms, and ensure that there the first element matches the name of the trait

+ * This is primarily needed to ensure any system using the first synonym as a display shows the actual name of the ontology term + * @param trait + * @return list of synonyms with at least one value (the name of the trait) + */ + private List prepSynonyms(Trait trait) { + List preppedSynonyms = new ArrayList<>(); + if(trait.getSynonyms() != null) { + preppedSynonyms = trait.getSynonyms(); + int traitNameIdx = -1; + for(int i = 0; i < preppedSynonyms.size(); i++) { + if(preppedSynonyms.get(i).equals(trait.getObservationVariableName())) { + traitNameIdx = i; + break; + } + } + if(traitNameIdx > -1) { + String temp = preppedSynonyms.get(traitNameIdx); + preppedSynonyms.set(traitNameIdx, preppedSynonyms.get(0)); + preppedSynonyms.set(0, temp); + } else { + preppedSynonyms.add(0, trait.getObservationVariableName()); + } + } else { + preppedSynonyms.add(trait.getObservationVariableName()); + } + return preppedSynonyms; + } + @NotNull private List filterVariables(List programTraits, Optional observationVariableDbId, From c089de86edcb9602d9a2b2422bed65bb3176388c Mon Sep 17 00:00:00 2001 From: timparsons Date: Mon, 23 Oct 2023 11:28:49 -0400 Subject: [PATCH 07/20] [BI-1921] fixing compilation error --- .../brapps/importer/daos/BrAPIObservationUnitDAO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java index f8ed51f25..59506b012 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java +++ b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java @@ -163,7 +163,7 @@ public List getObservationUnitsForDataset(@NotNull String ouSearchRequest.programDbIds(List.of(program.getBrapiProgram().getProgramDbId())); ouSearchRequest.externalReferenceSources(List.of(datasetReferenceSource)); ouSearchRequest.externalReferenceIDs(List.of(datasetId)); - return searchObservationUnitsAndProcess(ouSearchRequest, program.getId(), true); + return searchObservationUnitsAndProcess(ouSearchRequest, program, true); } public List getObservationUnits(Program program, From 9cc43509ebfd5860c6aae741657ff0a8c3c34c25 Mon Sep 17 00:00:00 2001 From: timparsons Date: Wed, 29 Nov 2023 09:53:20 -0500 Subject: [PATCH 08/20] [BI-1921] fixing failing unit tests - Adding new unit tests for new BrAPI endpoints - reorganizing BrAPI DAOs to be in more appropriate package --- .../brapi/v2/BrAPIImagesController.java | 6 +- .../v2/BrAPIObservationLevelsController.java | 4 +- .../v2/BrAPIObservationUnitController.java | 47 +- .../BrAPIObservationVariableController.java | 4 +- .../brapi/v2/BrAPIObservationsController.java | 6 +- .../brapi/v2/BrAPIProgramsController.java | 8 +- .../brapi/v2/BrAPIStudiesController.java | 6 +- .../brapi/v2/BrAPITrialsController.java | 4 +- .../daos => brapi/v2/dao}/BrAPICrossDAO.java | 2 +- .../daos => brapi/v2/dao}/BrAPIListDAO.java | 20 +- .../v2/dao}/BrAPILocationDAO.java | 3 +- .../v2/dao}/BrAPIObservationDAO.java | 3 +- .../v2/dao}/BrAPIObservationUnitDAO.java | 38 +- .../v2/dao}/BrAPIObservationVariableDAO.java | 2 +- .../v2/dao}/BrAPIProgramDAO.java | 2 +- .../daos => brapi/v2/dao}/BrAPISeasonDAO.java | 19 +- .../daos => brapi/v2/dao}/BrAPIStudyDAO.java | 3 +- .../daos => brapi/v2/dao}/BrAPITrialDAO.java | 19 +- .../v2/dao}/impl/BrAPITrialDAOImpl.java | 4 +- .../v2/dao}/impl/ImportDAOImpl.java | 2 +- .../v2/dao}/impl/ImportMappingDAOImpl.java | 2 +- .../v2/services/BrAPIGermplasmService.java | 2 +- .../brapi/v2/services/BrAPIListService.java | 4 +- .../brapi/v2/services/BrAPIStudyService.java | 2 +- .../brapi/v2/services/BrAPITrialService.java | 3 +- .../processors/ExperimentProcessor.java | 3 +- .../processors/GermplasmProcessor.java | 2 +- .../processors/ObservationProcessor.java | 4 +- .../processors/ObservationUnitProcessor.java | 2 +- .../services/processors/StudyProcessor.java | 2 +- .../services/processors/TrialProcessor.java | 2 +- ...vationLevelsControllerIntegrationTest.java | 324 +++++++++++++ ...ervationUnitControllerIntegrationTest.java | 457 ++++++++++++++++++ ...tionVariableControllerIntegrationTest.java | 22 + ...ObservationsControllerIntegrationTest.java | 22 + ...BrAPIStudiesControllerIntegrationTest.java | 411 ++++++++++++++++ .../v2/BrAPIV2ControllerIntegrationTest.java | 220 +-------- .../GermplasmControllerIntegrationTest.java | 2 +- .../importer/ExperimentFileImportTest.java | 3 +- .../daos/BrAPIObservationUnitDAOTest.java | 1 + .../BrAPIGermplasmServiceUnitTest.java | 2 +- ...gwaGenotypeServiceImplIntegrationTest.java | 6 +- 42 files changed, 1404 insertions(+), 296 deletions(-) rename src/main/java/org/breedinginsight/{brapps/importer/daos => brapi/v2/dao}/BrAPICrossDAO.java (97%) rename src/main/java/org/breedinginsight/{brapps/importer/daos => brapi/v2/dao}/BrAPIListDAO.java (91%) rename src/main/java/org/breedinginsight/{brapps/importer/daos => brapi/v2/dao}/BrAPILocationDAO.java (97%) rename src/main/java/org/breedinginsight/{brapps/importer/daos => brapi/v2/dao}/BrAPIObservationDAO.java (98%) rename src/main/java/org/breedinginsight/{brapps/importer/daos => brapi/v2/dao}/BrAPIObservationUnitDAO.java (91%) rename src/main/java/org/breedinginsight/{brapps/importer/daos => brapi/v2/dao}/BrAPIObservationVariableDAO.java (97%) rename src/main/java/org/breedinginsight/{brapps/importer/daos => brapi/v2/dao}/BrAPIProgramDAO.java (98%) rename src/main/java/org/breedinginsight/{brapps/importer/daos => brapi/v2/dao}/BrAPISeasonDAO.java (80%) rename src/main/java/org/breedinginsight/{brapps/importer/daos => brapi/v2/dao}/BrAPIStudyDAO.java (99%) rename src/main/java/org/breedinginsight/{brapps/importer/daos => brapi/v2/dao}/BrAPITrialDAO.java (63%) rename src/main/java/org/breedinginsight/{brapps/importer/daos => brapi/v2/dao}/impl/BrAPITrialDAOImpl.java (99%) rename src/main/java/org/breedinginsight/{brapps/importer/daos => brapi/v2/dao}/impl/ImportDAOImpl.java (99%) rename src/main/java/org/breedinginsight/{brapps/importer/daos => brapi/v2/dao}/impl/ImportMappingDAOImpl.java (99%) create mode 100644 src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsControllerIntegrationTest.java create mode 100644 src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitControllerIntegrationTest.java create mode 100644 src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableControllerIntegrationTest.java create mode 100644 src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java create mode 100644 src/test/java/org/breedinginsight/brapi/v2/BrAPIStudiesControllerIntegrationTest.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIImagesController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIImagesController.java index 1893d10d0..f760af9f0 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIImagesController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIImagesController.java @@ -70,7 +70,7 @@ public HttpResponse imagesImageDbIdGet(@PathVariable("programId") UUID programId @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) public HttpResponse imagesImageDbIdImagecontentPut(@PathVariable("programId") UUID programId, @PathVariable("imageDbId") String imageDbId, - Object body) { + @Body Object body) { return HttpResponse.notFound(); } @@ -78,13 +78,13 @@ public HttpResponse imagesImageDbIdImagecontentPut(@PathVariable("programId") UU @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) public HttpResponse imagesImageDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("imageDbId") String imageDbId, - BrAPIImage body) { + @Body BrAPIImage body) { return HttpResponse.notFound(); } @Post("/images") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse imagesPost(@PathVariable("programId") UUID programId, List body) { + public HttpResponse imagesPost(@PathVariable("programId") UUID programId, @Body List body) { return HttpResponse.notFound(); } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsController.java index d5616a793..9c54bed9a 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsController.java @@ -39,8 +39,8 @@ import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; import org.breedinginsight.brapi.v1.controller.BrapiVersion; -import org.breedinginsight.brapps.importer.daos.BrAPIStudyDAO; -import org.breedinginsight.brapps.importer.daos.BrAPITrialDAO; +import org.breedinginsight.brapi.v2.dao.BrAPIStudyDAO; +import org.breedinginsight.brapi.v2.dao.BrAPITrialDAO; import org.breedinginsight.daos.ProgramDAO; import org.breedinginsight.model.BrAPIConstants; import org.breedinginsight.model.Program; diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java index 0c17e31e5..29ce0cef5 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java @@ -35,7 +35,7 @@ import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; -import org.breedinginsight.brapps.importer.daos.BrAPIObservationUnitDAO; +import org.breedinginsight.brapi.v2.dao.BrAPIObservationUnitDAO; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.model.Program; import org.breedinginsight.services.ProgramService; @@ -66,11 +66,6 @@ public BrAPIObservationUnitController(@Property(name = "brapi.server.reference-s this.programService = programService; } - /* - TODO - - GET observationLevels - - GET observationUnits - */ @Get("/observationunits") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) public HttpResponse observationunitsGet(@PathVariable("programId") UUID programId, @@ -182,21 +177,21 @@ public HttpResponse observationunitsObservat @Put("/observationunits/{observationUnitDbId}") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse observationunitsObservationUnitDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("observationUnitDbId") String observationUnitDbId, BrAPIObservationUnit body) { + public HttpResponse observationunitsObservationUnitDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("observationUnitDbId") String observationUnitDbId, @Body BrAPIObservationUnit body) { //DO NOT IMPLEMENT - Users aren't yet able to update observation units return HttpResponse.notFound(); } @Post("/observationunits") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse observationunitsPost(@PathVariable("programId") UUID programId, List body) { + public HttpResponse observationunitsPost(@PathVariable("programId") UUID programId, @Body List body) { //DO NOT IMPLEMENT - Users are only able to create observation units via the DeltaBreed UI return HttpResponse.notFound(); } @Put("/observationunits") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse observationunitsPut(@PathVariable("programId") UUID programId, Map body) { + public HttpResponse observationunitsPut(@PathVariable("programId") UUID programId, @Body Map body) { //DO NOT IMPLEMENT - Users aren't yet able to update observation units return HttpResponse.notFound(); } @@ -205,23 +200,23 @@ public HttpResponse observationunitsPut(@PathVariable("programId") UUID progr @Produces({"application/json", "text/csv", "text/tsv"}) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) public HttpResponse observationunitsTableGet(@PathVariable("programId") UUID programId, - @Header("Accept") String accept, - @QueryValue("observationUnitDbId") String observationUnitDbId, - @QueryValue("observationVariableDbId") String observationVariableDbId, - @QueryValue("locationDbId") String locationDbId, - @QueryValue("seasonDbId") String seasonDbId, - @QueryValue("observationLevel") String observationLevel, - @QueryValue("programDbId") String programDbId, - @QueryValue("trialDbId") String trialDbId, - @QueryValue("studyDbId") String studyDbId, - @QueryValue("germplasmDbId") String germplasmDbId, - @QueryValue("observationUnitLevelName") String observationUnitLevelName, - @QueryValue("observationUnitLevelOrder") String observationUnitLevelOrder, - @QueryValue("observationUnitLevelCode") String observationUnitLevelCode, - @QueryValue("observationUnitLevelRelationshipName") String observationUnitLevelRelationshipName, - @QueryValue("observationUnitLevelRelationshipOrder") String observationUnitLevelRelationshipOrder, - @QueryValue("observationUnitLevelRelationshipCode") String observationUnitLevelRelationshipCode, - @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId) { + @Nullable @Header("Accept") String accept, + @Nullable @QueryValue("observationUnitDbId") String observationUnitDbId, + @Nullable @QueryValue("observationVariableDbId") String observationVariableDbId, + @Nullable @QueryValue("locationDbId") String locationDbId, + @Nullable @QueryValue("seasonDbId") String seasonDbId, + @Nullable @QueryValue("observationLevel") String observationLevel, + @Nullable @QueryValue("programDbId") String programDbId, + @Nullable @QueryValue("trialDbId") String trialDbId, + @Nullable @QueryValue("studyDbId") String studyDbId, + @Nullable @QueryValue("germplasmDbId") String germplasmDbId, + @Nullable @QueryValue("observationUnitLevelName") String observationUnitLevelName, + @Nullable @QueryValue("observationUnitLevelOrder") String observationUnitLevelOrder, + @Nullable @QueryValue("observationUnitLevelCode") String observationUnitLevelCode, + @Nullable @QueryValue("observationUnitLevelRelationshipName") String observationUnitLevelRelationshipName, + @Nullable @QueryValue("observationUnitLevelRelationshipOrder") String observationUnitLevelRelationshipOrder, + @Nullable @QueryValue("observationUnitLevelRelationshipCode") String observationUnitLevelRelationshipCode, + @Nullable @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId) { return HttpResponse.notFound(); } diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java index 539674fd3..d94ab0541 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java @@ -187,14 +187,14 @@ public HttpResponse variablesObservation @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) public HttpResponse variablesObservationVariableDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("observationVariableDbId") String observationVariableDbId, - BrAPIObservationVariable body) { + @Body BrAPIObservationVariable body) { //DO NOT IMPLEMENT - Users are only able to update traits via the DeltaBreed UI return HttpResponse.notFound(); } @Post("/variables") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse variablesPost(@PathVariable("programId") UUID programId, List body) { + public HttpResponse variablesPost(@PathVariable("programId") UUID programId, @Body List body) { //DO NOT IMPLEMENT - Users are only able to create new traits via the DeltaBreed UI return HttpResponse.notFound(); } diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java index cd35d9d5b..2f669255d 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java @@ -79,7 +79,7 @@ public HttpResponse observationsObservationDbIdGet(@PathVariable("programId") UU @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) public HttpResponse observationsObservationDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("observationDbId") String observationDbId, - BrAPIObservation body) { + @Body BrAPIObservation body) { /* DO NOT IMPLEMENT - users must create observations via file upload TODO identify how observations uploaded via BrAPI will be separated from curated observations @@ -89,7 +89,7 @@ public HttpResponse observationsObservationDbIdPut(@PathVariable("programId") UU @Post("/observations") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse observationsPost(@PathVariable("programId") UUID programId, List body) { + public HttpResponse observationsPost(@PathVariable("programId") UUID programId, @Body List body) { /* DO NOT IMPLEMENT - users must create observations via file upload TODO identify how observations uploaded via BrAPI will be separated from curated observations @@ -99,7 +99,7 @@ public HttpResponse observationsPost(@PathVariable("programId") UUID programId, @Put("/observations") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse observationsPut(@PathVariable("programId") UUID programId, Map body) { + public HttpResponse observationsPut(@PathVariable("programId") UUID programId, @Body Map body) { /* DO NOT IMPLEMENT - users must create observations via file upload TODO identify how observations uploaded via BrAPI will be separated from curated observations diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIProgramsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIProgramsController.java index dabbebc45..a30dbfbed 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIProgramsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIProgramsController.java @@ -93,7 +93,7 @@ public HttpResponse rootProgramsGet( } @Post(BrapiVersion.BRAPI_V2 + "/programs") - public HttpResponse rootProgramsPost(List body) { + public HttpResponse rootProgramsPost(@Body List body) { //DO NOT IMPLEMENT - Users should only be able to create new programs via the DeltaBreed UI return HttpResponse.notFound(); } @@ -117,7 +117,7 @@ public HttpResponse rootProgramsProgramDbIdGet(@Path } @Put(BrapiVersion.BRAPI_V2 + "/programs/{programDbId}") - public HttpResponse rootProgramsProgramDbIdPut(@PathVariable("programDbId") String programDbId, BrAPIProgram body) { + public HttpResponse rootProgramsProgramDbIdPut(@PathVariable("programDbId") String programDbId, @Body BrAPIProgram body) { //DO NOT IMPLEMENT - Users should only be able to update programs via the DeltaBreed UI return HttpResponse.notFound(); } @@ -154,7 +154,7 @@ public HttpResponse programsGet(@PathVariable("program @Post("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/programs") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse programsPost(@PathVariable("programId") UUID programId, List body) { + public HttpResponse programsPost(@PathVariable("programId") UUID programId, @Body List body) { //DO NOT IMPLEMENT - Users should only be able to create new programs via the DeltaBreed UI return HttpResponse.notFound(); } @@ -173,7 +173,7 @@ public HttpResponse programsProgramDbIdGet(@PathVari @Put("/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/programs/{programDbId}") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse programsProgramDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("programDbId") String programDbId, BrAPIProgram body) { + public HttpResponse programsProgramDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("programDbId") String programDbId, @Body BrAPIProgram body) { //DO NOT IMPLEMENT - Users should only be able to update programs via the DeltaBreed UI return HttpResponse.notFound(); } diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIStudiesController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIStudiesController.java index ea6f03a52..0382ecdb0 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIStudiesController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIStudiesController.java @@ -100,7 +100,7 @@ public HttpResponse>>> getStudies( @Post("/studies") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse studiesPost(@PathVariable("programId") UUID programId, List body) { + public HttpResponse studiesPost(@PathVariable("programId") UUID programId, @Body List body) { //DO NOT IMPLEMENT - Users are only able to create new studies via the DeltaBreed UI return HttpResponse.notFound(); } @@ -131,8 +131,8 @@ public HttpResponse studiesStudyDbIdGet(@PathVariable( @Put("/studies/{studyDbId}") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse studiesStudyDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("studyDbId") String studyDbId, - BrAPIStudy body) { + public HttpResponse studiesStudyDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("studyDbId") String studyDbId, + @Body BrAPIStudy body) { //DO NOT IMPLEMENT - Users are only able to update studies via the DeltaBreed UI return HttpResponse.notFound(); } diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java index 77dbf2b90..3966b1341 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java @@ -93,7 +93,7 @@ public HttpResponse getExperimentById( @Post("/trials") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse trialsPost(@PathVariable("programId") UUID programId, List body) { + public HttpResponse trialsPost(@PathVariable("programId") UUID programId, @Body List body) { //DO NOT IMPLEMENT - Users are only able to create new trials via the DeltaBreed UI return HttpResponse.notFound(); } @@ -101,7 +101,7 @@ public HttpResponse trialsPost(@PathVariable("programId") UUID programId, Lis @Put("/trials/{trialDbId}") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) - public HttpResponse trialsTrialDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("trialDbId") String trialDbId, BrAPITrial body) { + public HttpResponse trialsTrialDbIdPut(@PathVariable("programId") UUID programId, @PathVariable("trialDbId") String trialDbId, @Body BrAPITrial body) { //DO NOT IMPLEMENT - Users are only able to update trials via the DeltaBreed UI return HttpResponse.notFound(); } diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPICrossDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPICrossDAO.java similarity index 97% rename from src/main/java/org/breedinginsight/brapps/importer/daos/BrAPICrossDAO.java rename to src/main/java/org/breedinginsight/brapi/v2/dao/BrAPICrossDAO.java index 5a895d8ec..edfcfb6c7 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPICrossDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPICrossDAO.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.breedinginsight.brapps.importer.daos; +package org.breedinginsight.brapi.v2.dao; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.modules.germplasm.CrossesApi; diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java similarity index 91% rename from src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIListDAO.java rename to src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index cb24fd745..801b9775c 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -1,4 +1,21 @@ -package org.breedinginsight.brapps.importer.daos; +/* + * 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.dao; import lombok.extern.slf4j.Slf4j; import org.brapi.client.v2.ApiResponse; @@ -14,6 +31,7 @@ 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.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.daos.ProgramDAO; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPILocationDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPILocationDAO.java similarity index 97% rename from src/main/java/org/breedinginsight/brapps/importer/daos/BrAPILocationDAO.java rename to src/main/java/org/breedinginsight/brapi/v2/dao/BrAPILocationDAO.java index 2fe7b8b01..e45dadf5c 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPILocationDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPILocationDAO.java @@ -15,12 +15,13 @@ * limitations under the License. */ -package org.breedinginsight.brapps.importer.daos; +package org.breedinginsight.brapi.v2.dao; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.modules.core.LocationsApi; import org.brapi.v2.model.core.BrAPILocation; import org.brapi.v2.model.core.request.BrAPILocationSearchRequest; +import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.daos.ProgramDAO; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java similarity index 98% rename from src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationDAO.java rename to src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java index 9c9ed184b..b52aeff22 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.breedinginsight.brapps.importer.daos; +package org.breedinginsight.brapi.v2.dao; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.tuple.Pair; @@ -26,6 +26,7 @@ import org.brapi.v2.model.pheno.request.BrAPIObservationSearchRequest; import org.brapi.v2.model.pheno.response.BrAPIObservationListResponse; import org.brapi.v2.model.pheno.response.BrAPIObservationSingleResponse; +import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.daos.ProgramDAO; import org.breedinginsight.model.Program; diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java similarity index 91% rename from src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java rename to src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java index 59506b012..ac153d893 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java @@ -15,13 +15,14 @@ * limitations under the License. */ -package org.breedinginsight.brapps.importer.daos; +package org.breedinginsight.brapi.v2.dao; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import io.micronaut.context.annotation.Property; +import org.apache.commons.lang3.StringUtils; import org.brapi.client.v2.JSON; import io.micronaut.http.server.exceptions.InternalServerException; import org.brapi.client.v2.model.exceptions.ApiException; @@ -33,6 +34,7 @@ import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; import org.brapi.v2.model.pheno.BrAPIObservationTreatment; +import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.daos.ProgramDAO; @@ -305,16 +307,30 @@ private void processObservationUnits(Program program, List } } ou.setObservationUnitName(Utilities.removeProgramKeyAndUnknownAdditionalData(ou.getObservationUnitName(), program.getKey())); - ou.setGermplasmName(Utilities.removeProgramKeyAndUnknownAdditionalData(ou.getGermplasmName(), program.getKey())); - ou.setLocationName(Utilities.removeProgramKey(ou.getLocationName(), program.getKey())); - ou.setProgramName(ou.getProgramName().replaceAll("\\("+program.getKey()+"\\)", "").trim()); - ou.setTrialName(Utilities.removeProgramKey(ou.getTrialName(), program.getKey())); - ou.setStudyName(Utilities.removeProgramKeyAndUnknownAdditionalData(ou.getStudyName(), program.getKey())); - ou.getObservationUnitPosition() - .getObservationLevel() - .setLevelCode(Utilities.removeProgramKeyAndUnknownAdditionalData(ou.getObservationUnitPosition() - .getObservationLevel() - .getLevelCode(), program.getKey())); + if(StringUtils.isNotBlank(ou.getGermplasmName())) { + ou.setGermplasmName(Utilities.removeProgramKeyAndUnknownAdditionalData(ou.getGermplasmName(), program.getKey())); + } + if(StringUtils.isNotBlank(ou.getLocationName())) { + ou.setLocationName(Utilities.removeProgramKey(ou.getLocationName(), program.getKey())); + } + if(StringUtils.isNotBlank(ou.getProgramName())) { + ou.setProgramName(ou.getProgramName().replaceAll("\\(" + program.getKey() + "\\)", "").trim()); + } + if(StringUtils.isNotBlank(ou.getTrialName())) { + ou.setTrialName(Utilities.removeProgramKey(ou.getTrialName(), program.getKey())); + } + if(StringUtils.isNotBlank(ou.getStudyName())) { + ou.setStudyName(Utilities.removeProgramKeyAndUnknownAdditionalData(ou.getStudyName(), program.getKey())); + } + if (ou.getObservationUnitPosition() != null + && ou.getObservationUnitPosition().getObservationLevel() != null + && StringUtils.isNotBlank(ou.getObservationUnitPosition().getObservationLevel().getLevelCode())) { + ou.getObservationUnitPosition() + .getObservationLevel() + .setLevelCode(Utilities.removeProgramKeyAndUnknownAdditionalData(ou.getObservationUnitPosition() + .getObservationLevel() + .getLevelCode(), program.getKey())); + } } } diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationVariableDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationVariableDAO.java similarity index 97% rename from src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationVariableDAO.java rename to src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationVariableDAO.java index 86731a0f2..0de5c19ed 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationVariableDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationVariableDAO.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.breedinginsight.brapps.importer.daos; +package org.breedinginsight.brapi.v2.dao; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.modules.phenotype.ObservationVariablesApi; diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIProgramDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIProgramDAO.java similarity index 98% rename from src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIProgramDAO.java rename to src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIProgramDAO.java index 103585849..965de1cab 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIProgramDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIProgramDAO.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.breedinginsight.brapps.importer.daos; +package org.breedinginsight.brapi.v2.dao; import io.micronaut.context.annotation.Property; import org.brapi.client.v2.ApiResponse; diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISeasonDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPISeasonDAO.java similarity index 80% rename from src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISeasonDAO.java rename to src/main/java/org/breedinginsight/brapi/v2/dao/BrAPISeasonDAO.java index 0ca2600b8..8ce77e1e9 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISeasonDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPISeasonDAO.java @@ -1,4 +1,21 @@ -package org.breedinginsight.brapps.importer.daos; +/* + * 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.dao; import lombok.extern.slf4j.Slf4j; import org.brapi.client.v2.ApiResponse; diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIStudyDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIStudyDAO.java similarity index 99% rename from src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIStudyDAO.java rename to src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIStudyDAO.java index e4831dd6b..2901baa4e 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIStudyDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIStudyDAO.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.breedinginsight.brapps.importer.daos; +package org.breedinginsight.brapi.v2.dao; import com.google.gson.JsonObject; import io.micronaut.context.annotation.Property; @@ -26,6 +26,7 @@ import org.brapi.v2.model.BrAPIExternalReference; import org.brapi.v2.model.core.BrAPIStudy; import org.brapi.v2.model.core.request.BrAPIStudySearchRequest; +import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.daos.ProgramDAO; diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPITrialDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java similarity index 63% rename from src/main/java/org/breedinginsight/brapps/importer/daos/BrAPITrialDAO.java rename to src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java index 55e35f6e8..83ee3f47b 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPITrialDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java @@ -1,4 +1,21 @@ -package org.breedinginsight.brapps.importer.daos; +/* + * 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.dao; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.core.BrAPITrial; diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/impl/BrAPITrialDAOImpl.java b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java similarity index 99% rename from src/main/java/org/breedinginsight/brapps/importer/daos/impl/BrAPITrialDAOImpl.java rename to src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java index f26d039ba..a60008dcd 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/impl/BrAPITrialDAOImpl.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.breedinginsight.brapps.importer.daos.impl; +package org.breedinginsight.brapi.v2.dao.impl; import io.micronaut.context.annotation.Context; import io.micronaut.context.annotation.Property; @@ -26,7 +26,7 @@ import org.brapi.v2.model.BrAPIExternalReference; import org.brapi.v2.model.core.BrAPITrial; import org.brapi.v2.model.core.request.BrAPITrialSearchRequest; -import org.breedinginsight.brapps.importer.daos.BrAPITrialDAO; +import org.breedinginsight.brapi.v2.dao.BrAPITrialDAO; import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/impl/ImportDAOImpl.java b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/ImportDAOImpl.java similarity index 99% rename from src/main/java/org/breedinginsight/brapps/importer/daos/impl/ImportDAOImpl.java rename to src/main/java/org/breedinginsight/brapi/v2/dao/impl/ImportDAOImpl.java index 5b28a35db..7807d573d 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/impl/ImportDAOImpl.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/ImportDAOImpl.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.breedinginsight.brapps.importer.daos.impl; +package org.breedinginsight.brapi.v2.dao.impl; import io.micronaut.http.server.exceptions.InternalServerException; import org.breedinginsight.brapps.importer.daos.ImportDAO; diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/impl/ImportMappingDAOImpl.java b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/ImportMappingDAOImpl.java similarity index 99% rename from src/main/java/org/breedinginsight/brapps/importer/daos/impl/ImportMappingDAOImpl.java rename to src/main/java/org/breedinginsight/brapi/v2/dao/impl/ImportMappingDAOImpl.java index b0b3f7f2e..a7f1384f4 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/impl/ImportMappingDAOImpl.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/ImportMappingDAOImpl.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.breedinginsight.brapps.importer.daos.impl; +package org.breedinginsight.brapi.v2.dao.impl; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; 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 cfd44f64b..b64a88c23 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java @@ -12,7 +12,7 @@ import org.brapi.v2.model.germ.BrAPIGermplasm; import org.brapi.v2.model.germ.BrAPIGermplasmSynonyms; import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; -import org.breedinginsight.brapps.importer.daos.BrAPIListDAO; +import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.model.exports.FileType; import org.breedinginsight.model.Column; import org.breedinginsight.model.DownloadFile; 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 8ae42f92c..3353ac17c 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java @@ -9,7 +9,7 @@ import org.brapi.v2.model.core.request.BrAPIListSearchRequest; import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; -import org.breedinginsight.brapps.importer.daos.*; +import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.model.Program; import org.breedinginsight.services.exceptions.DoesNotExistException; @@ -17,10 +17,8 @@ import javax.inject.Inject; import javax.inject.Singleton; -import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.UUID; import java.util.stream.Collectors; @Slf4j diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIStudyService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIStudyService.java index db5e28351..570609d53 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIStudyService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIStudyService.java @@ -20,7 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.core.BrAPIStudy; -import org.breedinginsight.brapps.importer.daos.BrAPIStudyDAO; +import org.breedinginsight.brapi.v2.dao.BrAPIStudyDAO; import org.breedinginsight.model.Program; import javax.inject.Inject; diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java index f1939996c..9ebd95954 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java @@ -14,9 +14,8 @@ import org.brapi.v2.model.pheno.*; import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; -import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; +import org.breedinginsight.brapi.v2.dao.*; import org.breedinginsight.brapi.v2.model.request.query.ExperimentExportQuery; -import org.breedinginsight.brapps.importer.daos.*; import org.breedinginsight.brapps.importer.model.exports.FileType; import org.breedinginsight.brapps.importer.model.imports.experimentObservation.ExperimentObservation; import org.breedinginsight.brapps.importer.model.imports.experimentObservation.ExperimentObservation.Columns; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/ExperimentProcessor.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/ExperimentProcessor.java index 070b2214a..74065ffdf 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/ExperimentProcessor.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/ExperimentProcessor.java @@ -43,8 +43,7 @@ import org.breedinginsight.api.model.v1.response.ValidationError; import org.breedinginsight.api.model.v1.response.ValidationErrors; import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; -import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; -import org.breedinginsight.brapps.importer.daos.*; +import org.breedinginsight.brapi.v2.dao.*; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.model.imports.BrAPIImport; import org.breedinginsight.brapps.importer.model.imports.ChangeLogEntry; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/GermplasmProcessor.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/GermplasmProcessor.java index f51f7c1fb..de5eb65ae 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/GermplasmProcessor.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/GermplasmProcessor.java @@ -33,7 +33,7 @@ import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; -import org.breedinginsight.brapps.importer.daos.BrAPIListDAO; +import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.model.base.Germplasm; import org.breedinginsight.brapps.importer.model.imports.BrAPIImport; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/ObservationProcessor.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/ObservationProcessor.java index 693dda6e1..7fcefcd3d 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/ObservationProcessor.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/ObservationProcessor.java @@ -26,8 +26,8 @@ import org.brapi.v2.model.pheno.BrAPIObservation; import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.brapi.v2.model.pheno.BrAPIObservationVariable; -import org.breedinginsight.brapps.importer.daos.BrAPIObservationDAO; -import org.breedinginsight.brapps.importer.daos.BrAPIObservationVariableDAO; +import org.breedinginsight.brapi.v2.dao.BrAPIObservationDAO; +import org.breedinginsight.brapi.v2.dao.BrAPIObservationVariableDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.model.base.Observation; import org.breedinginsight.brapps.importer.model.imports.BrAPIImport; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/ObservationUnitProcessor.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/ObservationUnitProcessor.java index e931a5584..6f1e6723a 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/ObservationUnitProcessor.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/ObservationUnitProcessor.java @@ -24,7 +24,7 @@ import org.brapi.v2.model.core.BrAPIStudy; import org.brapi.v2.model.germ.BrAPIGermplasm; import org.brapi.v2.model.pheno.BrAPIObservationUnit; -import org.breedinginsight.brapps.importer.daos.BrAPIObservationUnitDAO; +import org.breedinginsight.brapi.v2.dao.BrAPIObservationUnitDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.model.base.ObservationUnit; import org.breedinginsight.brapps.importer.model.imports.BrAPIImport; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/StudyProcessor.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/StudyProcessor.java index 398eb2d88..e5f89e538 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/StudyProcessor.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/StudyProcessor.java @@ -23,7 +23,7 @@ import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.core.BrAPIStudy; import org.brapi.v2.model.core.BrAPITrial; -import org.breedinginsight.brapps.importer.daos.BrAPIStudyDAO; +import org.breedinginsight.brapi.v2.dao.BrAPIStudyDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.model.base.Study; import org.breedinginsight.brapps.importer.model.imports.BrAPIImport; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/TrialProcessor.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/TrialProcessor.java index bce850f51..1f838f9c6 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/TrialProcessor.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/TrialProcessor.java @@ -20,7 +20,7 @@ import io.micronaut.http.server.exceptions.InternalServerException; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.core.BrAPITrial; -import org.breedinginsight.brapps.importer.daos.BrAPITrialDAO; +import org.breedinginsight.brapi.v2.dao.BrAPITrialDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.model.base.Trial; import org.breedinginsight.brapps.importer.model.imports.BrAPIImport; diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsControllerIntegrationTest.java new file mode 100644 index 000000000..0e57050ca --- /dev/null +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsControllerIntegrationTest.java @@ -0,0 +1,324 @@ +/* + * 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 com.google.gson.*; +import io.kowalski.fannypack.FannyPack; +import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +import io.micronaut.http.client.RxHttpClient; +import io.micronaut.http.client.annotation.Client; +import io.micronaut.http.netty.cookies.NettyCookie; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import io.reactivex.Flowable; +import org.brapi.v2.model.BrAPIExternalReference; +import org.brapi.v2.model.germ.BrAPIGermplasm; +import org.breedinginsight.BrAPITest; +import org.breedinginsight.TestUtils; +import org.breedinginsight.api.auth.AuthenticatedUser; +import org.breedinginsight.api.model.v1.request.ProgramRequest; +import org.breedinginsight.api.model.v1.request.SpeciesRequest; +import org.breedinginsight.api.v1.controller.TestTokenValidator; +import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; +import org.breedinginsight.brapps.importer.ImportTestUtils; +import org.breedinginsight.brapps.importer.model.imports.experimentObservation.ExperimentObservation; +import org.breedinginsight.dao.db.enums.DataType; +import org.breedinginsight.dao.db.tables.pojos.SpeciesEntity; +import org.breedinginsight.daos.SpeciesDAO; +import org.breedinginsight.daos.UserDAO; +import org.breedinginsight.model.*; +import org.breedinginsight.services.OntologyService; +import org.breedinginsight.services.exceptions.ValidatorException; +import org.breedinginsight.services.parsers.experiment.ExperimentFileColumns; +import org.breedinginsight.services.writers.CSVWriter; +import org.jooq.DSLContext; +import org.junit.jupiter.api.*; + +import javax.inject.Inject; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.*; + +import static io.micronaut.http.HttpRequest.GET; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@MicronautTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class BrAPIObservationLevelsControllerIntegrationTest extends BrAPITest { + + private Program program; + private String experimentId; + private List envIds = new ArrayList<>(); + private final List> rows = new ArrayList<>(); + private final List columns = ExperimentFileColumns.getOrderedColumns(); + private List traits; + + @Property(name = "brapi.server.reference-source") + private String BRAPI_REFERENCE_SOURCE; + @Inject + private DSLContext dsl; + @Inject + private UserDAO userDAO; + @Inject + private SpeciesDAO speciesDAO; + @Inject + private OntologyService ontologyService; + @Inject + private BrAPIGermplasmDAO germplasmDAO; + + @Inject + @Client("/${micronaut.bi.api.version}") + private RxHttpClient client; + + private final Gson gson = new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, (JsonDeserializer) + (json, type, context) -> OffsetDateTime.parse(json.getAsString())) + .create(); + + @BeforeAll + void setup() throws Exception { + FannyPack fp = FannyPack.fill("src/test/resources/sql/ImportControllerIntegrationTest.sql"); + ImportTestUtils importTestUtils = new ImportTestUtils(); + FannyPack securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); + FannyPack brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); + + // Test User + User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); + + // Species + super.getBrapiDsl().execute(brapiFp.get("InsertSpecies")); + SpeciesEntity validSpecies = speciesDAO.findAll().get(0); + SpeciesRequest speciesRequest = SpeciesRequest.builder() + .commonName(validSpecies.getCommonName()) + .id(validSpecies.getId()) + .build(); + + // Test Program + ProgramRequest programRequest = ProgramRequest.builder() + .name("Test Program") + .abbreviation("Test") + .documentationUrl("localhost:8080") + .objective("To test things") + .species(speciesRequest) + .key("TEST") + .build(); + program = TestUtils.insertAndFetchTestProgram(gson, client, programRequest); + + dsl.execute(securityFp.get("InsertProgramRolesBreeder"), testUser.getId().toString(), program.getId()); + dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); + + // Get experiment import map + Flowable> call = client.exchange( + GET("/import/mappings?importName=ExperimentsTemplateMap") + .contentType(MediaType.APPLICATION_JSON) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + HttpResponse response = call.blockingFirst(); + String mappingId = JsonParser.parseString(Objects.requireNonNull(response.body())).getAsJsonObject() + .getAsJsonObject("result") + .getAsJsonArray("data") + .get(0).getAsJsonObject().get("id").getAsString(); + + // Add traits to program + traits = createTraits(2); + AuthenticatedUser user = new AuthenticatedUser(testUser.getName(), new ArrayList<>(), testUser.getId(), new ArrayList<>()); + try { + ontologyService.createTraits(program.getId(), traits, user, false); + } catch (ValidatorException e) { + System.err.println(e.getErrors()); + throw e; + } + + // Add germplasm to program + List germplasm = createGermplasm(1); + BrAPIExternalReference newReference = new BrAPIExternalReference(); + newReference.setReferenceSource(String.format("%s/programs", BRAPI_REFERENCE_SOURCE)); + newReference.setReferenceID(program.getId().toString()); + + germplasm.forEach(germ -> germ.getExternalReferences().add(newReference)); + + germplasmDAO.createBrAPIGermplasm(germplasm, program.getId(), null); + + // Make test experiment import + Map row1 = makeExpImportRow("Env1", "Plot"); + + //TODO once sub-entity support is added, update this to test for multiple levels + Map row2 = makeExpImportRow("Env2", "Plot"); + + // Add test observation data + for (Trait trait : traits) { + Random random = new Random(); + + // TODO: test for sending obs data as double. + // A float is returned from the backend instead of double. there is a separate card to fix this. + // Double val1 = Math.random(); + + Float val1 = random.nextFloat(); + row1.put(trait.getObservationVariableName(), val1); + } + + rows.add(row1); + rows.add(row2); + + // Import test experiment, environments, and any observations + JsonObject importResult = importTestUtils.uploadAndFetch( + writeDataToFile(rows, traits), + null, + true, + client, + program, + mappingId); + experimentId = importResult + .get("preview").getAsJsonObject() + .get("rows").getAsJsonArray() + .get(0).getAsJsonObject() + .get("trial").getAsJsonObject() + .get("id").getAsString(); + // Add environmentIds. + envIds.add(getEnvId(importResult, 0)); + envIds.add(getEnvId(importResult, 1)); + } + + @Test + public void testGetObservationLevels() { + Flowable> call = client.exchange( + GET(String.format("/programs/%s/brapi/v2/observationlevels", program.getId())) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse response = call.blockingFirst(); + assertEquals(HttpStatus.OK, response.getStatus()); + + JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); + JsonArray levels = responseObj.getAsJsonObject("result").getAsJsonArray("data"); + assertEquals(1, levels.size()); + List levelNames = new ArrayList<>(); + for(var level : levels) { + levelNames.add(level.getAsJsonObject().get("levelName").getAsString().toLowerCase()); + } + assertTrue(levelNames.contains("plot")); + } + + private File writeDataToFile(List> data, List traits) throws IOException { + File file = File.createTempFile("test", ".csv"); + + if(traits != null) { + traits.forEach(trait -> columns.add( + Column.builder() + .value(trait.getObservationVariableName()) + .dataType(Column.ColumnDataType.STRING) + .build()) + ); + } + ByteArrayOutputStream byteArrayOutputStream = CSVWriter.writeToCSV(columns, data); + FileOutputStream fos = new FileOutputStream(file); + fos.write(byteArrayOutputStream.toByteArray()); + + return file; + } + + private String getEnvId(JsonObject result, int index) { + return result + .get("preview").getAsJsonObject() + .get("rows").getAsJsonArray() + .get(index).getAsJsonObject() + .get("study").getAsJsonObject() + .get("brAPIObject").getAsJsonObject() + .get("externalReferences").getAsJsonArray() + .get(2).getAsJsonObject() + .get("referenceID").getAsString(); + } + + private List createGermplasm(int numToCreate) { + List germplasm = new ArrayList<>(); + for (int i = 0; i < numToCreate; i++) { + String gid = ""+(i+1); + BrAPIGermplasm testGermplasm = new BrAPIGermplasm(); + testGermplasm.setGermplasmName(String.format("Germplasm %s [TEST-%s]", gid, gid)); + testGermplasm.setSeedSource("Wild"); + testGermplasm.setAccessionNumber(gid); + testGermplasm.setDefaultDisplayName(String.format("Germplasm %s", gid)); + JsonObject additionalInfo = new JsonObject(); + additionalInfo.addProperty("importEntryNumber", gid); + additionalInfo.addProperty("breedingMethod", "Allopolyploid"); + testGermplasm.setAdditionalInfo(additionalInfo); + List externalRef = new ArrayList<>(); + BrAPIExternalReference testReference = new BrAPIExternalReference(); + testReference.setReferenceSource(BRAPI_REFERENCE_SOURCE); + testReference.setReferenceID(UUID.randomUUID().toString()); + externalRef.add(testReference); + testGermplasm.setExternalReferences(externalRef); + germplasm.add(testGermplasm); + } + + return germplasm; + } + + private List createTraits(int numToCreate) { + List traits = new ArrayList<>(); + for (int i = 0; i < numToCreate; i++) { + String varName = "tt_test_" + (i + 1); + traits.add(Trait.builder() + .observationVariableName(varName) + .fullName(varName) + .entity("Plant " + i) + .attribute("height " + i) + .traitDescription("test") + .programObservationLevel(ProgramObservationLevel.builder().name("Plot").build()) + .scale(Scale.builder() + .scaleName("test scale") + .dataType(DataType.NUMERICAL) + .validValueMin(0) + .validValueMax(100) + .build()) + .method(Method.builder() + .description("test method") + .methodClass("test method") + .build()) + .build()); + } + + return traits; + } + + private Map makeExpImportRow(String environment, String level) { + Map row = new HashMap<>(); + row.put(ExperimentObservation.Columns.GERMPLASM_GID, "1"); + row.put(ExperimentObservation.Columns.TEST_CHECK, "T"); + row.put(ExperimentObservation.Columns.EXP_TITLE, "Test Exp"); + row.put(ExperimentObservation.Columns.EXP_UNIT, level); + row.put(ExperimentObservation.Columns.EXP_TYPE, "Phenotyping"); + row.put(ExperimentObservation.Columns.ENV, environment); + row.put(ExperimentObservation.Columns.ENV_LOCATION, "Location A"); + row.put(ExperimentObservation.Columns.ENV_YEAR, "2023"); + row.put(ExperimentObservation.Columns.EXP_UNIT_ID, "a-1"); + row.put(ExperimentObservation.Columns.REP_NUM, "1"); + row.put(ExperimentObservation.Columns.BLOCK_NUM, "1"); + row.put(ExperimentObservation.Columns.ROW, "1"); + row.put(ExperimentObservation.Columns.COLUMN, "1"); + return row; + } +} diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitControllerIntegrationTest.java new file mode 100644 index 000000000..4111e58ed --- /dev/null +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitControllerIntegrationTest.java @@ -0,0 +1,457 @@ +/* + * 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 com.google.gson.*; +import io.kowalski.fannypack.FannyPack; +import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +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; +import lombok.SneakyThrows; +import org.brapi.v2.model.BrAPIExternalReference; +import org.brapi.v2.model.germ.BrAPIGermplasm; +import org.brapi.v2.model.pheno.BrAPIObservationUnit; +import org.brapi.v2.model.pheno.response.BrAPIObservationUnitSingleResponse; +import org.breedinginsight.BrAPITest; +import org.breedinginsight.TestUtils; +import org.breedinginsight.api.auth.AuthenticatedUser; +import org.breedinginsight.api.model.v1.request.ProgramRequest; +import org.breedinginsight.api.model.v1.request.SpeciesRequest; +import org.breedinginsight.api.v1.controller.TestTokenValidator; +import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; +import org.breedinginsight.brapps.importer.ImportTestUtils; +import org.breedinginsight.brapps.importer.model.imports.experimentObservation.ExperimentObservation; +import org.breedinginsight.dao.db.enums.DataType; +import org.breedinginsight.dao.db.tables.pojos.SpeciesEntity; +import org.breedinginsight.daos.SpeciesDAO; +import org.breedinginsight.daos.UserDAO; +import org.breedinginsight.model.*; +import org.breedinginsight.services.OntologyService; +import org.breedinginsight.services.exceptions.ValidatorException; +import org.breedinginsight.services.parsers.experiment.ExperimentFileColumns; +import org.breedinginsight.services.writers.CSVWriter; +import org.jooq.DSLContext; +import org.junit.jupiter.api.*; + +import javax.inject.Inject; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +import static io.micronaut.http.HttpRequest.*; +import static io.micronaut.http.HttpRequest.GET; +import static org.junit.jupiter.api.Assertions.*; + +@MicronautTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class BrAPIObservationUnitControllerIntegrationTest extends BrAPITest { + + private Program program; + private String experimentId; + private List envIds = new ArrayList<>(); + private final List> rows = new ArrayList<>(); + private final List columns = ExperimentFileColumns.getOrderedColumns(); + private List traits; + + @Property(name = "brapi.server.reference-source") + private String BRAPI_REFERENCE_SOURCE; + @Inject + private DSLContext dsl; + @Inject + private UserDAO userDAO; + @Inject + private SpeciesDAO speciesDAO; + @Inject + private OntologyService ontologyService; + @Inject + private BrAPIGermplasmDAO germplasmDAO; + + @Inject + @Client("/${micronaut.bi.api.version}") + private RxHttpClient client; + + private final Gson gson = new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, (JsonDeserializer) + (json, type, context) -> OffsetDateTime.parse(json.getAsString())) + .create(); + + @BeforeAll + void setup() throws Exception { + FannyPack fp = FannyPack.fill("src/test/resources/sql/ImportControllerIntegrationTest.sql"); + ImportTestUtils importTestUtils = new ImportTestUtils(); + FannyPack securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); + FannyPack brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); + + // Test User + User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); + + // Species + super.getBrapiDsl().execute(brapiFp.get("InsertSpecies")); + SpeciesEntity validSpecies = speciesDAO.findAll().get(0); + SpeciesRequest speciesRequest = SpeciesRequest.builder() + .commonName(validSpecies.getCommonName()) + .id(validSpecies.getId()) + .build(); + + // Test Program + ProgramRequest programRequest = ProgramRequest.builder() + .name("Test Program") + .abbreviation("Test") + .documentationUrl("localhost:8080") + .objective("To test things") + .species(speciesRequest) + .key("TEST") + .build(); + program = TestUtils.insertAndFetchTestProgram(gson, client, programRequest); + + dsl.execute(securityFp.get("InsertProgramRolesBreeder"), testUser.getId().toString(), program.getId()); + dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); + + // Get experiment import map + Flowable> call = client.exchange( + GET("/import/mappings?importName=ExperimentsTemplateMap") + .contentType(MediaType.APPLICATION_JSON) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + HttpResponse response = call.blockingFirst(); + String mappingId = JsonParser.parseString(Objects.requireNonNull(response.body())).getAsJsonObject() + .getAsJsonObject("result") + .getAsJsonArray("data") + .get(0).getAsJsonObject().get("id").getAsString(); + + // Add traits to program + traits = createTraits(2); + AuthenticatedUser user = new AuthenticatedUser(testUser.getName(), new ArrayList<>(), testUser.getId(), new ArrayList<>()); + try { + ontologyService.createTraits(program.getId(), traits, user, false); + } catch (ValidatorException e) { + System.err.println(e.getErrors()); + throw e; + } + + // Add germplasm to program + List germplasm = createGermplasm(1); + BrAPIExternalReference newReference = new BrAPIExternalReference(); + newReference.setReferenceSource(String.format("%s/programs", BRAPI_REFERENCE_SOURCE)); + newReference.setReferenceID(program.getId().toString()); + + germplasm.forEach(germ -> germ.getExternalReferences().add(newReference)); + + germplasmDAO.createBrAPIGermplasm(germplasm, program.getId(), null); + + // Make test experiment import + Map row1 = makeExpImportRow("Env1"); + Map row2 = makeExpImportRow("Env2"); + + // Add test observation data + for (Trait trait : traits) { + Random random = new Random(); + + // TODO: test for sending obs data as double. + // A float is returned from the backend instead of double. there is a separate card to fix this. + // Double val1 = Math.random(); + + Float val1 = random.nextFloat(); + row1.put(trait.getObservationVariableName(), val1); + } + + rows.add(row1); + rows.add(row2); + + // Import test experiment, environments, and any observations + JsonObject importResult = importTestUtils.uploadAndFetch( + writeDataToFile(rows, traits), + null, + true, + client, + program, + mappingId); + experimentId = importResult + .get("preview").getAsJsonObject() + .get("rows").getAsJsonArray() + .get(0).getAsJsonObject() + .get("trial").getAsJsonObject() + .get("id").getAsString(); + // Add environmentIds. + envIds.add(getEnvId(importResult, 0)); + envIds.add(getEnvId(importResult, 1)); + } + + @Test + @SneakyThrows + public void testPostObsUnitNotFound() { + BrAPIObservationUnit ou = new BrAPIObservationUnit().observationUnitName("test"); + + Flowable> postCall = client.exchange( + POST(String.format("/programs/%s/brapi/v2/observationunits", + program.getId().toString()), List.of(ou)) + .contentType(MediaType.APPLICATION_JSON) + .bearerAuth("test-registered-user"), String.class + ); + + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse response = postCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } + + @Test + @SneakyThrows + public void testPutSingleOUNotFound() { + Flowable> call = client.exchange( + GET(String.format("/programs/%s/brapi/v2/observationunits?trialDbId=%s", program.getId(), experimentId)) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse response = call.blockingFirst(); + assertEquals(HttpStatus.OK, response.getStatus()); + + JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); + + BrAPIObservationUnit ou = gson.fromJson(responseObj.get("result").getAsJsonObject().get("data").getAsJsonArray().get(0).getAsJsonObject(), BrAPIObservationUnit.class); + ou.setGermplasmName("updated"); + + Flowable> postCall = client.exchange( + PUT(String.format("/programs/%s/brapi/v2/observationunits/%s", + program.getId().toString(), ou.getObservationUnitDbId()), ou) + .contentType(MediaType.APPLICATION_JSON) + .bearerAuth("test-registered-user"), String.class + ); + + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse errorResponse = postCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } + + @Test + @SneakyThrows + public void testPutMultipleOUsNotFound() { + Flowable> call = client.exchange( + GET(String.format("/programs/%s/brapi/v2/observationunits?trialDbId=%s", program.getId(), experimentId)).bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse response = call.blockingFirst(); + assertEquals(HttpStatus.OK, response.getStatus()); + + JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); + + BrAPIObservationUnit ou = gson.fromJson(responseObj.get("result").getAsJsonObject().get("data").getAsJsonArray().get(0).getAsJsonObject(), BrAPIObservationUnit.class); + ou.setGermplasmName("updated"); + + Flowable> postCall = client.exchange( + PUT(String.format("/programs/%s/brapi/v2/observationunits", + program.getId().toString()), Map.of(ou.getObservationUnitDbId(), ou)) + .contentType(MediaType.APPLICATION_JSON) + .bearerAuth("test-registered-user"), String.class + ); + + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse errorResponse = postCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } + + @Test + @SneakyThrows + public void testGetObsUnitTableNotFound() { + Flowable> getCall = client.exchange( + GET(String.format("/programs/%s/brapi/v2/observationunits/table", + program.getId().toString())) + .contentType(MediaType.APPLICATION_JSON) + .bearerAuth("test-registered-user"), String.class + ); + + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse response = getCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } + + @Test + public void testGetOUListByExpId() { + Flowable> call = client.exchange( + GET(String.format("/programs/%s/brapi/v2/observationunits?trialDbId=%s", program.getId(), experimentId)) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse response = call.blockingFirst(); + assertEquals(HttpStatus.OK, response.getStatus()); + + JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); + + JsonArray ous = responseObj.getAsJsonObject("result").getAsJsonArray("data"); + assertEquals(2, ous.size()); + List ouNames = new ArrayList<>(); + for(var study : ous) { + ouNames.add(study.getAsJsonObject().get("observationUnitName").getAsString()); + } + assertTrue(ouNames.contains("Env1 - a-1")); + assertTrue(ouNames.contains("Env2 - a-1")); + } + + @Test + public void testGetOUById() { + Flowable> call = client.exchange( + GET(String.format("/programs/%s/brapi/v2/observationunits?trialDbId=%s", program.getId(), experimentId)) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse response = call.blockingFirst(); + assertEquals(HttpStatus.OK, response.getStatus()); + + JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); + JsonArray ous = responseObj.getAsJsonObject("result").getAsJsonArray("data"); + assertEquals(2, ous.size()); + JsonObject ou = ous.get(0).getAsJsonObject(); + + Flowable> ouCall = client.exchange( + GET(String.format("/programs/%s/brapi/v2/observationunits/%s", program.getId(), ou.get("observationUnitDbId").getAsString())) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse ouResponse = ouCall.blockingFirst(); + assertEquals(HttpStatus.OK, ouResponse.getStatus()); + + AtomicReference brAPIObservationUnitSingleResponse = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> { + brAPIObservationUnitSingleResponse.set(gson.fromJson(ouResponse.body(), BrAPIObservationUnitSingleResponse.class)); + }); + + assertNotNull(brAPIObservationUnitSingleResponse.get()); + assertNotNull(brAPIObservationUnitSingleResponse.get().getResult()); + assertEquals(ou.get("observationUnitDbId").getAsString(), brAPIObservationUnitSingleResponse.get().getResult().getObservationUnitDbId()); + } + + private File writeDataToFile(List> data, List traits) throws IOException { + File file = File.createTempFile("test", ".csv"); + + if(traits != null) { + traits.forEach(trait -> columns.add( + Column.builder() + .value(trait.getObservationVariableName()) + .dataType(Column.ColumnDataType.STRING) + .build()) + ); + } + ByteArrayOutputStream byteArrayOutputStream = CSVWriter.writeToCSV(columns, data); + FileOutputStream fos = new FileOutputStream(file); + fos.write(byteArrayOutputStream.toByteArray()); + + return file; + } + + private String getEnvId(JsonObject result, int index) { + return result + .get("preview").getAsJsonObject() + .get("rows").getAsJsonArray() + .get(index).getAsJsonObject() + .get("study").getAsJsonObject() + .get("brAPIObject").getAsJsonObject() + .get("externalReferences").getAsJsonArray() + .get(2).getAsJsonObject() + .get("referenceID").getAsString(); + } + + private List createGermplasm(int numToCreate) { + List germplasm = new ArrayList<>(); + for (int i = 0; i < numToCreate; i++) { + String gid = ""+(i+1); + BrAPIGermplasm testGermplasm = new BrAPIGermplasm(); + testGermplasm.setGermplasmName(String.format("Germplasm %s [TEST-%s]", gid, gid)); + testGermplasm.setSeedSource("Wild"); + testGermplasm.setAccessionNumber(gid); + testGermplasm.setDefaultDisplayName(String.format("Germplasm %s", gid)); + JsonObject additionalInfo = new JsonObject(); + additionalInfo.addProperty("importEntryNumber", gid); + additionalInfo.addProperty("breedingMethod", "Allopolyploid"); + testGermplasm.setAdditionalInfo(additionalInfo); + List externalRef = new ArrayList<>(); + BrAPIExternalReference testReference = new BrAPIExternalReference(); + testReference.setReferenceSource(BRAPI_REFERENCE_SOURCE); + testReference.setReferenceID(UUID.randomUUID().toString()); + externalRef.add(testReference); + testGermplasm.setExternalReferences(externalRef); + germplasm.add(testGermplasm); + } + + return germplasm; + } + + private List createTraits(int numToCreate) { + List traits = new ArrayList<>(); + for (int i = 0; i < numToCreate; i++) { + String varName = "tt_test_" + (i + 1); + traits.add(Trait.builder() + .observationVariableName(varName) + .fullName(varName) + .entity("Plant " + i) + .attribute("height " + i) + .traitDescription("test") + .programObservationLevel(ProgramObservationLevel.builder().name("Plot").build()) + .scale(Scale.builder() + .scaleName("test scale") + .dataType(DataType.NUMERICAL) + .validValueMin(0) + .validValueMax(100) + .build()) + .method(Method.builder() + .description("test method") + .methodClass("test method") + .build()) + .build()); + } + + return traits; + } + + private Map makeExpImportRow(String environment) { + Map row = new HashMap<>(); + row.put(ExperimentObservation.Columns.GERMPLASM_GID, "1"); + row.put(ExperimentObservation.Columns.TEST_CHECK, "T"); + row.put(ExperimentObservation.Columns.EXP_TITLE, "Test Exp"); + row.put(ExperimentObservation.Columns.EXP_UNIT, "Plot"); + row.put(ExperimentObservation.Columns.EXP_TYPE, "Phenotyping"); + row.put(ExperimentObservation.Columns.ENV, environment); + row.put(ExperimentObservation.Columns.ENV_LOCATION, "Location A"); + row.put(ExperimentObservation.Columns.ENV_YEAR, "2023"); + row.put(ExperimentObservation.Columns.EXP_UNIT_ID, environment+" - a-1"); + row.put(ExperimentObservation.Columns.REP_NUM, "1"); + row.put(ExperimentObservation.Columns.BLOCK_NUM, "1"); + row.put(ExperimentObservation.Columns.ROW, "1"); + row.put(ExperimentObservation.Columns.COLUMN, "1"); + return row; + } +} diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableControllerIntegrationTest.java new file mode 100644 index 000000000..1596f26e7 --- /dev/null +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableControllerIntegrationTest.java @@ -0,0 +1,22 @@ +/* + * 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; + +public class BrAPIObservationVariableControllerIntegrationTest { + //TODO +} diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java new file mode 100644 index 000000000..cae4497e5 --- /dev/null +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java @@ -0,0 +1,22 @@ +/* + * 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; + +public class BrAPIObservationsControllerIntegrationTest { + //TODO +} diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIStudiesControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIStudiesControllerIntegrationTest.java new file mode 100644 index 000000000..1373d9ab3 --- /dev/null +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIStudiesControllerIntegrationTest.java @@ -0,0 +1,411 @@ +/* + * 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 com.google.gson.*; +import io.kowalski.fannypack.FannyPack; +import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +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; +import lombok.SneakyThrows; +import org.brapi.v2.model.BrAPIExternalReference; +import org.brapi.v2.model.core.BrAPIStudy; +import org.brapi.v2.model.core.response.BrAPIStudySingleResponse; +import org.brapi.v2.model.germ.BrAPIGermplasm; +import org.breedinginsight.BrAPITest; +import org.breedinginsight.TestUtils; +import org.breedinginsight.api.auth.AuthenticatedUser; +import org.breedinginsight.api.model.v1.request.ProgramRequest; +import org.breedinginsight.api.model.v1.request.SpeciesRequest; +import org.breedinginsight.api.v1.controller.TestTokenValidator; +import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; +import org.breedinginsight.brapps.importer.ImportTestUtils; +import org.breedinginsight.brapps.importer.model.imports.experimentObservation.ExperimentObservation; +import org.breedinginsight.dao.db.enums.DataType; +import org.breedinginsight.dao.db.tables.pojos.SpeciesEntity; +import org.breedinginsight.daos.SpeciesDAO; +import org.breedinginsight.daos.UserDAO; +import org.breedinginsight.model.*; +import org.breedinginsight.services.OntologyService; +import org.breedinginsight.services.exceptions.ValidatorException; +import org.breedinginsight.services.parsers.experiment.ExperimentFileColumns; +import org.breedinginsight.services.writers.CSVWriter; +import org.jooq.DSLContext; +import org.junit.jupiter.api.*; + +import javax.inject.Inject; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +import static io.micronaut.http.HttpRequest.*; +import static io.micronaut.http.HttpRequest.GET; +import static org.junit.jupiter.api.Assertions.*; + +@MicronautTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class BrAPIStudiesControllerIntegrationTest extends BrAPITest { + private Program program; + private String experimentId; + private List envIds = new ArrayList<>(); + private final List> rows = new ArrayList<>(); + private final List columns = ExperimentFileColumns.getOrderedColumns(); + private List traits; + + @Property(name = "brapi.server.reference-source") + private String BRAPI_REFERENCE_SOURCE; + @Inject + private DSLContext dsl; + @Inject + private UserDAO userDAO; + @Inject + private SpeciesDAO speciesDAO; + @Inject + private OntologyService ontologyService; + @Inject + private BrAPIGermplasmDAO germplasmDAO; + + @Inject + @Client("/${micronaut.bi.api.version}") + private RxHttpClient client; + + private final Gson gson = new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, (JsonDeserializer) + (json, type, context) -> OffsetDateTime.parse(json.getAsString())) + .create(); + + @BeforeAll + void setup() throws Exception { + FannyPack fp = FannyPack.fill("src/test/resources/sql/ImportControllerIntegrationTest.sql"); + ImportTestUtils importTestUtils = new ImportTestUtils(); + FannyPack securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); + FannyPack brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); + + // Test User + User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); + + // Species + super.getBrapiDsl().execute(brapiFp.get("InsertSpecies")); + SpeciesEntity validSpecies = speciesDAO.findAll().get(0); + SpeciesRequest speciesRequest = SpeciesRequest.builder() + .commonName(validSpecies.getCommonName()) + .id(validSpecies.getId()) + .build(); + + // Test Program + ProgramRequest programRequest = ProgramRequest.builder() + .name("Test Program") + .abbreviation("Test") + .documentationUrl("localhost:8080") + .objective("To test things") + .species(speciesRequest) + .key("TEST") + .build(); + program = TestUtils.insertAndFetchTestProgram(gson, client, programRequest); + + dsl.execute(securityFp.get("InsertProgramRolesBreeder"), testUser.getId().toString(), program.getId()); + dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); + + // Get experiment import map + Flowable> call = client.exchange( + GET("/import/mappings?importName=ExperimentsTemplateMap") + .contentType(MediaType.APPLICATION_JSON) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + HttpResponse response = call.blockingFirst(); + String mappingId = JsonParser.parseString(Objects.requireNonNull(response.body())).getAsJsonObject() + .getAsJsonObject("result") + .getAsJsonArray("data") + .get(0).getAsJsonObject().get("id").getAsString(); + + // Add traits to program + traits = createTraits(2); + AuthenticatedUser user = new AuthenticatedUser(testUser.getName(), new ArrayList<>(), testUser.getId(), new ArrayList<>()); + try { + ontologyService.createTraits(program.getId(), traits, user, false); + } catch (ValidatorException e) { + System.err.println(e.getErrors()); + throw e; + } + + // Add germplasm to program + List germplasm = createGermplasm(1); + BrAPIExternalReference newReference = new BrAPIExternalReference(); + newReference.setReferenceSource(String.format("%s/programs", BRAPI_REFERENCE_SOURCE)); + newReference.setReferenceID(program.getId().toString()); + + germplasm.forEach(germ -> germ.getExternalReferences().add(newReference)); + + germplasmDAO.createBrAPIGermplasm(germplasm, program.getId(), null); + + // Make test experiment import + Map row1 = makeExpImportRow("Env1"); + Map row2 = makeExpImportRow("Env2"); + + // Add test observation data + for (Trait trait : traits) { + Random random = new Random(); + + // TODO: test for sending obs data as double. + // A float is returned from the backend instead of double. there is a separate card to fix this. + // Double val1 = Math.random(); + + Float val1 = random.nextFloat(); + row1.put(trait.getObservationVariableName(), val1); + } + + rows.add(row1); + rows.add(row2); + + // Import test experiment, environments, and any observations + JsonObject importResult = importTestUtils.uploadAndFetch( + writeDataToFile(rows, traits), + null, + true, + client, + program, + mappingId); + experimentId = importResult + .get("preview").getAsJsonObject() + .get("rows").getAsJsonArray() + .get(0).getAsJsonObject() + .get("trial").getAsJsonObject() + .get("id").getAsString(); + // Add environmentIds. + envIds.add(getEnvId(importResult, 0)); + envIds.add(getEnvId(importResult, 1)); + } + + @Test + @SneakyThrows + public void testPostGetStudiesNotFound() { + BrAPIStudy study = new BrAPIStudy().studyName("test study") + .studyCode("123") + .studyType("Phenotyping Trial") + .studyDescription("Test study description") + .active(true) + .startDate(OffsetDateTime.of(2021, 1, 5, 0, 0, 0, 0, ZoneOffset.UTC)) + .endDate(OffsetDateTime.of(2021, 2, 5, 0, 0, 0, 0, ZoneOffset.UTC)); + + Flowable> postCall = client.exchange( + POST(String.format("/programs/%s/brapi/v2/studies", + program.getId().toString()), List.of(study)) + .contentType(MediaType.APPLICATION_JSON) + .bearerAuth("test-registered-user"), String.class + ); + + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse response = postCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } + + @Test + @SneakyThrows + public void testPutGetStudiesNotFound() { + BrAPIStudy study = new BrAPIStudy().studyName("test study") + .studyCode("123") + .studyType("Phenotyping Trial") + .studyDescription("Test study description") + .active(true) + .startDate(OffsetDateTime.of(2021, 1, 5, 0, 0, 0, 0, ZoneOffset.UTC)) + .endDate(OffsetDateTime.of(2021, 2, 5, 0, 0, 0, 0, ZoneOffset.UTC)); + + Flowable> putCall = client.exchange( + PUT(String.format("/programs/%s/brapi/v2/studies/abc", + program.getId().toString()), List.of(study)) + .contentType(MediaType.APPLICATION_JSON) + .bearerAuth("test-registered-user"), String.class + ); + + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse response = putCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } + + @Test + public void testGetStudiesListByExpId() { + Flowable> call = client.exchange( + GET(String.format("/programs/%s/brapi/v2/studies?trialDbId=%s", program.getId(), experimentId)) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse response = call.blockingFirst(); + assertEquals(HttpStatus.OK, response.getStatus()); + + JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); + JsonArray studies = responseObj.getAsJsonObject("result").getAsJsonArray("data"); + assertEquals(2, studies.size()); + List studyNames = new ArrayList<>(); + for(var study : studies) { + studyNames.add(study.getAsJsonObject().get("studyName").getAsString()); + } + assertTrue(studyNames.contains("Env1")); + assertTrue(studyNames.contains("Env2")); + } + + @Test + public void testGetStudyById() { + Flowable> call = client.exchange( + GET(String.format("/programs/%s/brapi/v2/studies?trialDbId=%s", program.getId(), experimentId)) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse response = call.blockingFirst(); + assertEquals(HttpStatus.OK, response.getStatus()); + + JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); + JsonArray studies = responseObj.getAsJsonObject("result").getAsJsonArray("data"); + assertEquals(2, studies.size()); + JsonObject study = studies.get(0).getAsJsonObject(); + + Flowable> studyCall = client.exchange( + GET(String.format("/programs/%s/brapi/v2/studies/%s", program.getId(), study.get("studyDbId").getAsString())) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse studyResponse = studyCall.blockingFirst(); + assertEquals(HttpStatus.OK, studyResponse.getStatus()); + + AtomicReference brAPIStudySingleResponse = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> { + brAPIStudySingleResponse.set(gson.fromJson(studyResponse.body(), BrAPIStudySingleResponse.class)); + }); + + assertNotNull(brAPIStudySingleResponse.get()); + assertNotNull(brAPIStudySingleResponse.get().getResult()); + assertEquals(study.get("studyDbId").getAsString(), brAPIStudySingleResponse.get().getResult().getStudyDbId()); + } + + private File writeDataToFile(List> data, List traits) throws IOException { + File file = File.createTempFile("test", ".csv"); + + if(traits != null) { + traits.forEach(trait -> columns.add( + Column.builder() + .value(trait.getObservationVariableName()) + .dataType(Column.ColumnDataType.STRING) + .build()) + ); + } + ByteArrayOutputStream byteArrayOutputStream = CSVWriter.writeToCSV(columns, data); + FileOutputStream fos = new FileOutputStream(file); + fos.write(byteArrayOutputStream.toByteArray()); + + return file; + } + + private String getEnvId(JsonObject result, int index) { + return result + .get("preview").getAsJsonObject() + .get("rows").getAsJsonArray() + .get(index).getAsJsonObject() + .get("study").getAsJsonObject() + .get("brAPIObject").getAsJsonObject() + .get("externalReferences").getAsJsonArray() + .get(2).getAsJsonObject() + .get("referenceID").getAsString(); + } + + private List createGermplasm(int numToCreate) { + List germplasm = new ArrayList<>(); + for (int i = 0; i < numToCreate; i++) { + String gid = ""+(i+1); + BrAPIGermplasm testGermplasm = new BrAPIGermplasm(); + testGermplasm.setGermplasmName(String.format("Germplasm %s [TEST-%s]", gid, gid)); + testGermplasm.setSeedSource("Wild"); + testGermplasm.setAccessionNumber(gid); + testGermplasm.setDefaultDisplayName(String.format("Germplasm %s", gid)); + JsonObject additionalInfo = new JsonObject(); + additionalInfo.addProperty("importEntryNumber", gid); + additionalInfo.addProperty("breedingMethod", "Allopolyploid"); + testGermplasm.setAdditionalInfo(additionalInfo); + List externalRef = new ArrayList<>(); + BrAPIExternalReference testReference = new BrAPIExternalReference(); + testReference.setReferenceSource(BRAPI_REFERENCE_SOURCE); + testReference.setReferenceID(UUID.randomUUID().toString()); + externalRef.add(testReference); + testGermplasm.setExternalReferences(externalRef); + germplasm.add(testGermplasm); + } + + return germplasm; + } + + private List createTraits(int numToCreate) { + List traits = new ArrayList<>(); + for (int i = 0; i < numToCreate; i++) { + String varName = "tt_test_" + (i + 1); + traits.add(Trait.builder() + .observationVariableName(varName) + .fullName(varName) + .entity("Plant " + i) + .attribute("height " + i) + .traitDescription("test") + .programObservationLevel(ProgramObservationLevel.builder().name("Plot").build()) + .scale(Scale.builder() + .scaleName("test scale") + .dataType(DataType.NUMERICAL) + .validValueMin(0) + .validValueMax(100) + .build()) + .method(Method.builder() + .description("test method") + .methodClass("test method") + .build()) + .build()); + } + + return traits; + } + + private Map makeExpImportRow(String environment) { + Map row = new HashMap<>(); + row.put(ExperimentObservation.Columns.GERMPLASM_GID, "1"); + row.put(ExperimentObservation.Columns.TEST_CHECK, "T"); + row.put(ExperimentObservation.Columns.EXP_TITLE, "Test Exp"); + row.put(ExperimentObservation.Columns.EXP_UNIT, "Plot"); + row.put(ExperimentObservation.Columns.EXP_TYPE, "Phenotyping"); + row.put(ExperimentObservation.Columns.ENV, environment); + row.put(ExperimentObservation.Columns.ENV_LOCATION, "Location A"); + row.put(ExperimentObservation.Columns.ENV_YEAR, "2023"); + row.put(ExperimentObservation.Columns.EXP_UNIT_ID, "a-1"); + row.put(ExperimentObservation.Columns.REP_NUM, "1"); + row.put(ExperimentObservation.Columns.BLOCK_NUM, "1"); + row.put(ExperimentObservation.Columns.ROW, "1"); + row.put(ExperimentObservation.Columns.COLUMN, "1"); + return row; + } +} diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java index ebb3979d1..0ef73d284 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java @@ -25,6 +25,7 @@ import io.micronaut.http.MediaType; import io.micronaut.http.client.RxHttpClient; import io.micronaut.http.client.annotation.Client; +import io.micronaut.http.client.exceptions.HttpClientResponseException; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import io.reactivex.Flowable; import lombok.SneakyThrows; @@ -49,7 +50,6 @@ import javax.inject.Inject; import java.time.OffsetDateTime; -import java.time.ZoneOffset; import java.util.Arrays; import java.util.Collections; import java.util.UUID; @@ -145,12 +145,15 @@ public void testRootServerInfo() { assertEquals("Breeding Insight", serverInfo.getOrganizationName()); assertEquals("DeltaBreed", serverInfo.getServerName()); assertEquals("bidevteam@cornell.edu", serverInfo.getContactEmail()); - assertEquals("breedinginsight.org", serverInfo.getOrganizationURL()); + assertEquals("https://breedinginsight.org", serverInfo.getOrganizationURL()); + assertEquals("BrAPI endpoints are not implemented at the root of this domain. Please make BrAPI calls in the context of a program (ex: https://app.breedinginsight.net/v1/programs/{programId}/brapi/v2)", serverInfo.getServerDescription()); + assertEquals("Cornell University, Ithaca, NY, USA", serverInfo.getLocation()); + assertEquals("https://brapi.org/specification", serverInfo.getDocumentationURL()); } @Test @SneakyThrows - public void testPostGetVariablesProxy() { + public void testPostVariablesNotFound() { BrAPIObservationVariable variable = generateVariable(); Flowable> postCall = biClient.exchange( @@ -161,94 +164,15 @@ public void testPostGetVariablesProxy() { .bearerAuth("test-registered-user"), String.class ); - HttpResponse postResponse; - try { - postResponse = postCall.blockingFirst(); - } catch (Exception e) { - throw new Exception(e); - } - //check the POST call was successful - assertEquals(HttpStatus.OK, postResponse.getStatus()); - - BrAPIObservationVariable createdVariable = GSON.fromJson(postResponse.body(), BrAPIObservationVariableListResponse.class) - .getResult() - .getData() - .get(0); - - //and that a variable is returned - assertNotNull(createdVariable); - //and that the variable has been assigned an ID - assertNotNull(createdVariable.getObservationVariableDbId(), "observationVariableDbId is null"); - - Flowable> getCall = biClient.exchange( - GET(String.format("%s/programs/%s/brapi/v2/variables/%s", - biApiVersion, - validProgram.getId().toString(), - createdVariable.getObservationVariableDbId())) - .bearerAuth("test-registered-user"), String.class - ); - - HttpResponse getResponse; - try { - getResponse = getCall.blockingFirst(); - } catch (Exception e) { - throw new Exception(e); - } - assertEquals(HttpStatus.OK, getResponse.getStatus()); - assertNotNull(getResponse.body(), "Response body is empty"); - BrAPIObservationVariableSingleResponse brAPIObservationVariableSingleResponse = GSON.fromJson(getResponse.body(), BrAPIObservationVariableSingleResponse.class); - - BrAPIObservationVariable fetchedVariable = brAPIObservationVariableSingleResponse.getResult(); - assertNotNull(fetchedVariable, "Observation Variable was not found"); - assertEquals(createdVariable.getObservationVariableDbId(), fetchedVariable.getObservationVariableDbId()); - //make sure the original values sent in the POST were saved correctly - assertEquals(variable.getObservationVariableName(), fetchedVariable.getObservationVariableName()); - assertEquals(variable.getExternalReferences(), fetchedVariable.getExternalReferences()); - assertEquals(variable.getCommonCropName(), fetchedVariable.getCommonCropName()); - - assertNotNull(fetchedVariable.getTrait(), "Trait is null"); - assertEquals(createdVariable.getTrait().getTraitDbId(), fetchedVariable.getTrait().getTraitDbId()); - //make sure the original values sent in the POST were saved correctly - assertEquals(variable.getTrait().getTraitName(), fetchedVariable.getTrait().getTraitName()); - assertEquals(variable.getTrait().getTraitClass(), fetchedVariable.getTrait().getTraitClass()); - - - assertNotNull(fetchedVariable.getMethod(), "Method is null"); - assertEquals(createdVariable.getMethod().getMethodDbId(), fetchedVariable.getMethod().getMethodDbId()); - //make sure the original values sent in the POST were saved correctly - assertEquals(variable.getMethod().getMethodName(), fetchedVariable.getMethod().getMethodName()); - assertEquals(variable.getMethod().getMethodClass(), fetchedVariable.getMethod().getMethodClass()); - - assertNotNull(fetchedVariable.getScale(), "Scale is null"); - assertEquals(createdVariable.getScale().getScaleDbId(), fetchedVariable.getScale().getScaleDbId()); - //make sure the original values sent in the POST were saved correctly - assertEquals(variable.getScale().getScaleName(), fetchedVariable.getScale().getScaleName()); - - Flowable> getScaleCall = biClient.exchange( - GET(String.format("%s/programs/%s/brapi/v2/scales/%s", - biApiVersion, - validProgram.getId().toString(), - createdVariable.getScale().getScaleDbId())) - .bearerAuth("test-registered-user"), String.class - ); - - HttpResponse getScaleResponse; - try { - getScaleResponse = getScaleCall.blockingFirst(); - } catch (Exception e) { - throw new Exception(e); - } - assertEquals(HttpStatus.OK, getScaleResponse.getStatus()); - - BrAPIScale scaleResponse = GSON.fromJson(JsonParser.parseString(getScaleResponse.body()).getAsJsonObject().getAsJsonObject("result"), BrAPIScale.class); - - //TODO this is not being returned -// assertEquals(variable.getScale().getDataType(), scaleResponse.getDataType()); + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse response = postCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); } @Test @SneakyThrows - public void testPutVariablesProxy() { + public void testPutVariablesNotFound() { BrAPIObservationVariable variable = generateVariable(); Flowable> postCall = biClient.exchange( @@ -259,127 +183,13 @@ public void testPutVariablesProxy() { .bearerAuth("test-registered-user"), String.class ); - HttpResponse postResponse; - try { - postResponse = postCall.blockingFirst(); - } catch (Exception e) { - throw new Exception(e); - } - //check the POST call was successful - assertEquals(HttpStatus.OK, postResponse.getStatus()); - - BrAPIObservationVariable createdVariable = GSON.fromJson(postResponse.body(), BrAPIObservationVariableListResponse.class) - .getResult() - .getData() - .get(0); - - createdVariable.setObservationVariableName("Updated variable name"); - - - Flowable> putCall = biClient.exchange( - PUT(String.format("%s/programs/%s/brapi/v2/variables/%s", - biApiVersion, - validProgram.getId().toString(), createdVariable.getObservationVariableDbId()), variable) - .contentType(MediaType.APPLICATION_JSON) - .bearerAuth("test-registered-user"), String.class - ); - - HttpResponse putResponse; - try { - putResponse = putCall.blockingFirst(); - } catch (Exception e) { - throw new Exception(e); - } - //check the PUT call was successful - assertEquals(HttpStatus.OK, putResponse.getStatus()); - - Flowable> getCall = biClient.exchange( - GET(String.format("%s/programs/%s/brapi/v2/variables/%s", - biApiVersion, - validProgram.getId().toString(), - createdVariable.getObservationVariableDbId())) - .bearerAuth("test-registered-user"), String.class - ); - - HttpResponse getResponse; - try { - getResponse = getCall.blockingFirst(); - } catch (Exception e) { - throw new Exception(e); - } - assertEquals(HttpStatus.OK, getResponse.getStatus()); - BrAPIObservationVariableSingleResponse brAPIObservationVariableSingleResponse = GSON.fromJson(getResponse.body(), BrAPIObservationVariableSingleResponse.class); - - BrAPIObservationVariable fetchedVariable = brAPIObservationVariableSingleResponse.getResult(); - //make sure the updated value persisted - assertEquals(variable.getObservationVariableName(), fetchedVariable.getObservationVariableName()); + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse response = postCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); } - @Test - @SneakyThrows - public void testPostGetStudiesProxy() { - BrAPIStudy study = new BrAPIStudy().studyName("test study") - .studyCode("123") - .studyType("Phenotyping Trial") - .studyDescription("Test study description") - .active(true) - .startDate(OffsetDateTime.of(2021, 1, 5, 0, 0, 0, 0, ZoneOffset.UTC)) - .endDate(OffsetDateTime.of(2021, 2, 5, 0, 0, 0, 0, ZoneOffset.UTC)); - - Flowable> postCall = biClient.exchange( - POST(String.format("%s/programs/%s/brapi/v2/studies", - biApiVersion, - validProgram.getId().toString()), Arrays.asList(study)) - .contentType(MediaType.APPLICATION_JSON) - .bearerAuth("test-registered-user"), String.class - ); - - HttpResponse postResponse; - try { - postResponse = postCall.blockingFirst(); - } catch (Exception e) { - throw new Exception(e); - } - //check the POST call was successful - assertEquals(HttpStatus.OK, postResponse.getStatus()); - - BrAPIStudy createdStudy = GSON.fromJson(postResponse.body(), BrAPIStudyListResponse.class) - .getResult() - .getData() - .get(0); - - //and that a study is returned - assertNotNull(createdStudy); - //and that the study has been assigned an ID - assertNotNull(createdStudy.getStudyDbId(), "studyDbId is null"); - - Flowable> getCall = biClient.exchange( - GET(String.format("%s/programs/%s/brapi/v2/studies/%s", - biApiVersion, - validProgram.getId().toString(), - createdStudy.getStudyDbId())) - .bearerAuth("test-registered-user"), String.class - ); - HttpResponse getResponse; - try { - getResponse = getCall.blockingFirst(); - } catch (Exception e) { - throw new Exception(e); - } - assertEquals(HttpStatus.OK, getResponse.getStatus()); - assertNotNull(getResponse.body(), "Response body is empty"); - - BrAPIStudy fetchedStudy = GSON.fromJson(getResponse.body(), BrAPIStudySingleResponse.class).getResult(); - - assertEquals(study.getStudyName(), fetchedStudy.getStudyName()); - assertEquals(study.getStudyCode(), fetchedStudy.getStudyCode()); - assertEquals(study.getStudyType(), fetchedStudy.getStudyType()); - assertEquals(study.getStudyDescription(), fetchedStudy.getStudyDescription()); - assertEquals(study.isActive(), fetchedStudy.isActive()); - assertEquals(study.getStartDate(), fetchedStudy.getStartDate()); - assertEquals(study.getEndDate(), fetchedStudy.getEndDate()); - } private BrAPIObservationVariable generateVariable() { var random = UUID.randomUUID() diff --git a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java index 0c52536fc..fb95e4dd2 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java @@ -20,7 +20,7 @@ import org.breedinginsight.api.v1.controller.TestTokenValidator; import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; -import org.breedinginsight.brapps.importer.daos.BrAPIListDAO; +import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.dao.db.tables.pojos.BiUserEntity; import org.breedinginsight.daos.UserDAO; import org.breedinginsight.model.Program; diff --git a/src/test/java/org/breedinginsight/brapps/importer/ExperimentFileImportTest.java b/src/test/java/org/breedinginsight/brapps/importer/ExperimentFileImportTest.java index 8db3b5bf4..b3bcb0da1 100644 --- a/src/test/java/org/breedinginsight/brapps/importer/ExperimentFileImportTest.java +++ b/src/test/java/org/breedinginsight/brapps/importer/ExperimentFileImportTest.java @@ -45,8 +45,7 @@ import org.breedinginsight.api.model.v1.request.ProgramRequest; import org.breedinginsight.api.model.v1.request.SpeciesRequest; import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; -import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; -import org.breedinginsight.brapps.importer.daos.*; +import org.breedinginsight.brapi.v2.dao.*; import org.breedinginsight.brapps.importer.model.imports.experimentObservation.ExperimentObservation.Columns; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.dao.db.tables.pojos.BiUserEntity; diff --git a/src/test/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAOTest.java b/src/test/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAOTest.java index ce742dc7c..b7a2ba564 100644 --- a/src/test/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAOTest.java +++ b/src/test/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAOTest.java @@ -14,6 +14,7 @@ import org.breedinginsight.api.model.v1.request.SpeciesRequest; import org.breedinginsight.api.v1.controller.TestTokenValidator; import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; +import org.breedinginsight.brapi.v2.dao.BrAPIObservationUnitDAO; import org.breedinginsight.brapps.importer.model.ImportProgress; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.dao.db.tables.pojos.SpeciesEntity; diff --git a/src/test/java/org/breedinginsight/services/BrAPIGermplasmServiceUnitTest.java b/src/test/java/org/breedinginsight/services/BrAPIGermplasmServiceUnitTest.java index 49d55d9e9..afa00df4e 100644 --- a/src/test/java/org/breedinginsight/services/BrAPIGermplasmServiceUnitTest.java +++ b/src/test/java/org/breedinginsight/services/BrAPIGermplasmServiceUnitTest.java @@ -12,7 +12,7 @@ import org.breedinginsight.DatabaseTest; import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; -import org.breedinginsight.brapps.importer.daos.BrAPIListDAO; +import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.exports.FileType; import org.breedinginsight.daos.ProgramDAO; diff --git a/src/test/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImplIntegrationTest.java b/src/test/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImplIntegrationTest.java index 56b3c099b..452a36268 100644 --- a/src/test/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImplIntegrationTest.java +++ b/src/test/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImplIntegrationTest.java @@ -42,11 +42,11 @@ import org.brapi.v2.model.pheno.response.BrAPIObservationUnitListResponse; import org.brapi.v2.model.pheno.response.BrAPIObservationUnitListResponseResult; import org.breedinginsight.DatabaseTest; -import org.breedinginsight.brapps.importer.daos.BrAPITrialDAO; +import org.breedinginsight.brapi.v2.dao.BrAPITrialDAO; import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.daos.ImportMappingDAO; -import org.breedinginsight.brapps.importer.daos.impl.BrAPITrialDAOImpl; -import org.breedinginsight.brapps.importer.daos.impl.ImportMappingDAOImpl; +import org.breedinginsight.brapi.v2.dao.impl.BrAPITrialDAOImpl; +import org.breedinginsight.brapi.v2.dao.impl.ImportMappingDAOImpl; import org.breedinginsight.brapps.importer.model.ImportProgress; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.model.mapping.ImportMapping; From 42ea0c1b9925973b4269ffb23e700e267a20aee2 Mon Sep 17 00:00:00 2001 From: timparsons Date: Wed, 29 Nov 2023 13:26:03 -0500 Subject: [PATCH 09/20] [BI-1921] fixing unit tests --- .../TraitValidatorIntegrationTest.java | 97 +++++++++++++++---- 1 file changed, 79 insertions(+), 18 deletions(-) diff --git a/src/test/java/org/breedinginsight/services/validators/TraitValidatorIntegrationTest.java b/src/test/java/org/breedinginsight/services/validators/TraitValidatorIntegrationTest.java index d5c3dc7d6..3006bc7b5 100644 --- a/src/test/java/org/breedinginsight/services/validators/TraitValidatorIntegrationTest.java +++ b/src/test/java/org/breedinginsight/services/validators/TraitValidatorIntegrationTest.java @@ -17,20 +17,34 @@ package org.breedinginsight.services.validators; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializer; import io.kowalski.fannypack.FannyPack; +import io.micronaut.http.client.RxHttpClient; +import io.micronaut.http.client.annotation.Client; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import lombok.SneakyThrows; -import org.breedinginsight.DatabaseTest; +import org.breedinginsight.BrAPITest; +import org.breedinginsight.TestUtils; +import org.breedinginsight.api.auth.AuthenticatedUser; +import org.breedinginsight.api.model.v1.request.ProgramRequest; +import org.breedinginsight.api.model.v1.request.SpeciesRequest; +import org.breedinginsight.api.v1.controller.TestTokenValidator; +import org.breedinginsight.dao.db.enums.DataType; import org.breedinginsight.dao.db.tables.daos.ProgramDao; import org.breedinginsight.dao.db.tables.pojos.ProgramEntity; -import org.breedinginsight.model.Method; -import org.breedinginsight.model.ProgramObservationLevel; -import org.breedinginsight.model.Scale; -import org.breedinginsight.model.Trait; +import org.breedinginsight.dao.db.tables.pojos.SpeciesEntity; +import org.breedinginsight.daos.SpeciesDAO; +import org.breedinginsight.daos.UserDAO; +import org.breedinginsight.model.*; +import org.breedinginsight.services.TraitService; import org.jooq.DSLContext; import org.junit.jupiter.api.*; import javax.inject.Inject; +import java.time.OffsetDateTime; +import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -39,7 +53,7 @@ @MicronautTest @TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class TraitValidatorIntegrationTest extends DatabaseTest { +public class TraitValidatorIntegrationTest extends BrAPITest { private ProgramEntity validProgram; @@ -49,29 +63,76 @@ public class TraitValidatorIntegrationTest extends DatabaseTest { private ProgramDao programDao; @Inject private TraitValidatorService traitValidator; + @Inject + private TraitService traitService; + @Inject + private UserDAO userDAO; + @Inject + private SpeciesDAO speciesDAO; + @Inject + @Client("/${micronaut.bi.api.version}") + private RxHttpClient client; + + private final Gson gson = new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, (JsonDeserializer) + (json, type, context) -> OffsetDateTime.parse(json.getAsString())) + .create(); @BeforeAll - public void setup() { + public void setup() throws Exception { // Insert our traits into the db var fp = FannyPack.fill("src/test/resources/sql/TraitControllerIntegrationTest.sql"); - - // Insert program - dsl.execute(fp.get("InsertProgram")); + FannyPack securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); + FannyPack brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); + + // Test User + User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); + + // Species + super.getBrapiDsl().execute(brapiFp.get("InsertSpecies")); + SpeciesEntity validSpecies = speciesDAO.findAll().get(0); + SpeciesRequest speciesRequest = SpeciesRequest.builder() + .commonName(validSpecies.getCommonName()) + .id(validSpecies.getId()) + .build(); + + // Test Program + ProgramRequest programRequest = ProgramRequest.builder() + .name("Test Program") + .abbreviation("test") + .documentationUrl("localhost:8080") + .objective("To test things") + .species(speciesRequest) + .key("TEST") + .build(); + validProgram = TestUtils.insertAndFetchTestProgram(gson, client, programRequest); + + dsl.execute(securityFp.get("InsertProgramRolesBreeder"), testUser.getId().toString(), validProgram.getId()); + AuthenticatedUser user = new AuthenticatedUser(testUser.getName(), new ArrayList<>(), testUser.getId(), new ArrayList<>()); // Insert program observation level dsl.execute(fp.get("InsertProgramObservationLevel")); - // Insert program ontology sql - dsl.execute(fp.get("InsertProgramOntology")); + Trait trait = Trait.builder() + .observationVariableName("Test Trait") + .fullName("Test Trait") + .traitDescription("test") + .entity("test") + .attribute("test") + .programObservationLevel(ProgramObservationLevel.builder().name("Plot").build()) + .method(Method.builder() + .description("test method") + .methodClass("test method") + .build()) + .scale(Scale.builder() + .scaleName("Test Scale") + .dataType(DataType.TEXT) + .build()) + .build(); // Insert Trait - dsl.execute(fp.get("InsertMethod")); - dsl.execute(fp.get("InsertScale")); - dsl.execute(fp.get("InsertTrait")); - - // Retrieve our new data - validProgram = programDao.findAll().get(0); + traitService.createTraits(validProgram.getId(), new ArrayList<>(List.of(trait)), user, false); } @Test From 2f3309fb2f5db324de73d81ac3e9c85dc7942032 Mon Sep 17 00:00:00 2001 From: timparsons Date: Thu, 30 Nov 2023 11:36:11 -0500 Subject: [PATCH 10/20] [BI-1921] adding more unit tests --- .../BrAPIObservationVariableController.java | 1 + .../brapi/v2/BrAPIObservationsController.java | 90 ++-- ...onVariablesControllerIntegrationTest.java} | 2 +- ...tionVariableControllerIntegrationTest.java | 22 - ...ObservationsControllerIntegrationTest.java | 397 ++++++++++++++++- ...tionVariableControllerIntegrationTest.java | 420 ++++++++++++++++++ 6 files changed, 863 insertions(+), 69 deletions(-) rename src/test/java/org/breedinginsight/brapi/v1/controller/{BrapiObservationVariablesControllerIntegrationTest.java => BrapiV1ObservationVariablesControllerIntegrationTest.java} (99%) delete mode 100644 src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableControllerIntegrationTest.java create mode 100644 src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ObservationVariableControllerIntegrationTest.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java index d94ab0541..6a748978f 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java @@ -218,6 +218,7 @@ private BrAPIObservationVariable convertToBrAPI(Trait trait) { .synonyms(synonyms)) .method(new BrAPIMethod().ontologyReference(brAPIOntologyReference) .methodDbId(trait.getMethod().getId().toString()) + .methodClass(trait.getMethod().getMethodClass()) .description(trait.getMethod().getDescription()) .formula(trait.getMethod().getFormula())) .scale(new BrAPIScale().ontologyReference(brAPIOntologyReference) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java index 2f669255d..5a6c51148 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java @@ -28,6 +28,7 @@ import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; import org.breedinginsight.brapi.v1.controller.BrapiVersion; +import javax.annotation.Nullable; import java.util.Date; import java.util.List; import java.util.Map; @@ -41,30 +42,31 @@ public class BrAPIObservationsController { @Get("/observations") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) public HttpResponse observationsGet(@PathVariable("programId") UUID programId, - @QueryValue("observationDbId") String observationDbId, - @QueryValue("observationUnitDbId") String observationUnitDbId, - @QueryValue("observationVariableDbId") String observationVariableDbId, - @QueryValue("locationDbId") String locationDbId, - @QueryValue("seasonDbId") String seasonDbId, - @QueryValue("observationTimeStampRangeStart") Date observationTimeStampRangeStart, - @QueryValue("observationTimeStampRangeEnd") Date observationTimeStampRangeEnd, - @QueryValue("observationUnitLevelName") String observationUnitLevelName, - @QueryValue("observationUnitLevelOrder") String observationUnitLevelOrder, - @QueryValue("observationUnitLevelCode") String observationUnitLevelCode, - @QueryValue("observationUnitLevelRelationshipName") String observationUnitLevelRelationshipName, - @QueryValue("observationUnitLevelRelationshipOrder") String observationUnitLevelRelationshipOrder, - @QueryValue("observationUnitLevelRelationshipCode") String observationUnitLevelRelationshipCode, - @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId, - @QueryValue("commonCropName") String commonCropName, - @QueryValue("programDbId") String programDbId, - @QueryValue("trialDbId") String trialDbId, - @QueryValue("studyDbId") String studyDbId, - @QueryValue("germplasmDbId") String germplasmDbId, - @QueryValue("externalReferenceID") String externalReferenceID, - @QueryValue("externalReferenceId") String externalReferenceId, - @QueryValue("externalReferenceSource") String externalReferenceSource, - @QueryValue("page") Integer page, - @QueryValue("pageSize") Integer pageSize) { + @Nullable @QueryValue("observationDbId") String observationDbId, + @Nullable @QueryValue("observationUnitDbId") String observationUnitDbId, + @Nullable @QueryValue("observationVariableDbId") String observationVariableDbId, + @Nullable @QueryValue("locationDbId") String locationDbId, + @Nullable @QueryValue("seasonDbId") String seasonDbId, + @Nullable @QueryValue("observationTimeStampRangeStart") Date observationTimeStampRangeStart, + @Nullable @QueryValue("observationTimeStampRangeEnd") Date observationTimeStampRangeEnd, + @Nullable @QueryValue("observationUnitLevelName") String observationUnitLevelName, + @Nullable @QueryValue("observationUnitLevelOrder") String observationUnitLevelOrder, + @Nullable @QueryValue("observationUnitLevelCode") String observationUnitLevelCode, + @Nullable @QueryValue("observationUnitLevelRelationshipName") String observationUnitLevelRelationshipName, + @Nullable @QueryValue("observationUnitLevelRelationshipOrder") String observationUnitLevelRelationshipOrder, + @Nullable @QueryValue("observationUnitLevelRelationshipCode") String observationUnitLevelRelationshipCode, + @Nullable @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId, + @Nullable @QueryValue("commonCropName") String commonCropName, + @Nullable @QueryValue("programDbId") String programDbId, + @Nullable @QueryValue("trialDbId") String trialDbId, + @Nullable @QueryValue("studyDbId") String studyDbId, + @Nullable @QueryValue("germplasmDbId") String germplasmDbId, + @Nullable @QueryValue("externalReferenceID") String externalReferenceID, + @Nullable @QueryValue("externalReferenceId") String externalReferenceId, + @Nullable @QueryValue("externalReferenceSource") String externalReferenceSource, + @Nullable @QueryValue("page") Integer page, + @Nullable @QueryValue("pageSize") Integer pageSize) { + //TODO return HttpResponse.notFound(); } @@ -111,26 +113,26 @@ public HttpResponse observationsPut(@PathVariable("programId") UUID programId, @ @Produces({"application/json", "text/csv", "text/tsv"}) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.ALL}) public HttpResponse observationsTableGet(@PathVariable("programId") UUID programId, - @Header("Accept") String accept, - @QueryValue("observationUnitDbId") String observationUnitDbId, - @QueryValue("observationVariableDbId") String observationVariableDbId, - @QueryValue("locationDbId") String locationDbId, - @QueryValue("seasonDbId") String seasonDbId, - @QueryValue("observationLevel") String observationLevel, - @QueryValue("searchResultsDbId") String searchResultsDbId, - @QueryValue("observationTimeStampRangeStart") Date observationTimeStampRangeStart, - @QueryValue("observationTimeStampRangeEnd") Date observationTimeStampRangeEnd, - @QueryValue("programDbId") String programDbId, - @QueryValue("trialDbId") String trialDbId, - @QueryValue("studyDbId") String studyDbId, - @QueryValue("germplasmDbId") String germplasmDbId, - @QueryValue("observationUnitLevelName") String observationUnitLevelName, - @QueryValue("observationUnitLevelOrder") String observationUnitLevelOrder, - @QueryValue("observationUnitLevelCode") String observationUnitLevelCode, - @QueryValue("observationUnitLevelRelationshipName") String observationUnitLevelRelationshipName, - @QueryValue("observationUnitLevelRelationshipOrder") String observationUnitLevelRelationshipOrder, - @QueryValue("observationUnitLevelRelationshipCode") String observationUnitLevelRelationshipCode, - @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId) { + @Nullable @Header("Accept") String accept, + @Nullable @QueryValue("observationUnitDbId") String observationUnitDbId, + @Nullable @QueryValue("observationVariableDbId") String observationVariableDbId, + @Nullable @QueryValue("locationDbId") String locationDbId, + @Nullable @QueryValue("seasonDbId") String seasonDbId, + @Nullable @QueryValue("observationLevel") String observationLevel, + @Nullable @QueryValue("searchResultsDbId") String searchResultsDbId, + @Nullable @QueryValue("observationTimeStampRangeStart") Date observationTimeStampRangeStart, + @Nullable @QueryValue("observationTimeStampRangeEnd") Date observationTimeStampRangeEnd, + @Nullable @QueryValue("programDbId") String programDbId, + @Nullable @QueryValue("trialDbId") String trialDbId, + @Nullable @QueryValue("studyDbId") String studyDbId, + @Nullable @QueryValue("germplasmDbId") String germplasmDbId, + @Nullable @QueryValue("observationUnitLevelName") String observationUnitLevelName, + @Nullable @QueryValue("observationUnitLevelOrder") String observationUnitLevelOrder, + @Nullable @QueryValue("observationUnitLevelCode") String observationUnitLevelCode, + @Nullable @QueryValue("observationUnitLevelRelationshipName") String observationUnitLevelRelationshipName, + @Nullable @QueryValue("observationUnitLevelRelationshipOrder") String observationUnitLevelRelationshipOrder, + @Nullable @QueryValue("observationUnitLevelRelationshipCode") String observationUnitLevelRelationshipCode, + @Nullable @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId) { return HttpResponse.notFound(); } } diff --git a/src/test/java/org/breedinginsight/brapi/v1/controller/BrapiObservationVariablesControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v1/controller/BrapiV1ObservationVariablesControllerIntegrationTest.java similarity index 99% rename from src/test/java/org/breedinginsight/brapi/v1/controller/BrapiObservationVariablesControllerIntegrationTest.java rename to src/test/java/org/breedinginsight/brapi/v1/controller/BrapiV1ObservationVariablesControllerIntegrationTest.java index 4da980a4e..300fcd928 100644 --- a/src/test/java/org/breedinginsight/brapi/v1/controller/BrapiObservationVariablesControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v1/controller/BrapiV1ObservationVariablesControllerIntegrationTest.java @@ -59,7 +59,7 @@ @MicronautTest @TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class BrapiObservationVariablesControllerIntegrationTest extends BrAPITest { +public class BrapiV1ObservationVariablesControllerIntegrationTest extends BrAPITest { @Inject @Client(BrapiVersion.BRAPI_V1) diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableControllerIntegrationTest.java deleted file mode 100644 index 1596f26e7..000000000 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableControllerIntegrationTest.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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; - -public class BrAPIObservationVariableControllerIntegrationTest { - //TODO -} diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java index cae4497e5..84175a6d0 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java @@ -17,6 +17,399 @@ package org.breedinginsight.brapi.v2; -public class BrAPIObservationsControllerIntegrationTest { - //TODO +import com.google.gson.*; +import io.kowalski.fannypack.FannyPack; +import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +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; +import lombok.SneakyThrows; +import org.brapi.v2.model.BrAPIExternalReference; +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.response.BrAPIObservationUnitSingleResponse; +import org.breedinginsight.BrAPITest; +import org.breedinginsight.TestUtils; +import org.breedinginsight.api.auth.AuthenticatedUser; +import org.breedinginsight.api.model.v1.request.ProgramRequest; +import org.breedinginsight.api.model.v1.request.SpeciesRequest; +import org.breedinginsight.api.v1.controller.TestTokenValidator; +import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; +import org.breedinginsight.brapps.importer.ImportTestUtils; +import org.breedinginsight.brapps.importer.model.imports.experimentObservation.ExperimentObservation; +import org.breedinginsight.dao.db.enums.DataType; +import org.breedinginsight.dao.db.tables.pojos.SpeciesEntity; +import org.breedinginsight.daos.SpeciesDAO; +import org.breedinginsight.daos.UserDAO; +import org.breedinginsight.model.*; +import org.breedinginsight.services.OntologyService; +import org.breedinginsight.services.exceptions.ValidatorException; +import org.breedinginsight.services.parsers.experiment.ExperimentFileColumns; +import org.breedinginsight.services.writers.CSVWriter; +import org.jooq.DSLContext; +import org.junit.jupiter.api.*; + +import javax.inject.Inject; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +import static io.micronaut.http.HttpRequest.*; +import static org.junit.jupiter.api.Assertions.*; + +@MicronautTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class BrAPIObservationsControllerIntegrationTest extends BrAPITest { + + private Program program; + private String experimentId; + private List envIds = new ArrayList<>(); + private final List> rows = new ArrayList<>(); + private final List columns = ExperimentFileColumns.getOrderedColumns(); + private List traits; + + @Property(name = "brapi.server.reference-source") + private String BRAPI_REFERENCE_SOURCE; + @Inject + private DSLContext dsl; + @Inject + private UserDAO userDAO; + @Inject + private SpeciesDAO speciesDAO; + @Inject + private OntologyService ontologyService; + @Inject + private BrAPIGermplasmDAO germplasmDAO; + + @Inject + @Client("/${micronaut.bi.api.version}") + private RxHttpClient client; + + private final Gson gson = new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, (JsonDeserializer) + (json, type, context) -> OffsetDateTime.parse(json.getAsString())) + .create(); + + @BeforeAll + void setup() throws Exception { + FannyPack fp = FannyPack.fill("src/test/resources/sql/ImportControllerIntegrationTest.sql"); + ImportTestUtils importTestUtils = new ImportTestUtils(); + FannyPack securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); + FannyPack brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); + + // Test User + User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); + + // Species + super.getBrapiDsl().execute(brapiFp.get("InsertSpecies")); + SpeciesEntity validSpecies = speciesDAO.findAll().get(0); + SpeciesRequest speciesRequest = SpeciesRequest.builder() + .commonName(validSpecies.getCommonName()) + .id(validSpecies.getId()) + .build(); + + // Test Program + ProgramRequest programRequest = ProgramRequest.builder() + .name("Test Program") + .abbreviation("Test") + .documentationUrl("localhost:8080") + .objective("To test things") + .species(speciesRequest) + .key("TEST") + .build(); + program = TestUtils.insertAndFetchTestProgram(gson, client, programRequest); + + dsl.execute(securityFp.get("InsertProgramRolesBreeder"), testUser.getId().toString(), program.getId()); + dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); + + // Get experiment import map + Flowable> call = client.exchange( + GET("/import/mappings?importName=ExperimentsTemplateMap") + .contentType(MediaType.APPLICATION_JSON) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + HttpResponse response = call.blockingFirst(); + String mappingId = JsonParser.parseString(Objects.requireNonNull(response.body())).getAsJsonObject() + .getAsJsonObject("result") + .getAsJsonArray("data") + .get(0).getAsJsonObject().get("id").getAsString(); + + // Add traits to program + traits = createTraits(2); + AuthenticatedUser user = new AuthenticatedUser(testUser.getName(), new ArrayList<>(), testUser.getId(), new ArrayList<>()); + try { + ontologyService.createTraits(program.getId(), traits, user, false); + } catch (ValidatorException e) { + System.err.println(e.getErrors()); + throw e; + } + + // Add germplasm to program + List germplasm = createGermplasm(1); + BrAPIExternalReference newReference = new BrAPIExternalReference(); + newReference.setReferenceSource(String.format("%s/programs", BRAPI_REFERENCE_SOURCE)); + newReference.setReferenceID(program.getId().toString()); + + germplasm.forEach(germ -> germ.getExternalReferences().add(newReference)); + + germplasmDAO.createBrAPIGermplasm(germplasm, program.getId(), null); + + // Make test experiment import + Map row1 = makeExpImportRow("Env1"); + Map row2 = makeExpImportRow("Env2"); + + // Add test observation data + for (Trait trait : traits) { + Random random = new Random(); + + // TODO: test for sending obs data as double. + // A float is returned from the backend instead of double. there is a separate card to fix this. + // Double val1 = Math.random(); + + Float val1 = random.nextFloat(); + row1.put(trait.getObservationVariableName(), val1); + } + + rows.add(row1); + rows.add(row2); + + // Import test experiment, environments, and any observations + JsonObject importResult = importTestUtils.uploadAndFetch( + writeDataToFile(rows, traits), + null, + true, + client, + program, + mappingId); + experimentId = importResult + .get("preview").getAsJsonObject() + .get("rows").getAsJsonArray() + .get(0).getAsJsonObject() + .get("trial").getAsJsonObject() + .get("id").getAsString(); + // Add environmentIds. + envIds.add(getEnvId(importResult, 0)); + envIds.add(getEnvId(importResult, 1)); + } + + @Test + @SneakyThrows + public void testPostObsNotFound() { + BrAPIObservation obs = new BrAPIObservation().observationUnitName("test"); + + Flowable> postCall = client.exchange( + POST(String.format("/programs/%s/brapi/v2/observations", + program.getId().toString()), List.of(obs)) + .contentType(MediaType.APPLICATION_JSON) + .bearerAuth("test-registered-user"), String.class + ); + + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse response = postCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } + + @Test + @SneakyThrows + public void testPutSingleObsNotFound() { + BrAPIObservation obs = new BrAPIObservation().observationDbId(UUID.randomUUID().toString()).germplasmName("updated"); + + Flowable> postCall = client.exchange( + PUT(String.format("/programs/%s/brapi/v2/observations/%s", + program.getId().toString(), obs.getObservationUnitDbId()), obs) + .contentType(MediaType.APPLICATION_JSON) + .bearerAuth("test-registered-user"), String.class + ); + + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse errorResponse = postCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } + + @Test + @SneakyThrows + public void testPutMultipleObsNotFound() { + BrAPIObservation obs = new BrAPIObservation().observationDbId(UUID.randomUUID().toString()).germplasmName("updated"); + + Flowable> postCall = client.exchange( + PUT(String.format("/programs/%s/brapi/v2/observations", + program.getId().toString()), Map.of(obs.getObservationDbId(), obs)) + .contentType(MediaType.APPLICATION_JSON) + .bearerAuth("test-registered-user"), String.class + ); + + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse errorResponse = postCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } + + @Test + @SneakyThrows + public void testGetObsTableNotFound() { + Flowable> getCall = client.exchange( + GET(String.format("/programs/%s/brapi/v2/observations/table", + program.getId().toString())) + .contentType(MediaType.APPLICATION_JSON) + .bearerAuth("test-registered-user"), String.class + ); + + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse response = getCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } + + @Test + @Disabled("Disabled until fetching of observations is implemented") + public void testGetObsListByExpId() { + Flowable> call = client.exchange( + GET(String.format("/programs/%s/brapi/v2/observations?trialDbId=%s", program.getId(), experimentId)) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse response = call.blockingFirst(); + assertEquals(HttpStatus.OK, response.getStatus()); + } + + @Test + @Disabled("Disabled until fetching of observations is implemented") + public void testGetOUById() { + Flowable> call = client.exchange( + GET(String.format("/programs/%s/brapi/v2/observations?trialDbId=%s", program.getId(), experimentId)) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse response = call.blockingFirst(); + assertEquals(HttpStatus.OK, response.getStatus()); + + JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); + JsonArray observations = responseObj.getAsJsonObject("result").getAsJsonArray("data"); + JsonObject obs = observations.get(0).getAsJsonObject(); + + Flowable> ouCall = client.exchange( + GET(String.format("/programs/%s/brapi/v2/observations/%s", program.getId(), obs.get("observationDbId").getAsString())) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse ouResponse = ouCall.blockingFirst(); + assertEquals(HttpStatus.OK, ouResponse.getStatus()); + } + + private File writeDataToFile(List> data, List traits) throws IOException { + File file = File.createTempFile("test", ".csv"); + + if(traits != null) { + traits.forEach(trait -> columns.add( + Column.builder() + .value(trait.getObservationVariableName()) + .dataType(Column.ColumnDataType.STRING) + .build()) + ); + } + ByteArrayOutputStream byteArrayOutputStream = CSVWriter.writeToCSV(columns, data); + FileOutputStream fos = new FileOutputStream(file); + fos.write(byteArrayOutputStream.toByteArray()); + + return file; + } + + private String getEnvId(JsonObject result, int index) { + return result + .get("preview").getAsJsonObject() + .get("rows").getAsJsonArray() + .get(index).getAsJsonObject() + .get("study").getAsJsonObject() + .get("brAPIObject").getAsJsonObject() + .get("externalReferences").getAsJsonArray() + .get(2).getAsJsonObject() + .get("referenceID").getAsString(); + } + + private List createGermplasm(int numToCreate) { + List germplasm = new ArrayList<>(); + for (int i = 0; i < numToCreate; i++) { + String gid = ""+(i+1); + BrAPIGermplasm testGermplasm = new BrAPIGermplasm(); + testGermplasm.setGermplasmName(String.format("Germplasm %s [TEST-%s]", gid, gid)); + testGermplasm.setSeedSource("Wild"); + testGermplasm.setAccessionNumber(gid); + testGermplasm.setDefaultDisplayName(String.format("Germplasm %s", gid)); + JsonObject additionalInfo = new JsonObject(); + additionalInfo.addProperty("importEntryNumber", gid); + additionalInfo.addProperty("breedingMethod", "Allopolyploid"); + testGermplasm.setAdditionalInfo(additionalInfo); + List externalRef = new ArrayList<>(); + BrAPIExternalReference testReference = new BrAPIExternalReference(); + testReference.setReferenceSource(BRAPI_REFERENCE_SOURCE); + testReference.setReferenceID(UUID.randomUUID().toString()); + externalRef.add(testReference); + testGermplasm.setExternalReferences(externalRef); + germplasm.add(testGermplasm); + } + + return germplasm; + } + + private List createTraits(int numToCreate) { + List traits = new ArrayList<>(); + for (int i = 0; i < numToCreate; i++) { + String varName = "tt_test_" + (i + 1); + traits.add(Trait.builder() + .observationVariableName(varName) + .fullName(varName) + .entity("Plant " + i) + .attribute("height " + i) + .traitDescription("test") + .programObservationLevel(ProgramObservationLevel.builder().name("Plot").build()) + .scale(Scale.builder() + .scaleName("test scale") + .dataType(DataType.NUMERICAL) + .validValueMin(0) + .validValueMax(100) + .build()) + .method(Method.builder() + .description("test method") + .methodClass("test method") + .build()) + .build()); + } + + return traits; + } + + private Map makeExpImportRow(String environment) { + Map row = new HashMap<>(); + row.put(ExperimentObservation.Columns.GERMPLASM_GID, "1"); + row.put(ExperimentObservation.Columns.TEST_CHECK, "T"); + row.put(ExperimentObservation.Columns.EXP_TITLE, "Test Exp"); + row.put(ExperimentObservation.Columns.EXP_UNIT, "Plot"); + row.put(ExperimentObservation.Columns.EXP_TYPE, "Phenotyping"); + row.put(ExperimentObservation.Columns.ENV, environment); + row.put(ExperimentObservation.Columns.ENV_LOCATION, "Location A"); + row.put(ExperimentObservation.Columns.ENV_YEAR, "2023"); + row.put(ExperimentObservation.Columns.EXP_UNIT_ID, environment+" - a-1"); + row.put(ExperimentObservation.Columns.REP_NUM, "1"); + row.put(ExperimentObservation.Columns.BLOCK_NUM, "1"); + row.put(ExperimentObservation.Columns.ROW, "1"); + row.put(ExperimentObservation.Columns.COLUMN, "1"); + return row; + } } diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ObservationVariableControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ObservationVariableControllerIntegrationTest.java new file mode 100644 index 000000000..0058d91f3 --- /dev/null +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ObservationVariableControllerIntegrationTest.java @@ -0,0 +1,420 @@ +/* + * 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 com.google.gson.*; +import io.kowalski.fannypack.FannyPack; +import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +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; +import lombok.SneakyThrows; +import org.brapi.v2.model.BrAPIExternalReference; +import org.brapi.v2.model.germ.BrAPIGermplasm; +import org.brapi.v2.model.pheno.BrAPIObservationVariable; +import org.breedinginsight.BrAPITest; +import org.breedinginsight.TestUtils; +import org.breedinginsight.api.auth.AuthenticatedUser; +import org.breedinginsight.api.model.v1.request.ProgramRequest; +import org.breedinginsight.api.model.v1.request.SpeciesRequest; +import org.breedinginsight.api.v1.controller.TestTokenValidator; +import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; +import org.breedinginsight.brapps.importer.ImportTestUtils; +import org.breedinginsight.brapps.importer.model.imports.experimentObservation.ExperimentObservation; +import org.breedinginsight.dao.db.enums.DataType; +import org.breedinginsight.dao.db.tables.pojos.SpeciesEntity; +import org.breedinginsight.daos.SpeciesDAO; +import org.breedinginsight.daos.UserDAO; +import org.breedinginsight.model.*; +import org.breedinginsight.services.OntologyService; +import org.breedinginsight.services.exceptions.ValidatorException; +import org.breedinginsight.services.parsers.experiment.ExperimentFileColumns; +import org.breedinginsight.services.writers.CSVWriter; +import org.jooq.DSLContext; +import org.junit.jupiter.api.*; + +import javax.inject.Inject; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.*; + +import static io.micronaut.http.HttpRequest.*; +import static io.micronaut.http.HttpRequest.GET; +import static org.junit.jupiter.api.Assertions.*; + +@MicronautTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class BrAPIV2ObservationVariableControllerIntegrationTest extends BrAPITest { + private Program program; + private String experimentId; + private List envIds = new ArrayList<>(); + private final List> rows = new ArrayList<>(); + private final List columns = ExperimentFileColumns.getOrderedColumns(); + private List traits; + + @Property(name = "brapi.server.reference-source") + private String BRAPI_REFERENCE_SOURCE; + @Inject + private DSLContext dsl; + @Inject + private UserDAO userDAO; + @Inject + private SpeciesDAO speciesDAO; + @Inject + private OntologyService ontologyService; + @Inject + private BrAPIGermplasmDAO germplasmDAO; + + @Inject + @Client("/${micronaut.bi.api.version}") + private RxHttpClient client; + + private final Gson gson = new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, (JsonDeserializer) + (json, type, context) -> OffsetDateTime.parse(json.getAsString())) + .create(); + + @BeforeAll + void setup() throws Exception { + FannyPack fp = FannyPack.fill("src/test/resources/sql/ImportControllerIntegrationTest.sql"); + ImportTestUtils importTestUtils = new ImportTestUtils(); + FannyPack securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); + FannyPack brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); + + // Test User + User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); + + // Species + super.getBrapiDsl().execute(brapiFp.get("InsertSpecies")); + SpeciesEntity validSpecies = speciesDAO.findAll().get(0); + SpeciesRequest speciesRequest = SpeciesRequest.builder() + .commonName(validSpecies.getCommonName()) + .id(validSpecies.getId()) + .build(); + + // Test Program + ProgramRequest programRequest = ProgramRequest.builder() + .name("Test Program") + .abbreviation("Test") + .documentationUrl("localhost:8080") + .objective("To test things") + .species(speciesRequest) + .key("TEST") + .build(); + program = TestUtils.insertAndFetchTestProgram(gson, client, programRequest); + + dsl.execute(securityFp.get("InsertProgramRolesBreeder"), testUser.getId().toString(), program.getId()); + dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); + + // Get experiment import map + Flowable> call = client.exchange( + GET("/import/mappings?importName=ExperimentsTemplateMap") + .contentType(MediaType.APPLICATION_JSON) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + HttpResponse response = call.blockingFirst(); + String mappingId = JsonParser.parseString(Objects.requireNonNull(response.body())).getAsJsonObject() + .getAsJsonObject("result") + .getAsJsonArray("data") + .get(0).getAsJsonObject().get("id").getAsString(); + + // Add traits to program + traits = createTraits(4); + AuthenticatedUser user = new AuthenticatedUser(testUser.getName(), new ArrayList<>(), testUser.getId(), new ArrayList<>()); + try { + ontologyService.createTraits(program.getId(), traits, user, false); + } catch (ValidatorException e) { + System.err.println(e.getErrors()); + throw e; + } + + // Add germplasm to program + List germplasm = createGermplasm(1); + BrAPIExternalReference newReference = new BrAPIExternalReference(); + newReference.setReferenceSource(String.format("%s/programs", BRAPI_REFERENCE_SOURCE)); + newReference.setReferenceID(program.getId().toString()); + + germplasm.forEach(germ -> germ.getExternalReferences().add(newReference)); + + germplasmDAO.createBrAPIGermplasm(germplasm, program.getId(), null); + + // Make test experiment import + Map row1 = makeExpImportRow("Env1"); + Map row2 = makeExpImportRow("Env2"); + + // Add test observation data + List expTraits = List.of(traits.get(0), traits.get(1)); + for (int i = 0; i < 2; i++) { + var trait = expTraits.get(i); + Random random = new Random(); + + // TODO: test for sending obs data as double. + // A float is returned from the backend instead of double. there is a separate card to fix this. + // Double val1 = Math.random(); + + Float val1 = random.nextFloat(); + row1.put(trait.getObservationVariableName(), val1); + } + + rows.add(row1); + rows.add(row2); + + // Import test experiment, environments, and any observations + JsonObject importResult = importTestUtils.uploadAndFetch( + writeDataToFile(rows, expTraits), + null, + true, + client, + program, + mappingId); + experimentId = importResult + .get("preview").getAsJsonObject() + .get("rows").getAsJsonArray() + .get(0).getAsJsonObject() + .get("trial").getAsJsonObject() + .get("id").getAsString(); + // Add environmentIds. + envIds.add(getEnvId(importResult, 0)); + envIds.add(getEnvId(importResult, 1)); + } + + @Test + @SneakyThrows + public void testPostVariablesNotFound() { + BrAPIObservationVariable variable = new BrAPIObservationVariable().observationVariableName(traits.get(0).getObservationVariableName()+" post"); + + Flowable> postCall = client.exchange( + POST(String.format("/programs/%s/brapi/v2/variables", + program.getId().toString()), List.of(variable)) + .contentType(MediaType.APPLICATION_JSON) + .bearerAuth("test-registered-user"), String.class + ); + + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse response = postCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } + + @Test + @SneakyThrows + public void testPutVariablesNotFound() { + BrAPIObservationVariable variable = new BrAPIObservationVariable().observationVariableName(traits.get(0).getObservationVariableName()+" put"); + + Flowable> putCall = client.exchange( + PUT(String.format("/programs/%s/brapi/v2/variables/%s", + program.getId().toString(), traits.get(0).getId()), List.of(variable)) + .contentType(MediaType.APPLICATION_JSON) + .bearerAuth("test-registered-user"), String.class + ); + + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse response = putCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } + + @Test + public void testGetVariables() { + Flowable> call = client.exchange( + GET(String.format("/programs/%s/brapi/v2/variables", program.getId(), experimentId)) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse response = call.blockingFirst(); + assertEquals(HttpStatus.OK, response.getStatus()); + + JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); + JsonArray variables = responseObj.getAsJsonObject("result").getAsJsonArray("data"); + assertEquals(4, variables.size()); + List variableNames = new ArrayList<>(); + for(var variable : variables) { + variableNames.add(variable.getAsJsonObject().get("observationVariableName").getAsString()); + } + assertTrue(variableNames.contains("tt_test_1")); + assertTrue(variableNames.contains("tt_test_2")); + assertTrue(variableNames.contains("tt_test_3")); + assertTrue(variableNames.contains("tt_test_4")); + } + + @Test + public void testGetVariablesByExpId() { + Flowable> call = client.exchange( + GET(String.format("/programs/%s/brapi/v2/variables?trialDbId=%s", program.getId(), experimentId)) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse response = call.blockingFirst(); + assertEquals(HttpStatus.OK, response.getStatus()); + + JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); + JsonArray variables = responseObj.getAsJsonObject("result").getAsJsonArray("data"); + assertEquals(2, variables.size()); + List variableNames = new ArrayList<>(); + for(var variable : variables) { + variableNames.add(variable.getAsJsonObject().get("observationVariableName").getAsString()); + } + assertTrue(variableNames.contains("tt_test_1")); + assertTrue(variableNames.contains("tt_test_2")); + } + + @Test + public void testGetVariableById() { + Flowable> call = client.exchange( + GET(String.format("/programs/%s/brapi/v2/variables/%s", program.getId(), traits.get(0).getId())) + .bearerAuth("test-registered-user"), + String.class + ); + + HttpResponse response = call.blockingFirst(); + assertEquals(HttpStatus.OK, response.getStatus()); + + JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); + JsonObject variable = responseObj.getAsJsonObject("result"); + assertNotNull(variable); + assertEquals(traits.get(0).getId().toString(), variable.get("observationVariableDbId").getAsString()); + assertEquals(traits.get(0).getObservationVariableName(), variable.get("observationVariableName").getAsString()); + + JsonObject trait = variable.getAsJsonObject("trait"); + assertEquals(traits.get(0).getEntity(), trait.get("entity").getAsString()); + assertEquals(traits.get(0).getAttribute(), trait.get("attribute").getAsString()); + + JsonObject method = variable.getAsJsonObject("method"); + assertEquals(traits.get(0).getMethod().getMethodClass(), + method.get("methodClass").getAsString()); + assertEquals(traits.get(0).getMethod().getDescription(), method.get("description").getAsString()); + + JsonObject scale = variable.getAsJsonObject("scale"); + assertEquals(traits.get(0).getScale().getScaleName(), scale.get("scaleName").getAsString()); + assertEquals(traits.get(0).getScale().getDataType().getLiteral().toLowerCase(), scale.get("dataType").getAsString().toLowerCase()); + assertEquals(traits.get(0).getScale().getValidValueMin(), scale.getAsJsonObject("validValues").get("min").getAsInt()); + assertEquals(traits.get(0).getScale().getValidValueMax(), scale.getAsJsonObject("validValues").get("max").getAsInt()); + } + + private File writeDataToFile(List> data, List traits) throws IOException { + File file = File.createTempFile("test", ".csv"); + + if(traits != null) { + traits.forEach(trait -> columns.add( + Column.builder() + .value(trait.getObservationVariableName()) + .dataType(Column.ColumnDataType.STRING) + .build()) + ); + } + ByteArrayOutputStream byteArrayOutputStream = CSVWriter.writeToCSV(columns, data); + FileOutputStream fos = new FileOutputStream(file); + fos.write(byteArrayOutputStream.toByteArray()); + + return file; + } + + private String getEnvId(JsonObject result, int index) { + return result + .get("preview").getAsJsonObject() + .get("rows").getAsJsonArray() + .get(index).getAsJsonObject() + .get("study").getAsJsonObject() + .get("brAPIObject").getAsJsonObject() + .get("externalReferences").getAsJsonArray() + .get(2).getAsJsonObject() + .get("referenceID").getAsString(); + } + + private List createGermplasm(int numToCreate) { + List germplasm = new ArrayList<>(); + for (int i = 0; i < numToCreate; i++) { + String gid = ""+(i+1); + BrAPIGermplasm testGermplasm = new BrAPIGermplasm(); + testGermplasm.setGermplasmName(String.format("Germplasm %s [TEST-%s]", gid, gid)); + testGermplasm.setSeedSource("Wild"); + testGermplasm.setAccessionNumber(gid); + testGermplasm.setDefaultDisplayName(String.format("Germplasm %s", gid)); + JsonObject additionalInfo = new JsonObject(); + additionalInfo.addProperty("importEntryNumber", gid); + additionalInfo.addProperty("breedingMethod", "Allopolyploid"); + testGermplasm.setAdditionalInfo(additionalInfo); + List externalRef = new ArrayList<>(); + BrAPIExternalReference testReference = new BrAPIExternalReference(); + testReference.setReferenceSource(BRAPI_REFERENCE_SOURCE); + testReference.setReferenceID(UUID.randomUUID().toString()); + externalRef.add(testReference); + testGermplasm.setExternalReferences(externalRef); + germplasm.add(testGermplasm); + } + + return germplasm; + } + + private List createTraits(int numToCreate) { + List traits = new ArrayList<>(); + for (int i = 0; i < numToCreate; i++) { + String varName = "tt_test_" + (i + 1); + traits.add(Trait.builder() + .observationVariableName(varName) + .fullName(varName) + .entity("Plant " + i) + .attribute("height " + i) + .traitDescription("test") + .programObservationLevel(ProgramObservationLevel.builder().name("Plot").build()) + .scale(Scale.builder() + .scaleName("test scale") + .dataType(DataType.NUMERICAL) + .validValueMin(0) + .validValueMax(100) + .build()) + .method(Method.builder() + .description("test method") + .methodClass("test method") + .build()) + .build()); + } + + return traits; + } + + private Map makeExpImportRow(String environment) { + Map row = new HashMap<>(); + row.put(ExperimentObservation.Columns.GERMPLASM_GID, "1"); + row.put(ExperimentObservation.Columns.TEST_CHECK, "T"); + row.put(ExperimentObservation.Columns.EXP_TITLE, "Test Exp"); + row.put(ExperimentObservation.Columns.EXP_UNIT, "Plot"); + row.put(ExperimentObservation.Columns.EXP_TYPE, "Phenotyping"); + row.put(ExperimentObservation.Columns.ENV, environment); + row.put(ExperimentObservation.Columns.ENV_LOCATION, "Location A"); + row.put(ExperimentObservation.Columns.ENV_YEAR, "2023"); + row.put(ExperimentObservation.Columns.EXP_UNIT_ID, "a-1"); + row.put(ExperimentObservation.Columns.REP_NUM, "1"); + row.put(ExperimentObservation.Columns.BLOCK_NUM, "1"); + row.put(ExperimentObservation.Columns.ROW, "1"); + row.put(ExperimentObservation.Columns.COLUMN, "1"); + return row; + } +} From 2769cf35547e8d1a943787132918447b7062ec97 Mon Sep 17 00:00:00 2001 From: timparsons Date: Thu, 30 Nov 2023 11:39:23 -0500 Subject: [PATCH 11/20] [BI-1921] cleaning up imports --- .../breedinginsight/brapi/v2/BrAPITrialsController.java | 6 +++--- .../org/breedinginsight/brapi/v2/BrAPIV2Controller.java | 3 ++- .../brapi/v2/dao/BrAPIObservationUnitDAO.java | 4 ++-- .../v2/BrAPIObservationUnitControllerIntegrationTest.java | 1 - .../v2/BrAPIObservationsControllerIntegrationTest.java | 5 +---- .../brapi/v2/BrAPIStudiesControllerIntegrationTest.java | 1 - .../brapi/v2/BrAPIV2ControllerIntegrationTest.java | 8 ++------ ...APIV2ObservationVariableControllerIntegrationTest.java | 1 - .../brapi/v2/GermplasmControllerIntegrationTest.java | 5 ++--- 9 files changed, 12 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java index 3966b1341..666115efa 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPITrialsController.java @@ -9,7 +9,6 @@ import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.v2.model.core.BrAPIStudy; import org.brapi.v2.model.core.BrAPITrial; import org.brapi.v2.model.core.response.BrAPITrialSingleResponse; import org.breedinginsight.api.auth.ProgramSecured; @@ -22,14 +21,15 @@ import org.breedinginsight.brapi.v2.model.request.query.ExperimentQuery; import org.breedinginsight.brapi.v2.services.BrAPITrialService; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; -import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.Utilities; import org.breedinginsight.utilities.response.ResponseUtils; import org.breedinginsight.utilities.response.mappers.ExperimentQueryMapper; + import javax.inject.Inject; import javax.validation.Valid; -import java.util.*; +import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; @Slf4j diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java index 3a485bd9a..534efbe47 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java @@ -41,7 +41,8 @@ import javax.inject.Inject; import java.io.IOException; -import java.util.*; +import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; @Slf4j diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java index ac153d893..40833dc3e 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java @@ -22,18 +22,18 @@ import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import io.micronaut.context.annotation.Property; +import io.micronaut.http.server.exceptions.InternalServerException; import org.apache.commons.lang3.StringUtils; import org.brapi.client.v2.JSON; -import io.micronaut.http.server.exceptions.InternalServerException; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.modules.phenotype.ObservationUnitsApi; import org.brapi.v2.model.germ.BrAPIGermplasm; +import org.brapi.v2.model.pheno.BrAPIObservationTreatment; import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.brapi.v2.model.pheno.BrAPIObservationUnitLevelRelationship; import org.brapi.v2.model.pheno.request.BrAPIObservationUnitSearchRequest; import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; -import org.brapi.v2.model.pheno.BrAPIObservationTreatment; import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitControllerIntegrationTest.java index 4111e58ed..a45dbb6f6 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitControllerIntegrationTest.java @@ -65,7 +65,6 @@ import java.util.concurrent.atomic.AtomicReference; import static io.micronaut.http.HttpRequest.*; -import static io.micronaut.http.HttpRequest.GET; import static org.junit.jupiter.api.Assertions.*; @MicronautTest diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java index 84175a6d0..4a0a78526 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java @@ -33,8 +33,6 @@ import org.brapi.v2.model.BrAPIExternalReference; 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.response.BrAPIObservationUnitSingleResponse; import org.breedinginsight.BrAPITest; import org.breedinginsight.TestUtils; import org.breedinginsight.api.auth.AuthenticatedUser; @@ -63,10 +61,9 @@ import java.io.IOException; import java.time.OffsetDateTime; import java.util.*; -import java.util.concurrent.atomic.AtomicReference; import static io.micronaut.http.HttpRequest.*; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; @MicronautTest @TestInstance(TestInstance.Lifecycle.PER_CLASS) diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIStudiesControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIStudiesControllerIntegrationTest.java index 1373d9ab3..955c6e619 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIStudiesControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIStudiesControllerIntegrationTest.java @@ -66,7 +66,6 @@ import java.util.concurrent.atomic.AtomicReference; import static io.micronaut.http.HttpRequest.*; -import static io.micronaut.http.HttpRequest.GET; import static org.junit.jupiter.api.Assertions.*; @MicronautTest diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java index 0ef73d284..ee5fbbfd7 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java @@ -33,12 +33,7 @@ import org.brapi.v2.model.BrAPIExternalReference; import org.brapi.v2.model.BrAPIPagination; import org.brapi.v2.model.core.BrAPIServerInfo; -import org.brapi.v2.model.core.BrAPIStudy; -import org.brapi.v2.model.core.response.BrAPIStudyListResponse; -import org.brapi.v2.model.core.response.BrAPIStudySingleResponse; import org.brapi.v2.model.pheno.*; -import org.brapi.v2.model.pheno.response.BrAPIObservationVariableListResponse; -import org.brapi.v2.model.pheno.response.BrAPIObservationVariableSingleResponse; import org.breedinginsight.BrAPITest; import org.breedinginsight.api.v1.controller.TestTokenValidator; import org.breedinginsight.dao.db.tables.daos.ProgramDao; @@ -54,7 +49,8 @@ import java.util.Collections; import java.util.UUID; -import static io.micronaut.http.HttpRequest.*; +import static io.micronaut.http.HttpRequest.GET; +import static io.micronaut.http.HttpRequest.POST; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ObservationVariableControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ObservationVariableControllerIntegrationTest.java index 0058d91f3..d54aaafc1 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ObservationVariableControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ObservationVariableControllerIntegrationTest.java @@ -63,7 +63,6 @@ import java.util.*; import static io.micronaut.http.HttpRequest.*; -import static io.micronaut.http.HttpRequest.GET; import static org.junit.jupiter.api.Assertions.*; @MicronautTest diff --git a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java index fb95e4dd2..4fdf0cce3 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java @@ -18,19 +18,18 @@ import org.breedinginsight.api.model.v1.request.query.FilterRequest; import org.breedinginsight.api.model.v1.request.query.SearchRequest; import org.breedinginsight.api.v1.controller.TestTokenValidator; -import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; -import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; +import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; import org.breedinginsight.dao.db.tables.pojos.BiUserEntity; import org.breedinginsight.daos.UserDAO; import org.breedinginsight.model.Program; import org.breedinginsight.model.Species; import org.breedinginsight.services.SpeciesService; +import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; import org.jooq.DSLContext; import org.junit.jupiter.api.*; import javax.inject.Inject; - import java.io.File; import java.time.OffsetDateTime; import java.util.ArrayList; From 5968eca4ce78826e33050d228dcede8b099940c7 Mon Sep 17 00:00:00 2001 From: timparsons Date: Fri, 1 Dec 2023 09:33:05 -0500 Subject: [PATCH 12/20] [BI-1921] fixing compilation errors --- .../api/v1/controller/ExperimentController.java | 1 + .../services/processors/SampleSubmissionProcessor.java | 4 +--- .../brapps/importer/SampleSubmissionFileImportTest.java | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java b/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java index 7c64827fe..017197219 100644 --- a/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java @@ -23,6 +23,7 @@ import javax.inject.Inject; import javax.validation.Valid; +import java.util.Optional; import java.util.UUID; @Slf4j diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/SampleSubmissionProcessor.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/SampleSubmissionProcessor.java index d92ebb300..396bf8658 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/SampleSubmissionProcessor.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/SampleSubmissionProcessor.java @@ -31,9 +31,7 @@ import org.breedinginsight.api.model.v1.response.ValidationError; import org.breedinginsight.api.model.v1.response.ValidationErrors; import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; -import org.breedinginsight.brapps.importer.daos.BrAPIObservationUnitDAO; -import org.breedinginsight.brapps.importer.daos.BrAPIPlateDAO; -import org.breedinginsight.brapps.importer.daos.BrAPISampleDAO; +import org.breedinginsight.brapi.v2.dao.BrAPIObservationUnitDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.model.imports.BrAPIImport; import org.breedinginsight.brapps.importer.model.imports.PendingImport; diff --git a/src/test/java/org/breedinginsight/brapps/importer/SampleSubmissionFileImportTest.java b/src/test/java/org/breedinginsight/brapps/importer/SampleSubmissionFileImportTest.java index aa446cc55..8c5b308c3 100644 --- a/src/test/java/org/breedinginsight/brapps/importer/SampleSubmissionFileImportTest.java +++ b/src/test/java/org/breedinginsight/brapps/importer/SampleSubmissionFileImportTest.java @@ -43,7 +43,7 @@ import org.breedinginsight.api.model.v1.request.ProgramRequest; import org.breedinginsight.api.model.v1.request.SpeciesRequest; import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; -import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; +import org.breedinginsight.brapi.v2.dao.*; import org.breedinginsight.brapps.importer.daos.*; import org.breedinginsight.brapps.importer.model.imports.experimentObservation.ExperimentObservation; import org.breedinginsight.brapps.importer.model.imports.sample.SampleSubmissionImport.Columns; From 16e1f2b069791587f4bb0640a03841d0a54e33e5 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:18:35 -0500 Subject: [PATCH 13/20] prepend serverinfo path with biapi version number --- .../java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java index 534efbe47..1e0dfba83 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java @@ -60,7 +60,7 @@ public BrAPIV2Controller(SecurityService securityService, ProgramService program } - @Get(BrapiVersion.BRAPI_V2 + "/serverinfo") + @Get("/${micronaut.bi.api.version}/" + BrapiVersion.BRAPI_V2 + "/serverinfo") @Produces(MediaType.APPLICATION_JSON) @Secured(SecurityRule.IS_ANONYMOUS) public BrAPIServerInfoResponse serverinfo() { @@ -70,8 +70,7 @@ public BrAPIServerInfoResponse serverinfo() { serverInfo.setCalls( new ServiceBuilder().versions("2.0", "2.1") - .setBase("serverinfo").GET().build() - .setBase("programs").GET().addPath("{programDbId}").GET().build() + .setBase("serverinfo").GET().build() ); return new BrAPIServerInfoResponse().result(serverInfo); From 5ce6f19322341b64b1d2f26a98d87bbc99c880b2 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:27:45 -0500 Subject: [PATCH 14/20] remove redundant query variables --- .../brapi/v2/BrAPIObservationUnitController.java | 1 - .../brapi/v2/BrAPIObservationVariableController.java | 1 - .../java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java index 29ce0cef5..c649626b1 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java @@ -82,7 +82,6 @@ public HttpResponse observationunitsGet(@PathV @Nullable @QueryValue("observationUnitLevelRelationshipCode") String observationUnitLevelRelationshipCode, @Nullable @QueryValue("observationUnitLevelRelationshipDbId") String observationUnitLevelRelationshipDbId, @Nullable @QueryValue("commonCropName") String commonCropName, - @Nullable @QueryValue("programDbId") String programDbId, @Nullable @QueryValue("trialDbId") String trialDbId, @Nullable @QueryValue("studyDbId") String studyDbId, @Nullable @QueryValue("germplasmDbId") String germplasmDbId, diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java index 6a748978f..7ec204bf5 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java @@ -101,7 +101,6 @@ public HttpResponse variablesGet(@PathVari @Nullable @QueryValue("traitPUI") String traitPUI, @Nullable @QueryValue("ontologyDbId") String ontologyDbId, @Nullable @QueryValue("commonCropName") String commonCropName, -// @QueryValue("programDbId") Optional<@Nullable String> programDbId, //this is redundant @Nullable @QueryValue("trialDbId") String experimentId, @Nullable @QueryValue("studyDbId") String environmentId, @Nullable @QueryValue("externalReferenceID") String externalReferenceID, diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java index 1e0dfba83..0d4a5449d 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java @@ -70,7 +70,7 @@ public BrAPIServerInfoResponse serverinfo() { serverInfo.setCalls( new ServiceBuilder().versions("2.0", "2.1") - .setBase("serverinfo").GET().build() + .setBase("serverinfo").GET().build() ); return new BrAPIServerInfoResponse().result(serverInfo); From 9f9b1426951119bc5ed9714d92a2cbb85a67df9b Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:03:57 -0500 Subject: [PATCH 15/20] move helper methods to trait service --- .../BrAPIObservationVariableController.java | 195 +---------------- .../daos/impl/AbstractDAO.java | 2 +- .../services/TraitService.java | 198 +++++++++++++++++- 3 files changed, 205 insertions(+), 190 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java index 7ec204bf5..f7e9368f4 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java @@ -25,37 +25,30 @@ import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.BrAPIIndexPagination; import org.brapi.v2.model.BrAPIMetadata; -import org.brapi.v2.model.BrAPIOntologyReference; -import org.brapi.v2.model.core.BrAPIStudy; -import org.brapi.v2.model.core.BrAPITrial; -import org.brapi.v2.model.pheno.*; +import org.brapi.v2.model.pheno.BrAPIObservationVariable; import org.brapi.v2.model.pheno.response.BrAPIObservationVariableListResponse; import org.brapi.v2.model.pheno.response.BrAPIObservationVariableListResponseResult; import org.brapi.v2.model.pheno.response.BrAPIObservationVariableSingleResponse; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; import org.breedinginsight.brapi.v1.controller.BrapiVersion; -import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; import org.breedinginsight.brapi.v2.services.BrAPITrialService; -import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; -import org.breedinginsight.model.Program; import org.breedinginsight.model.Trait; import org.breedinginsight.services.OntologyService; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.TraitService; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.Utilities; -import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; import javax.inject.Inject; -import java.util.*; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; @Slf4j @Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @@ -115,14 +108,15 @@ public HttpResponse variablesGet(@PathVari log.debug("unsupported variable filters, returning"); programTraits = new ArrayList<>(); } else if(environmentId != null || experimentId != null) { - programTraits = getBrAPIObservationVariablesForExperiment(programId, Optional.ofNullable(experimentId), Optional.ofNullable(environmentId)); + programTraits = traitService.getBrAPIObservationVariablesForExperiment( + programId, Optional.ofNullable(experimentId), Optional.ofNullable(environmentId)); } else { log.debug("fetching variables for the program: " + programId); programTraits = ontologyService.getTraitsByProgramId(programId, true); } - List filteredObsVars = filterVariables(programTraits, + List filteredObsVars = traitService.filterVariables(programTraits, Optional.ofNullable(observationVariableDbId), Optional.ofNullable(observationVariableName), Optional.ofNullable(traitClass), @@ -174,7 +168,7 @@ public HttpResponse variablesObservation BrAPIObservationVariableSingleResponse response = new BrAPIObservationVariableSingleResponse() .metadata(new BrAPIMetadata()) - .result(convertToBrAPI(trait.get())); + .result(traitService.convertToBrAPI(trait.get())); return HttpResponse.ok(response); } catch (DoesNotExistException e) { @@ -198,175 +192,4 @@ public HttpResponse variablesPost(@PathVariable("programId") UUID programId, return HttpResponse.notFound(); } - private BrAPIObservationVariable convertToBrAPI(Trait trait) { - BrAPIOntologyReference brAPIOntologyReference = new BrAPIOntologyReference().ontologyDbId(trait.getProgramOntologyId() - .toString()); - String status = trait.getActive() ? "active" : "inactive"; - List synonyms = prepSynonyms(trait); - return new BrAPIObservationVariable().observationVariableDbId(trait.getId().toString()) - .observationVariableName(trait.getObservationVariableName()) - .defaultValue(trait.getDefaultValue()) - .status(status) - .synonyms(synonyms) - .trait(new BrAPITrait().ontologyReference(brAPIOntologyReference) - .traitName(trait.getObservationVariableName()) - .traitDbId(trait.getId().toString()) - .entity(trait.getEntity()) - .attribute(trait.getAttribute()) - .status(status) - .synonyms(synonyms)) - .method(new BrAPIMethod().ontologyReference(brAPIOntologyReference) - .methodDbId(trait.getMethod().getId().toString()) - .methodClass(trait.getMethod().getMethodClass()) - .description(trait.getMethod().getDescription()) - .formula(trait.getMethod().getFormula())) - .scale(new BrAPIScale().ontologyReference(brAPIOntologyReference) - .scaleDbId(trait.getScale().getId().toString()) - .scaleName(trait.getScale().getScaleName()) - .dataType(BrAPITraitDataType.fromValue(trait.getScale().getDataType().getLiteral())) - .decimalPlaces(trait.getScale().getDecimalPlaces()) - .validValues(new BrAPIScaleValidValues().max(trait.getScale().getValidValueMax()) - .min(trait.getScale().getValidValueMin()) - .categories(trait.getScale().getCategories()))); - } - - /** - * Create a list of synonyms, and ensure that there the first element matches the name of the trait

- * This is primarily needed to ensure any system using the first synonym as a display shows the actual name of the ontology term - * @param trait - * @return list of synonyms with at least one value (the name of the trait) - */ - private List prepSynonyms(Trait trait) { - List preppedSynonyms = new ArrayList<>(); - if(trait.getSynonyms() != null) { - preppedSynonyms = trait.getSynonyms(); - int traitNameIdx = -1; - for(int i = 0; i < preppedSynonyms.size(); i++) { - if(preppedSynonyms.get(i).equals(trait.getObservationVariableName())) { - traitNameIdx = i; - break; - } - } - if(traitNameIdx > -1) { - String temp = preppedSynonyms.get(traitNameIdx); - preppedSynonyms.set(traitNameIdx, preppedSynonyms.get(0)); - preppedSynonyms.set(0, temp); - } else { - preppedSynonyms.add(0, trait.getObservationVariableName()); - } - } else { - preppedSynonyms.add(trait.getObservationVariableName()); - } - return preppedSynonyms; - } - - @NotNull - private List filterVariables(List programTraits, - Optional observationVariableDbId, - Optional observationVariableName, - Optional traitClass, - Optional methodDbId, - Optional methodName, - Optional scaleDbId, - Optional scaleName, - Optional traitDbId, - Optional traitName, - Optional ontologyDbId) throws DoesNotExistException { - log.debug("filtering variables:\n" + - "observationVariableDbId: " + observationVariableDbId + "\n" + - "observationVariableName: " + observationVariableName + "\n" + - "traitClass: " + traitClass + "\n" + - "methodDbId: " + methodDbId + "\n" + - "methodName: " + methodName + "\n" + - "scaleDbId: " + scaleDbId + "\n" + - "scaleName: " + scaleName + "\n" + - "traitDbId: " + traitDbId + "\n" + - "traitName: " + traitName + "\n" + - "ontologyDbId: " + ontologyDbId); - - return programTraits.stream() - .filter(trait -> { - boolean matches = true; - - Map, String>> filterParams = new HashMap<>(); - filterParams.put("observationVariableDbId", - Pair.of(observationVariableDbId, - trait.getId() - .toString())); - filterParams.put("observationVariableName", Pair.of(observationVariableName, trait.getObservationVariableName())); - filterParams.put("traitClass", Pair.of(traitClass, trait.getTraitClass())); - filterParams.put("methodDbId", - Pair.of(methodDbId, - trait.getMethodId() - .toString())); - filterParams.put("methodName", - Pair.of(methodName, - trait.getMethod() - .getDescription())); - filterParams.put("scaleDbId", - Pair.of(scaleDbId, - trait.getScale() - .getId() - .toString())); - filterParams.put("scaleName", - Pair.of(scaleName, - trait.getScale() - .getScaleName())); - filterParams.put("traitDbId", - Pair.of(traitDbId, - trait.getId() - .toString())); - filterParams.put("traitName", Pair.of(traitName, trait.getObservationVariableName())); - filterParams.put("ontologyDbId", - Pair.of(ontologyDbId, - trait.getProgramOntologyId() - .toString())); - - for (Map.Entry, String>> filter : filterParams.entrySet()) { - if (filter.getValue().getLeft().isPresent()) { - log.debug("filtering traits by: " + filter.getKey()); - matches = StringUtils.equals(filter.getValue() - .getLeft() - .get(), - filter.getValue() - .getRight()); - } - if (!matches) { - break; - } - } - - return matches; - }) - .map(this::convertToBrAPI) - .collect(Collectors.toList()); - } - - private List getBrAPIObservationVariablesForExperiment(UUID programId, Optional experimentId, Optional environmentId) throws DoesNotExistException, ApiException { - log.debug(String.format("fetching variables for experiment. expId: %s, envId: %s ", experimentId.orElse(""), environmentId.orElse(""))); - Optional program = programService.getById(programId); - if(program.isEmpty()) { - throw new DoesNotExistException("Could not find program: " + programId); - } - UUID expId; - if(experimentId.isPresent()) { - expId = UUID.fromString(experimentId.get()); - } else { - UUID envId = UUID.fromString(environmentId.get()); - BrAPIStudy environment = trialService.getEnvironment(program.get(), envId); - expId = UUID.fromString(Utilities.getExternalReference(environment.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.TRIALS)).get().getReferenceID()); - } - - BrAPITrial experiment = trialService.getExperiment(program.get(), expId); - if(experiment - .getAdditionalInfo().getAsJsonObject() - .has(BrAPIAdditionalInfoFields.OBSERVATION_DATASET_ID)) { - String obsDatasetId = experiment - .getAdditionalInfo().getAsJsonObject() - .get(BrAPIAdditionalInfoFields.OBSERVATION_DATASET_ID).getAsString(); - return trialService.getDatasetObsVars(obsDatasetId, program.get()); - } - - return new ArrayList<>(); - } } diff --git a/src/main/java/org/breedinginsight/daos/impl/AbstractDAO.java b/src/main/java/org/breedinginsight/daos/impl/AbstractDAO.java index 138cf73c4..50aee2608 100644 --- a/src/main/java/org/breedinginsight/daos/impl/AbstractDAO.java +++ b/src/main/java/org/breedinginsight/daos/impl/AbstractDAO.java @@ -161,7 +161,7 @@ public long count() throws DataAccessException { } @Override - public @Nullable P fetchOne(Field field, Z value) throws DataAccessException { + public P fetchOne(Field field, Z value) throws DataAccessException { return jooqDAO.fetchOne(field, value); } diff --git a/src/main/java/org/breedinginsight/services/TraitService.java b/src/main/java/org/breedinginsight/services/TraitService.java index b4fcaa060..d8db6a98e 100644 --- a/src/main/java/org/breedinginsight/services/TraitService.java +++ b/src/main/java/org/breedinginsight/services/TraitService.java @@ -22,10 +22,19 @@ import io.micronaut.http.server.exceptions.HttpServerException; import io.micronaut.http.server.exceptions.InternalServerException; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.text.WordUtils; -import org.brapi.v2.model.pheno.BrAPIObservation; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.BrAPIOntologyReference; +import org.brapi.v2.model.core.BrAPIStudy; +import org.brapi.v2.model.core.BrAPITrial; +import org.brapi.v2.model.pheno.*; import org.breedinginsight.api.auth.AuthenticatedUser; import org.breedinginsight.api.model.v1.response.ValidationErrors; +import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; +import org.breedinginsight.brapi.v2.services.BrAPITrialService; +import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.dao.db.enums.DataType; import org.breedinginsight.dao.db.tables.pojos.MethodEntity; import org.breedinginsight.dao.db.tables.pojos.ProgramSharedOntologyEntity; @@ -37,6 +46,8 @@ import org.breedinginsight.services.exceptions.ValidatorException; import org.breedinginsight.services.validators.TraitValidatorError; import org.breedinginsight.services.validators.TraitValidatorService; +import org.breedinginsight.utilities.Utilities; +import org.jetbrains.annotations.NotNull; import org.jooq.DSLContext; import javax.inject.Inject; @@ -60,14 +71,15 @@ public class TraitService { private TraitValidatorService traitValidator; private DSLContext dsl; private TraitValidatorError traitValidatorError; - + private BrAPITrialService trialService; + private String referenceSource; private final static String FAVORITES_TAG = "favorites"; @Inject public TraitService(TraitDAO traitDao, MethodDAO methodDao, ScaleDAO scaleDao, ObservationDAO observationDao, ProgramService programService, ProgramOntologyService programOntologyService, ProgramObservationLevelService programObservationLevelService, UserService userService, TraitValidatorService traitValidator, DSLContext dsl, TraitValidatorError traitValidatorError, - ProgramOntologyDAO programOntologyDAO) { + ProgramOntologyDAO programOntologyDAO, BrAPITrialService trialService, String referenceSource) { this.traitDAO = traitDao; this.methodDAO = methodDao; this.scaleDAO = scaleDao; @@ -80,6 +92,8 @@ public TraitService(TraitDAO traitDao, MethodDAO methodDao, ScaleDAO scaleDao, O this.dsl = dsl; this.traitValidatorError = traitValidatorError; this.programOntologyDAO = programOntologyDAO; + this.trialService = trialService; + this.referenceSource = referenceSource; } public List getByProgramId(UUID programId, boolean getFullTrait) throws DoesNotExistException { @@ -483,4 +497,182 @@ public List getByName(UUID programId, List names) throws DoesNotE return traitDAO.getTraitsByTraitName(programId, names.stream().map(name -> Trait.builder().observationVariableName(name).build()).collect(Collectors.toList())); } + + public List getBrAPIObservationVariablesForExperiment( + UUID programId, + Optional experimentId, + Optional environmentId + ) throws DoesNotExistException, ApiException { + log.debug(String.format("fetching variables for experiment. expId: %s, envId: %s ", + experimentId.orElse(""), environmentId.orElse(""))); + Optional program = programService.getById(programId); + if(program.isEmpty()) { + throw new DoesNotExistException("Could not find program: " + programId); + } + UUID expId; + if(experimentId.isPresent()) { + expId = UUID.fromString(experimentId.get()); + } else { + UUID envId = UUID.fromString(environmentId.get()); + BrAPIStudy environment = trialService.getEnvironment(program.get(), envId); + expId = UUID.fromString(Utilities.getExternalReference(environment.getExternalReferences(), + Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.TRIALS)).get().getReferenceId()); + } + + BrAPITrial experiment = trialService.getExperiment(program.get(), expId); + if(experiment + .getAdditionalInfo().getAsJsonObject() + .has(BrAPIAdditionalInfoFields.OBSERVATION_DATASET_ID)) { + String obsDatasetId = experiment + .getAdditionalInfo().getAsJsonObject() + .get(BrAPIAdditionalInfoFields.OBSERVATION_DATASET_ID).getAsString(); + return trialService.getDatasetObsVars(obsDatasetId, program.get()); + } + + return new ArrayList<>(); + } + + @NotNull + public List filterVariables(List programTraits, + Optional observationVariableDbId, + Optional observationVariableName, + Optional traitClass, + Optional methodDbId, + Optional methodName, + Optional scaleDbId, + Optional scaleName, + Optional traitDbId, + Optional traitName, + Optional ontologyDbId) throws DoesNotExistException { + log.debug("filtering variables:\n" + + "observationVariableDbId: " + observationVariableDbId + "\n" + + "observationVariableName: " + observationVariableName + "\n" + + "traitClass: " + traitClass + "\n" + + "methodDbId: " + methodDbId + "\n" + + "methodName: " + methodName + "\n" + + "scaleDbId: " + scaleDbId + "\n" + + "scaleName: " + scaleName + "\n" + + "traitDbId: " + traitDbId + "\n" + + "traitName: " + traitName + "\n" + + "ontologyDbId: " + ontologyDbId); + + return programTraits.stream() + .filter(trait -> { + boolean matches = true; + + Map, String>> filterParams = new HashMap<>(); + filterParams.put("observationVariableDbId", + Pair.of(observationVariableDbId, + trait.getId() + .toString())); + filterParams.put("observationVariableName", Pair.of(observationVariableName, trait.getObservationVariableName())); + filterParams.put("traitClass", Pair.of(traitClass, trait.getTraitClass())); + filterParams.put("methodDbId", + Pair.of(methodDbId, + trait.getMethodId() + .toString())); + filterParams.put("methodName", + Pair.of(methodName, + trait.getMethod() + .getDescription())); + filterParams.put("scaleDbId", + Pair.of(scaleDbId, + trait.getScale() + .getId() + .toString())); + filterParams.put("scaleName", + Pair.of(scaleName, + trait.getScale() + .getScaleName())); + filterParams.put("traitDbId", + Pair.of(traitDbId, + trait.getId() + .toString())); + filterParams.put("traitName", Pair.of(traitName, trait.getObservationVariableName())); + filterParams.put("ontologyDbId", + Pair.of(ontologyDbId, + trait.getProgramOntologyId() + .toString())); + + for (Map.Entry, String>> filter : filterParams.entrySet()) { + if (filter.getValue().getLeft().isPresent()) { + log.debug("filtering traits by: " + filter.getKey()); + matches = StringUtils.equals(filter.getValue() + .getLeft() + .get(), + filter.getValue() + .getRight()); + } + if (!matches) { + break; + } + } + + return matches; + }) + .map(this::convertToBrAPI) + .collect(Collectors.toList()); + } + + public BrAPIObservationVariable convertToBrAPI(Trait trait) { + BrAPIOntologyReference brAPIOntologyReference = new BrAPIOntologyReference().ontologyDbId(trait.getProgramOntologyId() + .toString()); + String status = trait.getActive() ? "active" : "inactive"; + List synonyms = prepSynonyms(trait); + return new BrAPIObservationVariable().observationVariableDbId(trait.getId().toString()) + .observationVariableName(trait.getObservationVariableName()) + .defaultValue(trait.getDefaultValue()) + .status(status) + .synonyms(synonyms) + .trait(new BrAPITrait().ontologyReference(brAPIOntologyReference) + .traitName(trait.getObservationVariableName()) + .traitDbId(trait.getId().toString()) + .entity(trait.getEntity()) + .attribute(trait.getAttribute()) + .status(status) + .synonyms(synonyms)) + .method(new BrAPIMethod().ontologyReference(brAPIOntologyReference) + .methodDbId(trait.getMethod().getId().toString()) + .methodClass(trait.getMethod().getMethodClass()) + .description(trait.getMethod().getDescription()) + .formula(trait.getMethod().getFormula())) + .scale(new BrAPIScale().ontologyReference(brAPIOntologyReference) + .scaleDbId(trait.getScale().getId().toString()) + .scaleName(trait.getScale().getScaleName()) + .dataType(BrAPITraitDataType.fromValue(trait.getScale().getDataType().getLiteral())) + .decimalPlaces(trait.getScale().getDecimalPlaces()) + .validValues(new BrAPIScaleValidValues().max(trait.getScale().getValidValueMax()) + .min(trait.getScale().getValidValueMin()) + .categories(trait.getScale().getCategories()))); + } + + /** + * Create a list of synonyms, and ensure that there the first element matches the name of the trait

+ * This is primarily needed to ensure any system using the first synonym as a display shows the actual name of the ontology term + * @param trait + * @return list of synonyms with at least one value (the name of the trait) + */ + private List prepSynonyms(Trait trait) { + List preppedSynonyms = new ArrayList<>(); + if(trait.getSynonyms() != null) { + preppedSynonyms = trait.getSynonyms(); + int traitNameIdx = -1; + for(int i = 0; i < preppedSynonyms.size(); i++) { + if(preppedSynonyms.get(i).equals(trait.getObservationVariableName())) { + traitNameIdx = i; + break; + } + } + if(traitNameIdx > -1) { + String temp = preppedSynonyms.get(traitNameIdx); + preppedSynonyms.set(traitNameIdx, preppedSynonyms.get(0)); + preppedSynonyms.set(0, temp); + } else { + preppedSynonyms.add(0, trait.getObservationVariableName()); + } + } else { + preppedSynonyms.add(trait.getObservationVariableName()); + } + return preppedSynonyms; + } } From f408da381c3af7cf7e06352ad57e93404dbf0f0d Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:08:16 -0500 Subject: [PATCH 16/20] remove vestigial comments --- .../breedinginsight/brapi/v2/services/BrAPITrialService.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java index 9ebd95954..98c07a23e 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java @@ -55,7 +55,6 @@ public class BrAPITrialService { private final BrAPITrialDAO trialDAO; private final BrAPIObservationDAO observationDAO; private final BrAPIListDAO listDAO; -// private final BrAPIObservationVariableDAO obsVarDAO; private final TraitService traitService; private final BrAPIStudyDAO studyDAO; @@ -70,7 +69,6 @@ public BrAPITrialService(@Property(name = "brapi.server.reference-source") Strin BrAPITrialDAO trialDAO, BrAPIObservationDAO observationDAO, BrAPIListDAO listDAO, -// BrAPIObservationVariableDAO obsVarDAO, TraitService traitService, BrAPIStudyDAO studyDAO, BrAPISeasonDAO seasonDAO, @@ -82,7 +80,6 @@ public BrAPITrialService(@Property(name = "brapi.server.reference-source") Strin this.trialDAO = trialDAO; this.observationDAO = observationDAO; this.listDAO = listDAO; -// this.obsVarDAO = obsVarDAO; this.traitService = traitService; this.studyDAO = studyDAO; this.seasonDAO = seasonDAO; @@ -413,7 +410,6 @@ public List getDatasetObsVars(String datasetId, Program program) throws A String.format("%s/%s", referenceSource, ExternalReferenceSource.DATASET.getName()), UUID.fromString(datasetId)); if (lists == null || lists.isEmpty()) { -// throw new DoesNotExistException("Dataset observation variables list not returned from BrAPI service"); log.warn(String.format("Dataset %s observation variables list not returned from BrAPI service", datasetId)); return new ArrayList<>(); } From 09f8c47869ea9a07e06da449c41a256fb4777427 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Tue, 23 Jan 2024 11:12:39 -0500 Subject: [PATCH 17/20] move helper methods to new ObservationVariableService class --- .../BrAPIObservationVariableController.java | 11 +- .../BrAPIObservationVariableService.java | 233 ++++++++++++++++++ 2 files changed, 240 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationVariableService.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java index f7e9368f4..568a2d286 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java @@ -35,6 +35,7 @@ import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; import org.breedinginsight.brapi.v1.controller.BrapiVersion; +import org.breedinginsight.brapi.v2.services.BrAPIObservationVariableService; import org.breedinginsight.brapi.v2.services.BrAPITrialService; import org.breedinginsight.model.Trait; import org.breedinginsight.services.OntologyService; @@ -57,19 +58,21 @@ public class BrAPIObservationVariableController { private final String referenceSource; private final OntologyService ontologyService; + private final BrAPIObservationVariableService observationVariableService; private final TraitService traitService; - private final BrAPITrialService trialService; private final ProgramService programService; @Inject public BrAPIObservationVariableController(OntologyService ontologyService, + BrAPIObservationVariableService observationVariableService, TraitService traitService, BrAPITrialService trialService, ProgramService programService, @Property(name = "brapi.server.reference-source") String referenceSource) { this.ontologyService = ontologyService; + this.observationVariableService = observationVariableService; this.traitService = traitService; this.trialService = trialService; this.programService = programService; @@ -108,7 +111,7 @@ public HttpResponse variablesGet(@PathVari log.debug("unsupported variable filters, returning"); programTraits = new ArrayList<>(); } else if(environmentId != null || experimentId != null) { - programTraits = traitService.getBrAPIObservationVariablesForExperiment( + programTraits = observationVariableService.getBrAPIObservationVariablesForExperiment( programId, Optional.ofNullable(experimentId), Optional.ofNullable(environmentId)); } else { log.debug("fetching variables for the program: " + programId); @@ -116,7 +119,7 @@ public HttpResponse variablesGet(@PathVari } - List filteredObsVars = traitService.filterVariables(programTraits, + List filteredObsVars = observationVariableService.filterVariables(programTraits, Optional.ofNullable(observationVariableDbId), Optional.ofNullable(observationVariableName), Optional.ofNullable(traitClass), @@ -168,7 +171,7 @@ public HttpResponse variablesObservation BrAPIObservationVariableSingleResponse response = new BrAPIObservationVariableSingleResponse() .metadata(new BrAPIMetadata()) - .result(traitService.convertToBrAPI(trait.get())); + .result(observationVariableService.convertToBrAPI(trait.get())); return HttpResponse.ok(response); } catch (DoesNotExistException e) { diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationVariableService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationVariableService.java new file mode 100644 index 000000000..76f879c3e --- /dev/null +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationVariableService.java @@ -0,0 +1,233 @@ +/* + * 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.services; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.BrAPIOntologyReference; +import org.brapi.v2.model.core.BrAPIStudy; +import org.brapi.v2.model.core.BrAPITrial; +import org.brapi.v2.model.pheno.*; +import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; +import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; +import org.breedinginsight.model.*; +import org.breedinginsight.services.ProgramService; +import org.breedinginsight.services.exceptions.DoesNotExistException; +import org.breedinginsight.utilities.Utilities; +import org.jetbrains.annotations.NotNull; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Singleton +public class BrAPIObservationVariableService { + private final ProgramService programService; + private final BrAPITrialService trialService; + private final String referenceSource; + + @Inject + public BrAPIObservationVariableService( + ProgramService programService, BrAPITrialService trialService, String referenceSource) { + this.programService = programService; + this.trialService = trialService; + this.referenceSource = referenceSource; + } + + public List getBrAPIObservationVariablesForExperiment( + UUID programId, + Optional experimentId, + Optional environmentId + ) throws DoesNotExistException, ApiException { + log.debug(String.format("fetching variables for experiment. expId: %s, envId: %s ", + experimentId.orElse(""), environmentId.orElse(""))); + Optional program = programService.getById(programId); + if(program.isEmpty()) { + throw new DoesNotExistException("Could not find program: " + programId); + } + UUID expId; + if(experimentId.isPresent()) { + expId = UUID.fromString(experimentId.get()); + } else { + UUID envId = UUID.fromString(environmentId.get()); + BrAPIStudy environment = trialService.getEnvironment(program.get(), envId); + expId = UUID.fromString(Utilities.getExternalReference(environment.getExternalReferences(), + Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.TRIALS)).get().getReferenceId()); + } + + BrAPITrial experiment = trialService.getExperiment(program.get(), expId); + if(experiment + .getAdditionalInfo().getAsJsonObject() + .has(BrAPIAdditionalInfoFields.OBSERVATION_DATASET_ID)) { + String obsDatasetId = experiment + .getAdditionalInfo().getAsJsonObject() + .get(BrAPIAdditionalInfoFields.OBSERVATION_DATASET_ID).getAsString(); + return trialService.getDatasetObsVars(obsDatasetId, program.get()); + } + + return new ArrayList<>(); + } + + @NotNull + public List filterVariables(List programTraits, + Optional observationVariableDbId, + Optional observationVariableName, + Optional traitClass, + Optional methodDbId, + Optional methodName, + Optional scaleDbId, + Optional scaleName, + Optional traitDbId, + Optional traitName, + Optional ontologyDbId) throws DoesNotExistException { + log.debug("filtering variables:\n" + + "observationVariableDbId: " + observationVariableDbId + "\n" + + "observationVariableName: " + observationVariableName + "\n" + + "traitClass: " + traitClass + "\n" + + "methodDbId: " + methodDbId + "\n" + + "methodName: " + methodName + "\n" + + "scaleDbId: " + scaleDbId + "\n" + + "scaleName: " + scaleName + "\n" + + "traitDbId: " + traitDbId + "\n" + + "traitName: " + traitName + "\n" + + "ontologyDbId: " + ontologyDbId); + + return programTraits.stream() + .filter(trait -> { + boolean matches = true; + + Map, String>> filterParams = new HashMap<>(); + filterParams.put("observationVariableDbId", + Pair.of(observationVariableDbId, + trait.getId() + .toString())); + filterParams.put("observationVariableName", Pair.of(observationVariableName, trait.getObservationVariableName())); + filterParams.put("traitClass", Pair.of(traitClass, trait.getTraitClass())); + filterParams.put("methodDbId", + Pair.of(methodDbId, + trait.getMethodId() + .toString())); + filterParams.put("methodName", + Pair.of(methodName, + trait.getMethod() + .getDescription())); + filterParams.put("scaleDbId", + Pair.of(scaleDbId, + trait.getScale() + .getId() + .toString())); + filterParams.put("scaleName", + Pair.of(scaleName, + trait.getScale() + .getScaleName())); + filterParams.put("traitDbId", + Pair.of(traitDbId, + trait.getId() + .toString())); + filterParams.put("traitName", Pair.of(traitName, trait.getObservationVariableName())); + filterParams.put("ontologyDbId", + Pair.of(ontologyDbId, + trait.getProgramOntologyId() + .toString())); + + for (Map.Entry, String>> filter : filterParams.entrySet()) { + if (filter.getValue().getLeft().isPresent()) { + log.debug("filtering traits by: " + filter.getKey()); + matches = StringUtils.equals(filter.getValue() + .getLeft() + .get(), + filter.getValue() + .getRight()); + } + if (!matches) { + break; + } + } + + return matches; + }) + .map(this::convertToBrAPI) + .collect(Collectors.toList()); + } + + public BrAPIObservationVariable convertToBrAPI(Trait trait) { + BrAPIOntologyReference brAPIOntologyReference = new BrAPIOntologyReference().ontologyDbId(trait.getProgramOntologyId() + .toString()); + String status = trait.getActive() ? "active" : "inactive"; + List synonyms = prepSynonyms(trait); + return new BrAPIObservationVariable().observationVariableDbId(trait.getId().toString()) + .observationVariableName(trait.getObservationVariableName()) + .defaultValue(trait.getDefaultValue()) + .status(status) + .synonyms(synonyms) + .trait(new BrAPITrait().ontologyReference(brAPIOntologyReference) + .traitName(trait.getObservationVariableName()) + .traitDbId(trait.getId().toString()) + .entity(trait.getEntity()) + .attribute(trait.getAttribute()) + .status(status) + .synonyms(synonyms)) + .method(new BrAPIMethod().ontologyReference(brAPIOntologyReference) + .methodDbId(trait.getMethod().getId().toString()) + .methodClass(trait.getMethod().getMethodClass()) + .description(trait.getMethod().getDescription()) + .formula(trait.getMethod().getFormula())) + .scale(new BrAPIScale().ontologyReference(brAPIOntologyReference) + .scaleDbId(trait.getScale().getId().toString()) + .scaleName(trait.getScale().getScaleName()) + .dataType(BrAPITraitDataType.fromValue(trait.getScale().getDataType().getLiteral())) + .decimalPlaces(trait.getScale().getDecimalPlaces()) + .validValues(new BrAPIScaleValidValues().max(trait.getScale().getValidValueMax()) + .min(trait.getScale().getValidValueMin()) + .categories(trait.getScale().getCategories()))); + } + + /** + * Create a list of synonyms, and ensure that there the first element matches the name of the trait

+ * This is primarily needed to ensure any system using the first synonym as a display shows the actual name of the ontology term + * @param trait + * @return list of synonyms with at least one value (the name of the trait) + */ + private List prepSynonyms(Trait trait) { + List preppedSynonyms = new ArrayList<>(); + if(trait.getSynonyms() != null) { + preppedSynonyms = trait.getSynonyms(); + int traitNameIdx = -1; + for(int i = 0; i < preppedSynonyms.size(); i++) { + if(preppedSynonyms.get(i).equals(trait.getObservationVariableName())) { + traitNameIdx = i; + break; + } + } + if(traitNameIdx > -1) { + String temp = preppedSynonyms.get(traitNameIdx); + preppedSynonyms.set(traitNameIdx, preppedSynonyms.get(0)); + preppedSynonyms.set(0, temp); + } else { + preppedSynonyms.add(0, trait.getObservationVariableName()); + } + } else { + preppedSynonyms.add(trait.getObservationVariableName()); + } + return preppedSynonyms; + } +} From 8a8dfed65f719fae0589c249ee1347c2c3f27a5d Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Tue, 23 Jan 2024 11:40:21 -0500 Subject: [PATCH 18/20] add orElse for optionals --- .../BrAPIObservationVariableService.java | 5 +- .../services/TraitService.java | 178 ------------------ 2 files changed, 3 insertions(+), 180 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationVariableService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationVariableService.java index 76f879c3e..091bcaab6 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationVariableService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationVariableService.java @@ -68,10 +68,11 @@ public List getBrAPIObservationVariablesForExperiment( if(experimentId.isPresent()) { expId = UUID.fromString(experimentId.get()); } else { - UUID envId = UUID.fromString(environmentId.get()); + UUID envId = UUID.fromString(environmentId.orElseThrow(() -> new IllegalStateException("no environment id found"))); BrAPIStudy environment = trialService.getEnvironment(program.get(), envId); expId = UUID.fromString(Utilities.getExternalReference(environment.getExternalReferences(), - Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.TRIALS)).get().getReferenceId()); + Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.TRIALS)) + .orElseThrow(() -> new IllegalStateException("no external reference found")).getReferenceId()); } BrAPITrial experiment = trialService.getExperiment(program.get(), expId); diff --git a/src/main/java/org/breedinginsight/services/TraitService.java b/src/main/java/org/breedinginsight/services/TraitService.java index d8db6a98e..d4f0f00d9 100644 --- a/src/main/java/org/breedinginsight/services/TraitService.java +++ b/src/main/java/org/breedinginsight/services/TraitService.java @@ -497,182 +497,4 @@ public List getByName(UUID programId, List names) throws DoesNotE return traitDAO.getTraitsByTraitName(programId, names.stream().map(name -> Trait.builder().observationVariableName(name).build()).collect(Collectors.toList())); } - - public List getBrAPIObservationVariablesForExperiment( - UUID programId, - Optional experimentId, - Optional environmentId - ) throws DoesNotExistException, ApiException { - log.debug(String.format("fetching variables for experiment. expId: %s, envId: %s ", - experimentId.orElse(""), environmentId.orElse(""))); - Optional program = programService.getById(programId); - if(program.isEmpty()) { - throw new DoesNotExistException("Could not find program: " + programId); - } - UUID expId; - if(experimentId.isPresent()) { - expId = UUID.fromString(experimentId.get()); - } else { - UUID envId = UUID.fromString(environmentId.get()); - BrAPIStudy environment = trialService.getEnvironment(program.get(), envId); - expId = UUID.fromString(Utilities.getExternalReference(environment.getExternalReferences(), - Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.TRIALS)).get().getReferenceId()); - } - - BrAPITrial experiment = trialService.getExperiment(program.get(), expId); - if(experiment - .getAdditionalInfo().getAsJsonObject() - .has(BrAPIAdditionalInfoFields.OBSERVATION_DATASET_ID)) { - String obsDatasetId = experiment - .getAdditionalInfo().getAsJsonObject() - .get(BrAPIAdditionalInfoFields.OBSERVATION_DATASET_ID).getAsString(); - return trialService.getDatasetObsVars(obsDatasetId, program.get()); - } - - return new ArrayList<>(); - } - - @NotNull - public List filterVariables(List programTraits, - Optional observationVariableDbId, - Optional observationVariableName, - Optional traitClass, - Optional methodDbId, - Optional methodName, - Optional scaleDbId, - Optional scaleName, - Optional traitDbId, - Optional traitName, - Optional ontologyDbId) throws DoesNotExistException { - log.debug("filtering variables:\n" + - "observationVariableDbId: " + observationVariableDbId + "\n" + - "observationVariableName: " + observationVariableName + "\n" + - "traitClass: " + traitClass + "\n" + - "methodDbId: " + methodDbId + "\n" + - "methodName: " + methodName + "\n" + - "scaleDbId: " + scaleDbId + "\n" + - "scaleName: " + scaleName + "\n" + - "traitDbId: " + traitDbId + "\n" + - "traitName: " + traitName + "\n" + - "ontologyDbId: " + ontologyDbId); - - return programTraits.stream() - .filter(trait -> { - boolean matches = true; - - Map, String>> filterParams = new HashMap<>(); - filterParams.put("observationVariableDbId", - Pair.of(observationVariableDbId, - trait.getId() - .toString())); - filterParams.put("observationVariableName", Pair.of(observationVariableName, trait.getObservationVariableName())); - filterParams.put("traitClass", Pair.of(traitClass, trait.getTraitClass())); - filterParams.put("methodDbId", - Pair.of(methodDbId, - trait.getMethodId() - .toString())); - filterParams.put("methodName", - Pair.of(methodName, - trait.getMethod() - .getDescription())); - filterParams.put("scaleDbId", - Pair.of(scaleDbId, - trait.getScale() - .getId() - .toString())); - filterParams.put("scaleName", - Pair.of(scaleName, - trait.getScale() - .getScaleName())); - filterParams.put("traitDbId", - Pair.of(traitDbId, - trait.getId() - .toString())); - filterParams.put("traitName", Pair.of(traitName, trait.getObservationVariableName())); - filterParams.put("ontologyDbId", - Pair.of(ontologyDbId, - trait.getProgramOntologyId() - .toString())); - - for (Map.Entry, String>> filter : filterParams.entrySet()) { - if (filter.getValue().getLeft().isPresent()) { - log.debug("filtering traits by: " + filter.getKey()); - matches = StringUtils.equals(filter.getValue() - .getLeft() - .get(), - filter.getValue() - .getRight()); - } - if (!matches) { - break; - } - } - - return matches; - }) - .map(this::convertToBrAPI) - .collect(Collectors.toList()); - } - - public BrAPIObservationVariable convertToBrAPI(Trait trait) { - BrAPIOntologyReference brAPIOntologyReference = new BrAPIOntologyReference().ontologyDbId(trait.getProgramOntologyId() - .toString()); - String status = trait.getActive() ? "active" : "inactive"; - List synonyms = prepSynonyms(trait); - return new BrAPIObservationVariable().observationVariableDbId(trait.getId().toString()) - .observationVariableName(trait.getObservationVariableName()) - .defaultValue(trait.getDefaultValue()) - .status(status) - .synonyms(synonyms) - .trait(new BrAPITrait().ontologyReference(brAPIOntologyReference) - .traitName(trait.getObservationVariableName()) - .traitDbId(trait.getId().toString()) - .entity(trait.getEntity()) - .attribute(trait.getAttribute()) - .status(status) - .synonyms(synonyms)) - .method(new BrAPIMethod().ontologyReference(brAPIOntologyReference) - .methodDbId(trait.getMethod().getId().toString()) - .methodClass(trait.getMethod().getMethodClass()) - .description(trait.getMethod().getDescription()) - .formula(trait.getMethod().getFormula())) - .scale(new BrAPIScale().ontologyReference(brAPIOntologyReference) - .scaleDbId(trait.getScale().getId().toString()) - .scaleName(trait.getScale().getScaleName()) - .dataType(BrAPITraitDataType.fromValue(trait.getScale().getDataType().getLiteral())) - .decimalPlaces(trait.getScale().getDecimalPlaces()) - .validValues(new BrAPIScaleValidValues().max(trait.getScale().getValidValueMax()) - .min(trait.getScale().getValidValueMin()) - .categories(trait.getScale().getCategories()))); - } - - /** - * Create a list of synonyms, and ensure that there the first element matches the name of the trait

- * This is primarily needed to ensure any system using the first synonym as a display shows the actual name of the ontology term - * @param trait - * @return list of synonyms with at least one value (the name of the trait) - */ - private List prepSynonyms(Trait trait) { - List preppedSynonyms = new ArrayList<>(); - if(trait.getSynonyms() != null) { - preppedSynonyms = trait.getSynonyms(); - int traitNameIdx = -1; - for(int i = 0; i < preppedSynonyms.size(); i++) { - if(preppedSynonyms.get(i).equals(trait.getObservationVariableName())) { - traitNameIdx = i; - break; - } - } - if(traitNameIdx > -1) { - String temp = preppedSynonyms.get(traitNameIdx); - preppedSynonyms.set(traitNameIdx, preppedSynonyms.get(0)); - preppedSynonyms.set(0, temp); - } else { - preppedSynonyms.add(0, trait.getObservationVariableName()); - } - } else { - preppedSynonyms.add(trait.getObservationVariableName()); - } - return preppedSynonyms; - } } From 46d01f063b21f7d9eff979aff1b9f54190b0c902 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Tue, 23 Jan 2024 12:08:16 -0500 Subject: [PATCH 19/20] remove circular dependency --- .../java/org/breedinginsight/services/TraitService.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/org/breedinginsight/services/TraitService.java b/src/main/java/org/breedinginsight/services/TraitService.java index d4f0f00d9..5f240e025 100644 --- a/src/main/java/org/breedinginsight/services/TraitService.java +++ b/src/main/java/org/breedinginsight/services/TraitService.java @@ -71,15 +71,13 @@ public class TraitService { private TraitValidatorService traitValidator; private DSLContext dsl; private TraitValidatorError traitValidatorError; - private BrAPITrialService trialService; - private String referenceSource; private final static String FAVORITES_TAG = "favorites"; @Inject public TraitService(TraitDAO traitDao, MethodDAO methodDao, ScaleDAO scaleDao, ObservationDAO observationDao, ProgramService programService, ProgramOntologyService programOntologyService, ProgramObservationLevelService programObservationLevelService, UserService userService, TraitValidatorService traitValidator, DSLContext dsl, TraitValidatorError traitValidatorError, - ProgramOntologyDAO programOntologyDAO, BrAPITrialService trialService, String referenceSource) { + ProgramOntologyDAO programOntologyDAO) { this.traitDAO = traitDao; this.methodDAO = methodDao; this.scaleDAO = scaleDao; @@ -92,8 +90,6 @@ public TraitService(TraitDAO traitDao, MethodDAO methodDao, ScaleDAO scaleDao, O this.dsl = dsl; this.traitValidatorError = traitValidatorError; this.programOntologyDAO = programOntologyDAO; - this.trialService = trialService; - this.referenceSource = referenceSource; } public List getByProgramId(UUID programId, boolean getFullTrait) throws DoesNotExistException { From 003481b6fa9d071e030de337c08529664df00f4e Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Tue, 23 Jan 2024 14:41:38 -0500 Subject: [PATCH 20/20] [BI-1921] update tests --- .../brapi/v2/BrAPIObservationVariableController.java | 7 +------ .../org/breedinginsight/brapi/v2/BrAPIV2Controller.java | 2 +- .../brapi/v2/services/BrAPIObservationVariableService.java | 4 +++- .../brapi/v2/BrAPIV2ControllerIntegrationTest.java | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java index 568a2d286..45d6983f3 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationVariableController.java @@ -17,7 +17,6 @@ package org.breedinginsight.brapi.v2; -import io.micronaut.context.annotation.Property; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; @@ -55,13 +54,11 @@ @Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @Secured(SecurityRule.IS_AUTHENTICATED) public class BrAPIObservationVariableController { - private final String referenceSource; private final OntologyService ontologyService; private final BrAPIObservationVariableService observationVariableService; private final TraitService traitService; private final BrAPITrialService trialService; - private final ProgramService programService; @Inject @@ -69,14 +66,12 @@ public BrAPIObservationVariableController(OntologyService ontologyService, BrAPIObservationVariableService observationVariableService, TraitService traitService, BrAPITrialService trialService, - ProgramService programService, - @Property(name = "brapi.server.reference-source") String referenceSource) { + ProgramService programService) { this.ontologyService = ontologyService; this.observationVariableService = observationVariableService; this.traitService = traitService; this.trialService = trialService; this.programService = programService; - this.referenceSource = referenceSource; } @Get("/variables") diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java index 0d4a5449d..d6e4dde43 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java @@ -60,7 +60,7 @@ public BrAPIV2Controller(SecurityService securityService, ProgramService program } - @Get("/${micronaut.bi.api.version}/" + BrapiVersion.BRAPI_V2 + "/serverinfo") + @Get("/${micronaut.bi.api.version}" + BrapiVersion.BRAPI_V2 + "/serverinfo") @Produces(MediaType.APPLICATION_JSON) @Secured(SecurityRule.IS_ANONYMOUS) public BrAPIServerInfoResponse serverinfo() { diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationVariableService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationVariableService.java index 091bcaab6..f9742fb74 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationVariableService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationVariableService.java @@ -17,6 +17,7 @@ package org.breedinginsight.brapi.v2.services; +import io.micronaut.context.annotation.Property; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -47,7 +48,8 @@ public class BrAPIObservationVariableService { @Inject public BrAPIObservationVariableService( - ProgramService programService, BrAPITrialService trialService, String referenceSource) { + ProgramService programService, BrAPITrialService trialService, + @Property(name = "brapi.server.reference-source") String referenceSource) { this.programService = programService; this.trialService = trialService; this.referenceSource = referenceSource; diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java index ee5fbbfd7..1e1cb922b 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java @@ -127,7 +127,7 @@ public void setup() { @Test public void testRootServerInfo() { - Flowable> call = biClient.exchange(GET("/brapi/v2/serverinfo"), String.class); + Flowable> call = biClient.exchange(GET("/v1/brapi/v2/serverinfo"), String.class); HttpResponse response = call.blockingFirst(); assertEquals(HttpStatus.OK, response.getStatus());