From f746954ed201269ff7842fa96e1555b7d55f816a Mon Sep 17 00:00:00 2001 From: HMS17 <84345306+HMS17@users.noreply.github.com> Date: Tue, 8 Oct 2024 16:09:29 -0400 Subject: [PATCH 001/132] [BI-2067] - Fix version --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index f8900998b..af5a61a1b 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -14,5 +14,5 @@ # limitations under the License. # -version=v1.0.0+833 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/45511950453d85470e7b1063c7130510411df54b +version=v1.1.0+835 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/5d525fe67963aa9c8a91560675ebed69cfe5ae87 From 0664cb2025eac8920a7cc74dd3ce5a8378ac0991 Mon Sep 17 00:00:00 2001 From: HMS17 <84345306+HMS17@users.noreply.github.com> Date: Tue, 8 Oct 2024 16:17:13 -0400 Subject: [PATCH 002/132] [BI-2067] - Fix version From 890aeee7bd606e7e3eef736f6c5bd6b84aa3b8f9 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Tue, 8 Oct 2024 21:17:25 +0000 Subject: [PATCH 003/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index af5a61a1b..419e1b1df 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -14,5 +14,5 @@ # limitations under the License. # -version=v1.1.0+835 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/5d525fe67963aa9c8a91560675ebed69cfe5ae87 +version=v1.1.0+839 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/7d237259f0eae02cced1d0a02b690a514d37de4e From 687910bcae08b7e3f90552aac89bd6a912834b61 Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Thu, 26 Sep 2024 16:38:44 -0400 Subject: [PATCH 004/132] [BI-2203] Added migtations to address bug in staging --- .../services/ExperimentPhenotypeService.java | 2 +- .../V1_31_0__Set_Dev_Admin_Email.java | 56 ++ src/main/resources/application.yml | 1 + .../V1.31.1__set_email_orcid_constraint.sql | 28 + .../delta/DeltaEntityFactoryUnitTest.java.bak | 552 ++++++++++++++++++ 5 files changed, 638 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java create mode 100644 src/main/resources/db/migration/V1.31.1__set_email_orcid_constraint.sql create mode 100644 src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java.bak diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/services/ExperimentPhenotypeService.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/services/ExperimentPhenotypeService.java index ce744234d..32bc8c348 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/services/ExperimentPhenotypeService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/services/ExperimentPhenotypeService.java @@ -56,7 +56,7 @@ public ProcessedPhenotypeData extractPhenotypes(ImportContext importContext) { Table data = importContext.getData(); ImportUpload upload = importContext.getUpload(); Program program = importContext.getProgram(); - + String[] cols = upload.getDynamicColumnNames(); DynamicColumnParser.DynamicColumnParseResult result = DynamicColumnParser.parse(data, upload.getDynamicColumnNames()); List traits = experimentValidateService.verifyTraits(program.getId(), result); diff --git a/src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java b/src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java new file mode 100644 index 000000000..c8bd1697d --- /dev/null +++ b/src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java @@ -0,0 +1,56 @@ +/* + * 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.db.migration; + +import lombok.extern.slf4j.Slf4j; +import org.breedinginsight.daos.UserDAO; +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.Context; +import org.jooq.DSLContext; + +import javax.inject.Inject; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.*; + +@Slf4j +public class V1_31_0__Set_Dev_Admin_Email extends BaseJavaMigration { + + @Inject + private DSLContext dsl; + @Inject + private UserDAO userDAO; + + final private String ORCID_SANDBOX_AUTHENTICATION = "orcid-sandbox-authentication"; + + public void migrate(Context context) throws Exception { + Map placeholders = context.getConfiguration().getPlaceholders(); + boolean isOrcidSandboxAuthentication = Boolean.parseBoolean( placeholders.get(ORCID_SANDBOX_AUTHENTICATION) ); + updateDevAdminUser(context, isOrcidSandboxAuthentication); + } + + private void updateDevAdminUser(Context context, boolean isOrcidSandboxAuthentication) throws SQLException { + String biDevAdminUserEmail = isOrcidSandboxAuthentication ? + "bidevteam@mailinator.com" : "bidevteam@cornell.edu"; + try (Statement update = context.getConnection().createStatement()) { + String sql = "UPDATE bi_user SET email='" + biDevAdminUserEmail + "' WHERE NULLIF(email,'') IS NULL AND bi_user.name='BI-DEV Admin'"; + log.debug(sql); + update.executeUpdate(sql); + } + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 10acaf1f0..85987ef9d 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -110,6 +110,7 @@ flyway: placeholders: default-url: ${brapi.server.default-url} brapi-reference-source: ${brapi.server.reference-source} + orcid-sandbox-authentication: ${ORCID_SANDBOX_AUTHENTICATION:false} out-of-order: true jooq: datasources: diff --git a/src/main/resources/db/migration/V1.31.1__set_email_orcid_constraint.sql b/src/main/resources/db/migration/V1.31.1__set_email_orcid_constraint.sql new file mode 100644 index 000000000..bd465ee4b --- /dev/null +++ b/src/main/resources/db/migration/V1.31.1__set_email_orcid_constraint.sql @@ -0,0 +1,28 @@ +/* + * 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. + */ + +/* + From https://breedinginsight.atlassian.net/jira/software/c/projects/BI/boards/1?selectedIssue=BI-2203 + + ...constraint should be added to the bi_user table that prevents the orcid id from being non-null if the email is null. + The reverse (non-null email and null orcid) must still be allowed since it is the state for a user pending verification. + */ + +ALTER TABLE bi_user DROP CONSTRAINT IF EXISTS email_orcid; + +ALTER TABLE bi_user +ADD CONSTRAINT email_orcid CHECK ( (email IS NOT NULL ) OR (orcid IS NULL) ) ; \ No newline at end of file diff --git a/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java.bak b/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java.bak new file mode 100644 index 000000000..cd8f71028 --- /dev/null +++ b/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java.bak @@ -0,0 +1,552 @@ +package org.breedinginsight.model.delta; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.brapi.v2.model.*; +import org.brapi.v2.model.core.*; +import org.brapi.v2.model.germ.*; +import org.brapi.v2.model.pheno.*; +import org.breedinginsight.DatabaseTest; +import org.breedinginsight.model.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import javax.inject.Inject; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.time.LocalDate; +import java.time.Month; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import static org.breedinginsight.utilities.DatasetUtil.gson; +import static org.junit.jupiter.api.Assertions.*; + +@Slf4j +@MicronautTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class DeltaEntityFactoryUnitTest extends DatabaseTest { + + @Inject + private DeltaEntityFactory entityFactory; + + @Test + @SneakyThrows + void deltaGermplasmTest() { + // Create BrAPIGermplasm + BrAPIGermplasm brAPIGermplasm = createBrAPIGermplasm(); + + // Use the factory to create a DeltaGermplasm from the BrAPIGermplasm + DeltaGermplasm deltaGermplasm = entityFactory.makeDeltaGermplasmBean(brAPIGermplasm); + assertNotNull(deltaGermplasm); + + // Assert that the DeltaEntity constructor cannot be called to directly instantiate + testConstructorNotAccessible(DeltaGermplasm.class); + // Check that clone makes a correct copy + BrAPIGermplasm clonedBrAPIGermplasm = deltaGermplasm.cloneEntity(); + assertNotNull(clonedBrAPIGermplasm); + assertEquals(clonedBrAPIGermplasm, brAPIGermplasm); + } + + @Test + @SneakyThrows + void deltaLocationTest() { + // Create BrAPILocation + ProgramLocation programLocation = createProgramLocation(); + + // Use the factory to create a DeltaLocation from the BrAPILocation + DeltaLocation deltaLocation = entityFactory.makeDeltaLocationBean(programLocation); + assertNotNull(deltaLocation); + + // Assert that the DeltaEntity constructor cannot be called to directly instantiate + testConstructorNotAccessible(DeltaLocation.class); + // Check that clone makes a correct copy + ProgramLocation clonedProgramLocation = deltaLocation.cloneEntity(); + assertNotNull(clonedProgramLocation); + assertEquals(clonedProgramLocation, programLocation); + } + + @Test + @SneakyThrows + void deltaObservationTest() { + // Create BrAPIObservation + BrAPIObservation brAPIObservation = createBrAPIObservation(); + + // Use the factory to create a DeltaObservation from the BrAPIObservation + DeltaObservation deltaObservation = entityFactory.makeDeltaObservationBean(brAPIObservation); + assertNotNull(deltaObservation); + + // Assert that the DeltaEntity constructor cannot be called to directly instantiate + testConstructorNotAccessible(DeltaObservation.class); + // Check that clone makes a correct copy + BrAPIObservation clonedBrAPIObservation = deltaObservation.cloneEntity(); + assertNotNull(clonedBrAPIObservation); + assertEquals(clonedBrAPIObservation, brAPIObservation); + } + + @Test + @SneakyThrows + void deltaObservationUnitTest() { + // Create BrAPIObservationUnit + BrAPIObservationUnit brAPIObservationUnit = createBrAPIObservationUnit(); + + // Use the factory to create a DeltaObservationUnit from the BrAPIObservationUnit + DeltaObservationUnit deltaObservationUnit = entityFactory.makeDeltaObservationUnitBean(brAPIObservationUnit); + assertNotNull(deltaObservationUnit); + + // Assert that the DeltaEntity constructor cannot be called to directly instantiate + testConstructorNotAccessible(DeltaObservationUnit.class); + + // Check that clone makes a correct copy + BrAPIObservationUnit clonedBrAPIObservationUnit = deltaObservationUnit.cloneEntity(); + assertNotNull(clonedBrAPIObservationUnit); + assertEquals(clonedBrAPIObservationUnit, brAPIObservationUnit); + } + + @Test + @SneakyThrows + void deltaObservationVariableTest() { + // Create BrAPIObservationVariable + BrAPIObservationVariable brAPIObservationVariable = createBrAPIObservationVariable(); + + // Use the factory to create a DeltaObservationVariable from the BrAPIObservationVariable + DeltaObservationVariable deltaObservationVariable = entityFactory.makeDeltaObservationVariableBean(brAPIObservationVariable); + assertNotNull(deltaObservationVariable); + + // Assert that the DeltaEntity constructor cannot be called to directly instantiate + testConstructorNotAccessible(DeltaObservationVariable.class); + // Check that clone makes a correct copy + BrAPIObservationVariable clonedBrAPIObservationVariable = deltaObservationVariable.cloneEntity(); + assertNotNull(clonedBrAPIObservationVariable); + assertEquals(clonedBrAPIObservationVariable, brAPIObservationVariable); + } + + @Test + @SneakyThrows + void deltaEnvironmentTest() { + // Create BrAPIStudy + BrAPIStudy brAPIStudy = createBrAPIStudy(); + + // Use the factory to create a DeltaEnvironment from the BrAPIStudy + Environment environment = entityFactory.makeEnvironmentBean(brAPIStudy); + assertNotNull(environment); + + // Assert that the DeltaEntity constructor cannot be called to directly instantiate + testConstructorNotAccessible(Environment.class); + // Check that clone makes a correct copy + BrAPIStudy clonedBrAPIStudy = environment.cloneEntity(); + assertNotNull(clonedBrAPIStudy); + assertEquals(clonedBrAPIStudy, brAPIStudy); + } + + @Test + @SneakyThrows + void deltaExperimentTest() { + // Create BrAPITrial + BrAPITrial brAPITrial = createBrAPITrial(); + + // Use the factory to create a DeltaExperiment from the BrAPITrial + Experiment experiment = entityFactory.makeExperimentBean(brAPITrial); + assertNotNull(experiment); + + // Assert that the DeltaEntity constructor cannot be called to directly instantiate + testConstructorNotAccessible(Experiment.class); + // Check that clone makes a correct copy + BrAPITrial clonedBrAPITrial = experiment.cloneEntity(); + assertNotNull(clonedBrAPITrial); + assertEquals(clonedBrAPITrial, brAPITrial); + } + + private void testConstructorNotAccessible(Class clazz) { + Constructor[] constructors = clazz.getDeclaredConstructors(); + for (Constructor constructor : constructors) { + constructor.setAccessible(true); + Class[] parameterTypes = constructor.getParameterTypes(); + Object[] args = new Object[parameterTypes.length]; + + try { + // Attempt to create default values for each parameter + for (int i = 0; i < parameterTypes.length; i++) { + args[i] = getDefaultValue(parameterTypes[i]); + } + + // Attempt to create an instance + Object instance = constructor.newInstance(args); + fail("Expected constructor to be inaccessible, but was able to create an instance: " + instance); + } catch (IllegalAccessException e) { + // This is the expected exception for inaccessible constructor + return; + } catch (InstantiationException e) { + // This can happen if the class is abstract or an interface + log.error("Cannot instantiate " + clazz.getSimpleName() + ": " + e.getMessage()); + return; + } catch (InvocationTargetException e) { + // This can happen if the constructor throws an exception + log.error("Constructor threw an exception: " + e.getCause().getMessage()); + return; + } catch (IllegalArgumentException e) { + // This is likely due to argument type mismatch + log.error("Argument type mismatch for " + clazz.getSimpleName() + ": " + e.getMessage()); + log.error("Expected types: " + Arrays.toString(parameterTypes)); + log.error("Provided args: " + Arrays.toString(args)); + return; + } + } + fail("No constructors found or all constructors are accessible for " + clazz.getSimpleName()); + } + + private Object getDefaultValue(Class type) { + if (type.isPrimitive()) { + if (type == boolean.class) return false; + if (type == char.class) return '\u0000'; + if (type == byte.class) return (byte) 0; + if (type == short.class) return (short) 0; + if (type == int.class) return 0; + if (type == long.class) return 0L; + if (type == float.class) return 0.0f; + if (type == double.class) return 0.0d; + } + return null; // For reference types + } + private BrAPIGermplasm createBrAPIGermplasm() { + String additionalInfoString = "{\"additionalInfo\":{\"createdBy\":{\"userId\":\"101e7314-ba2c-466b-a1e0-f02409ab0d3d\",\"userName\":\"BI-DEV Admin\"},\"createdDate\":\"14/06/2024 19:17:40\",\"femaleParentUUID\":\"2927a4a5-c204-4255-850b-a1eb2c291263\",\"listEntryNumbers\":{\"fa0f1715-84b8-4ca7-8abc-ff191f221048\":\"38356\"},\"importEntryNumber\":\"38356\",\"maleParentUnknown\":false}}"; + + List donors = new ArrayList<>(); + donors.add(new BrAPIGermplasmDonors().donorAccessionNumber("abc").donorInstituteCode("institution")); + donors.add(new BrAPIGermplasmDonors().donorAccessionNumber("xyz").donorInstituteCode("institution")); + + List synonyms = new ArrayList<>(); + synonyms.add(new BrAPIGermplasmSynonyms().synonym("germA").type("synonym")); + synonyms.add(new BrAPIGermplasmSynonyms().synonym("germB").type("synonym")); + + List taxonIds = new ArrayList<>(); + taxonIds.add(new BrAPITaxonID().taxonId("testTaxon1").sourceName("test taxon source")); + taxonIds.add(new BrAPITaxonID().taxonId("testTaxon2").sourceName("test taxon source")); + + var coordinates = BrApiGeoJSON.builder().geometry(null).type("Feature").build(); + var origin = new BrAPIGermplasmOrigin().coordinates(coordinates).coordinateUncertainty("+/- 10"); + var storageType = new BrAPIGermplasmStorageTypes(BrAPIGermplasmStorageTypesEnum._10); + storageType.setDescription("storage type description"); + + return new BrAPIGermplasm() + .germplasmDbId("0d14b3ee-980c-41e3-ad7e-f9fb597e2eb6") + .accessionNumber("1") + .acquisitionDate(LocalDate.of(2020, Month.APRIL, 1)) + .additionalInfo(toJsonObject(additionalInfoString)) + .biologicalStatusOfAccessionCode(BrAPIBiologicalStatusOfAccessionCode._100) + .biologicalStatusOfAccessionDescription("biological status") + .breedingMethodDbId("50ce8bcd-5c24-4e89-b7ad-5af127a8d99b") + .breedingMethodName("biparental") + .collection("collection") + .commonCropName("Snail") + .countryOfOriginCode("USA") + .defaultDisplayName("germ 1") + .documentationURL("http://localhost") + .donors(donors) + .externalReferences(createExternalReferences()) + .genus("Helix") + .germplasmName("germ1") + .germplasmOrigin(List.of(origin)) + .germplasmPUI("germ1PUI") + .germplasmPreprocessing("germ1Preprocessing") + .instituteCode("Corn") + .instituteCode("Cornell") + .pedigree("mommy snail/daddy snail") + .seedSource("snail farm") + .seedSourceDescription("it's a snail farm") + .species("snail") + .speciesAuthority("authority") + .storageTypes(List.of(storageType)) + .subtaxa("subtaxa") + .subtaxaAuthority("subtaxa authority") + .synonyms(synonyms) + .taxonIds(taxonIds); + } + + + private ProgramLocation createProgramLocation() { + return ProgramLocation.builder() + .locationDbId("522bcd9c-2b75-4c88-89d3-5059d5ac713b") + .country(Country.builder().id(UUID.fromString("970303d1-90be-41e8-a605-7b65d29076cb")).name("USA").build()) + .accessibility(Accessibility.builder().id(UUID.fromString("1921ab25-1596-4058-8a7a-77b4c932b67f")).name("private").build()) + .environmentType(EnvironmentType.builder().id(UUID.fromString("e8dff19f-d3bc-45af-b218-a68199e39feb")).name("forest").build()) + .topography(Topography.builder().id(UUID.fromString("3841a576-0765-455d-954a-07ab350983ff")).name("hill").build()) + .build(); + } + + private BrAPILocation createBrAPILocation() { + return new BrAPILocation() + .locationDbId("522bcd9c-2b75-4c88-89d3-5059d5ac713b") + .abbreviation("f1") + .additionalInfo(toJsonObject("{\"key\":\"value\"}")) + .coordinateDescription("description") + .coordinateUncertainty("12") + .coordinates(BrApiGeoJSON.builder().geometry(null).type("Feature").build()) + .countryCode("US") + .countryName("United States") + .documentationURL("http://localhost") + .environmentType("env type") + .exposure("S") + .externalReferences(createExternalReferences()) + .instituteAddress("119 CALS Surge Facility 525 Tower Road Ithaca, NY 14853-2703") + .instituteName("Breeding Insight") + .locationName("Field 1") + .locationType("field") + .parentLocationDbId("31fd8d8c-0a6f-486a-825c-4aff4249770e") + .parentLocationName("parent field") + .siteStatus("active") + .slope("3.1") + .topography("topo"); + } + + private BrAPIObservationUnit createBrAPIObservationUnit() { + String positionJson = "{\"entryType\":\"TEST\",\"geoCoordinates\":null,\"observationLevel\":{\"levelName\":\"plot\",\"levelOrder\":0,\"levelCode\":\"1186 [SKTEST-2]\"},\"observationLevelRelationships\":[{\"levelName\":\"rep\",\"levelOrder\":null,\"levelCode\":\"3\",\"observationUnitDbId\":\"a677de20-a1cd-4982-ac71-1bc17ef08424\"},{\"levelName\":\"block\",\"levelOrder\":null,\"levelCode\":\"1\",\"observationUnitDbId\":\"a677de20-a1cd-4982-ac71-1bc17ef08424\"}],\"positionCoordinateX\":null,\"positionCoordinateXType\":null,\"positionCoordinateY\":null,\"positionCoordinateYType\":null}"; + BrAPIObservationUnitPosition position = gson.fromJson(positionJson, BrAPIObservationUnitPosition.class); + + List treatments = new ArrayList<>(); + treatments.add(new BrAPIObservationTreatment().factor("factor1").modality("modality1")); + treatments.add(new BrAPIObservationTreatment().factor("factor2").modality("modality2")); + + return new BrAPIObservationUnit() + .observationUnitDbId("0d12951f-cc68-436b-8493-060611383ef2") + .additionalInfo(toJsonObject("{\"gid\":\"94\", \"observationLevel\":\"Plot\"}")) + .externalReferences(createExternalReferences()) + .germplasmDbId("8cbbbc0f-f3e2-4f24-82e6-5315d7bd9c8c") + .germplasmName("lucky") + .locationDbId("862d3950-0184-4f2e-a729-fc2688877482") + .locationName("location") + .observationUnitName("plot 1") + .observationUnitPUI("abcdefg") + .observationUnitPosition(position) + .programDbId("65182b12-0771-4deb-b5c5-2f8d87efebbd") + .programName("Bigger Snails") + .seedLotDbId("38569c7b-140b-4ae3-8a1e-7a503d18d854") + .seedLotName("seedlot") + .studyDbId("212a90c5-bef9-40b7-9a5b-5b90749388a7") + .studyName("snail size") + .treatments(treatments) + .trialDbId("ecad3e67-3e2c-4b73-b84a-663cdda6cb2f") + .trialName("snail trial") + .observations(List.of(createBrAPIObservation())) + .crossName("mix") + .crossDbId("f1a3b726-d9e3-4e12-b59c-9d03cc3c671a"); + } + + private BrAPIObservationVariable createBrAPIObservationVariable() { + return new BrAPIObservationVariable() + .observationVariableDbId("9f362177-a30f-42e1-993f-ba905767f481") + .observationVariableName("Height") + .observationVariablePUI("xyz") + .additionalInfo(toJsonObject("{\"fullname\":\"Snail Height\"}")) + .commonCropName("Snail") + .contextOfUse(List.of("first", "second")) + .defaultValue("default") + .documentationURL("http://localhost") + .externalReferences(createExternalReferences()) + .growthStage("stage") + .institution("Cornell") + .language("en-us") + .method(createBrAPIMethod()) + .ontologyReference(createBrAPIOntologyReference()) + .scale(createBrAPIScale()) + .scientist("Scientist") + .status("active") + .submissionTimestamp(OffsetDateTime.now()) + .synonyms(List.of("n1", "n2", "n3")) + .trait(createBrAPITrait()); + } + + private BrAPIStudy createBrAPIStudy() { + return new BrAPIStudy() + .studyDbId("9d73d864-d0b4-45bd-a994-e4d2daf437fc") + .active(true) + .additionalInfo(toJsonObject("{\"environmentNumber\": \"2\"}")) + .commonCropName("Snail") + .contacts(List.of(createBrAPIContact())) + .culturalPractices("practices") + .dataLinks(List.of(createBrAPIDataLink())) + .documentationURL("http://localhost") + .endDate(OffsetDateTime.now()) + .environmentParameters(List.of(creatBrAPIEnvironmentParameter())) + .experimentalDesign(createBrAPIStudyExperimentalDesign()) + .externalReferences(createExternalReferences()) + .growthFacility(createBrAPIStudyGrowthFacility()) + .lastUpdate(createBrAPIStudyLastUpdate()) + .license("license") + .locationDbId("4abea286-a93a-44ca-812f-5420106a71c3") + .locationName("location") + .observationLevels(List.of(createBrAPIObservationUnitHierarchyLevel())) + .observationUnitsDescription("units description") + .observationVariableDbIds(List.of("67f6449b-b79f-4126-91bf-1b3903571a3f", "0684c99e-a01a-4f59-8c14-bbe3e550c8c5")) + .seasons(List.of("2021Fall", "2022Fall")) + .startDate(OffsetDateTime.now()) + .studyCode("study code") + .studyDescription("description") + .studyName("Study name") + .studyPUI("d5295e32-da72-433f-b3bd-a40cf5b93bbb") + .studyType("phenotyping trial") + .trialDbId("703cb8d8-9167-457c-8934-6d065415d312") + .trialName("Snail Trial"); + } + + private BrAPITrial createBrAPITrial() { + String additionalInfoString = "{\"datasets\":[{\"id\":\"0d9f03bf-4b0c-40e8-95b9-9ca0a107f30f\",\"name\":\"Plot\",\"level\":\"0\"}],\"createdBy\":{\"userId\":\"e3f92938-fae4-4d57-93c6-11970a8128a6\",\"userName\":\"BI-DEV Admin\"},\"createdDate\":\"2024-07-12\",\"experimentType\":\"Disease resistance screening\",\"experimentNumber\":\"1\",\"defaultObservationLevel\":\"Plot\"}"; + + return new BrAPITrial() + .trialDbId("429ff1cd-1a4a-4be7-bd6c-d10047d03230") + .active(true) + .additionalInfo(toJsonObject(additionalInfoString)) + .commonCropName("Snail") + .contacts(List.of(createBrAPIContact())) + .datasetAuthorships(List.of(createBrAPITrialDatasetAuthorship())) + .documentationURL("http://localhost") + .endDate(LocalDate.now()) + .externalReferences(createExternalReferences()) + .programDbId("401ab961-4c37-47f0-910b-0ea39648f547") + .programName("Large Program") + .publications(List.of(createBrAPITrialPublication())) + .startDate(LocalDate.now()) + .trialDescription("the description") + .trialName("Snail Trial") + .trialPUI("9bfa3dba-5195-4a08-a891-cd2b105116fa"); + + } + + private BrAPIObservation createBrAPIObservation() { + String additionalInfoString = "{\"createdBy\":{\"userId\":\"e3f92938-fae4-4d57-93c6-11970a8128a6\",\"userName\":\"BI-DEV Admin\"},\"studyName\":\"Salinas, CA 2022\",\"createdDate\":\"2024-07-12T09:44:23.34292-04:00\"}"; + + return new BrAPIObservation() + .observationDbId("192161b2-3f89-499b-8f04-7298128e9f1a") + .additionalInfo(toJsonObject(additionalInfoString)) + .collector("intern") + .externalReferences(createExternalReferences()) +// .geoCoordinates(BrApiGeoJSON.builder().geometry(null).type("Feature").build()) + .germplasmDbId("2bb19ef2-fcc5-406d-b9c3-4517c665b699") + .germplasmName("lucky") + .observationTimeStamp(OffsetDateTime.now()) + .observationUnitDbId("f067b8d4-a9ae-4fed-91a0-beb240e0c17d") + .observationUnitName("plot1") + .observationVariableDbId("a5d03d72-8bab-4ff1-b991-57c8b563f8a4") + .observationVariableName("height") + .season(new BrAPISeason().seasonDbId("704b36bc-7477-41f2-b59f-2a878e279fb8").seasonName("fall 2020").year(2020)) + .studyDbId("301caddb-a853-4f8b-9c80-7483d0444767") + .uploadedBy("uploader") + .value("45"); + } + + private BrAPITrait createBrAPITrait() { + String jsonString = "{\"additionalInfo\":null,\"externalReferences\":[{\"referenceID\":\"89c76477-51fc-401d-9ef0-aa1a8676db67\",\"referenceId\":\"89c76477-51fc-401d-9ef0-aa1a8676db67\",\"referenceSource\":\"breedinginsight.org\"}],\"alternativeAbbreviations\":[],\"attribute\":\"INSV severity mean\",\"attributePUI\":null,\"entity\":\"Plot\",\"entityPUI\":null,\"mainAbbreviation\":null,\"ontologyReference\":null,\"status\":\"active\",\"synonyms\":[\"INSVSEVW7AVE\",\"Week 7 INSV severity mean\"],\"traitClass\":null,\"traitDescription\":\"Mean INSV severity of 10 plants per plot at Week 7\",\"traitName\":\"Plot INSV severity mean [SKTEST]\",\"traitPUI\":null,\"traitDbId\":\"5d6132dd-5298-4563-a9e1-d80136347338\"}"; + return gson.fromJson(jsonString, BrAPITrait.class); + } + + private BrAPIMethod createBrAPIMethod() { + String jsonString = "{\"additionalInfo\":null,\"externalReferences\":[{\"referenceID\":\"2edb0f01-2968-4ec2-93d6-f8ed2b31f262\",\"referenceId\":\"2edb0f01-2968-4ec2-93d6-f8ed2b31f262\",\"referenceSource\":\"breedinginsight.org\"}],\"bibliographicalReference\":null,\"description\":null,\"formula\":\"Mean INSVSEVW7\",\"methodClass\":\"Computation\",\"methodName\":\"Computation [SKTEST]\",\"methodPUI\":null,\"ontologyReference\":null,\"methodDbId\":\"260ff729-74c5-4893-9b0c-097a756da882\"}"; + return gson.fromJson(jsonString, BrAPIMethod.class); + } + + private BrAPIScale createBrAPIScale() { + String jsonString = "{\"additionalInfo\":null,\"externalReferences\":[{\"referenceID\":\"a23c9b8a-6206-42e6-95ba-6c4616d3fd5b\",\"referenceId\":\"a23c9b8a-6206-42e6-95ba-6c4616d3fd5b\",\"referenceSource\":\"breedinginsight.org\"}],\"dataType\":\"Numerical\",\"decimalPlaces\":null,\"units\":\"index\",\"ontologyReference\":null,\"scaleName\":\"index [SKTEST]\",\"scalePUI\":null,\"validValues\":{\"categories\":[{\"label\":\"No visible symptoms\",\"value\":\"0\"},{\"label\":\"Slight yellowing\",\"value\":\"1\"},{\"label\":\"Necrotic spots showing\",\"value\":\"2\"},{\"label\":\"Necrotic spots on majority of leaves\",\"value\":\"3\"},{\"label\":\"Plant nearly dead from INSV, few green leaves remaining\",\"value\":\"4\"},{\"label\":\"Plant dead from INSV\",\"value\":\"5\"}],\"max\":5,\"min\":0,\"maximumValue\":\"5\",\"minimumValue\":\"0\"},\"scaleDbId\":\"cc3bcf16-f22a-4ee8-a2ff-2e2f210f5233\"}"; + return gson.fromJson(jsonString, BrAPIScale.class); + } + + private BrAPIOntologyReference createBrAPIOntologyReference() { + return new BrAPIOntologyReference() + .ontologyDbId("776000c4-45d0-46fc-b527-5d6abe67eb99") + .ontologyName("nameabc") + .version("v4") + .addDocumentationLinksItem( + new BrAPIOntologyReferenceDocumentationLinks() + .type(BrAPIOntologyReferenceTypeEnum.WEBPAGE) + .URL("http://breedinginsight.org") + ); + } + + private BrAPIDataLink createBrAPIDataLink() { + return new BrAPIDataLink() + .dataFormat("table") + .description("desc") + .fileFormat("CSV") + .name("DataName") + .provenance("provenance") + .scientificType("scitype") + .url("http://localhost:8080") + .version("v7.5.2"); + } + + private BrAPIContact createBrAPIContact() { + return new BrAPIContact() + .contactDbId("4bc0b024-3734-41a8-a90b-78873fcaa512") + .type("contact type") + .instituteName("Example") + .name("contact") + .email("contact@example.com") + .orcid("0000-1234-5678-9101"); + } + + private BrAPITrialDatasetAuthorships createBrAPITrialDatasetAuthorship() { + return new BrAPITrialDatasetAuthorships() + .datasetPUI("5a2661e9-bd26-48b6-8fa4-79e15d242286") + .license("dataset license") + .publicReleaseDate(LocalDate.now()) + .submissionDate(LocalDate.now()); + } + + private BrAPITrialPublications createBrAPITrialPublication() { + return new BrAPITrialPublications() + .publicationPUI("f8f745e7-efad-4837-b77b-c34e7392918a") + .publicationReference("breedinginsight.org"); + } + + private BrAPIEnvironmentParameter creatBrAPIEnvironmentParameter() { + return new BrAPIEnvironmentParameter() + .parameterName("parameter") + .parameterPUI("845ea09e-cd01-4b97-b7cb-40c53d2011ef") + .description("env param") + .unit("unit") + .unitPUI("490ebd7e-e99e-47be-96ec-af264cb273ac") + .value("44") + .valuePUI("b718b07f-242e-4121-8794-e65edade4a8a"); + } + + private BrAPIStudyExperimentalDesign createBrAPIStudyExperimentalDesign() { + return new BrAPIStudyExperimentalDesign() + .description("the experiment design") + .PUI("3944e242-a927-42a0-ae80-d6a666d380e1"); + } + + private BrAPIStudyGrowthFacility createBrAPIStudyGrowthFacility() { + return new BrAPIStudyGrowthFacility() + .description("the growth facility") + .PUI("9bcbea88-4d68-4f7f-93a3-c75a3fba031b"); + } + + private BrAPIStudyLastUpdate createBrAPIStudyLastUpdate() { + return new BrAPIStudyLastUpdate() + .version("4.32.5") + .timestamp(OffsetDateTime.now()); + } + + private BrAPIObservationUnitHierarchyLevel createBrAPIObservationUnitHierarchyLevel() { + return new BrAPIObservationUnitHierarchyLevel() + .levelName("top level") + .levelOrder(0); + } + + private List createExternalReferences() { + List xrefs = new ArrayList<>(); + xrefs.add(new BrAPIExternalReference().referenceSource("breedinginsight.org").referenceId("e2d530b5-184a-4ef3-8b3a-1acd80a07a00")); + xrefs.add(new BrAPIExternalReference().referenceSource("breedinginsight.org/programs").referenceId("e1d21849-6107-4be9-984f-195b029c14e0")); + return xrefs; + } + + private JsonObject toJsonObject(String s) { + return JsonParser.parseString(s).getAsJsonObject(); + } + +} From 54eb634d30459185f3f3f4b0784f552fa9cccb95 Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Fri, 27 Sep 2024 09:53:53 -0400 Subject: [PATCH 005/132] removed backup file --- .../delta/DeltaEntityFactoryUnitTest.java.bak | 552 ------------------ 1 file changed, 552 deletions(-) delete mode 100644 src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java.bak diff --git a/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java.bak b/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java.bak deleted file mode 100644 index cd8f71028..000000000 --- a/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java.bak +++ /dev/null @@ -1,552 +0,0 @@ -package org.breedinginsight.model.delta; - -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import io.micronaut.test.extensions.junit5.annotation.MicronautTest; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.brapi.v2.model.*; -import org.brapi.v2.model.core.*; -import org.brapi.v2.model.germ.*; -import org.brapi.v2.model.pheno.*; -import org.breedinginsight.DatabaseTest; -import org.breedinginsight.model.*; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; - -import javax.inject.Inject; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.time.LocalDate; -import java.time.Month; -import java.time.OffsetDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; - -import static org.breedinginsight.utilities.DatasetUtil.gson; -import static org.junit.jupiter.api.Assertions.*; - -@Slf4j -@MicronautTest -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class DeltaEntityFactoryUnitTest extends DatabaseTest { - - @Inject - private DeltaEntityFactory entityFactory; - - @Test - @SneakyThrows - void deltaGermplasmTest() { - // Create BrAPIGermplasm - BrAPIGermplasm brAPIGermplasm = createBrAPIGermplasm(); - - // Use the factory to create a DeltaGermplasm from the BrAPIGermplasm - DeltaGermplasm deltaGermplasm = entityFactory.makeDeltaGermplasmBean(brAPIGermplasm); - assertNotNull(deltaGermplasm); - - // Assert that the DeltaEntity constructor cannot be called to directly instantiate - testConstructorNotAccessible(DeltaGermplasm.class); - // Check that clone makes a correct copy - BrAPIGermplasm clonedBrAPIGermplasm = deltaGermplasm.cloneEntity(); - assertNotNull(clonedBrAPIGermplasm); - assertEquals(clonedBrAPIGermplasm, brAPIGermplasm); - } - - @Test - @SneakyThrows - void deltaLocationTest() { - // Create BrAPILocation - ProgramLocation programLocation = createProgramLocation(); - - // Use the factory to create a DeltaLocation from the BrAPILocation - DeltaLocation deltaLocation = entityFactory.makeDeltaLocationBean(programLocation); - assertNotNull(deltaLocation); - - // Assert that the DeltaEntity constructor cannot be called to directly instantiate - testConstructorNotAccessible(DeltaLocation.class); - // Check that clone makes a correct copy - ProgramLocation clonedProgramLocation = deltaLocation.cloneEntity(); - assertNotNull(clonedProgramLocation); - assertEquals(clonedProgramLocation, programLocation); - } - - @Test - @SneakyThrows - void deltaObservationTest() { - // Create BrAPIObservation - BrAPIObservation brAPIObservation = createBrAPIObservation(); - - // Use the factory to create a DeltaObservation from the BrAPIObservation - DeltaObservation deltaObservation = entityFactory.makeDeltaObservationBean(brAPIObservation); - assertNotNull(deltaObservation); - - // Assert that the DeltaEntity constructor cannot be called to directly instantiate - testConstructorNotAccessible(DeltaObservation.class); - // Check that clone makes a correct copy - BrAPIObservation clonedBrAPIObservation = deltaObservation.cloneEntity(); - assertNotNull(clonedBrAPIObservation); - assertEquals(clonedBrAPIObservation, brAPIObservation); - } - - @Test - @SneakyThrows - void deltaObservationUnitTest() { - // Create BrAPIObservationUnit - BrAPIObservationUnit brAPIObservationUnit = createBrAPIObservationUnit(); - - // Use the factory to create a DeltaObservationUnit from the BrAPIObservationUnit - DeltaObservationUnit deltaObservationUnit = entityFactory.makeDeltaObservationUnitBean(brAPIObservationUnit); - assertNotNull(deltaObservationUnit); - - // Assert that the DeltaEntity constructor cannot be called to directly instantiate - testConstructorNotAccessible(DeltaObservationUnit.class); - - // Check that clone makes a correct copy - BrAPIObservationUnit clonedBrAPIObservationUnit = deltaObservationUnit.cloneEntity(); - assertNotNull(clonedBrAPIObservationUnit); - assertEquals(clonedBrAPIObservationUnit, brAPIObservationUnit); - } - - @Test - @SneakyThrows - void deltaObservationVariableTest() { - // Create BrAPIObservationVariable - BrAPIObservationVariable brAPIObservationVariable = createBrAPIObservationVariable(); - - // Use the factory to create a DeltaObservationVariable from the BrAPIObservationVariable - DeltaObservationVariable deltaObservationVariable = entityFactory.makeDeltaObservationVariableBean(brAPIObservationVariable); - assertNotNull(deltaObservationVariable); - - // Assert that the DeltaEntity constructor cannot be called to directly instantiate - testConstructorNotAccessible(DeltaObservationVariable.class); - // Check that clone makes a correct copy - BrAPIObservationVariable clonedBrAPIObservationVariable = deltaObservationVariable.cloneEntity(); - assertNotNull(clonedBrAPIObservationVariable); - assertEquals(clonedBrAPIObservationVariable, brAPIObservationVariable); - } - - @Test - @SneakyThrows - void deltaEnvironmentTest() { - // Create BrAPIStudy - BrAPIStudy brAPIStudy = createBrAPIStudy(); - - // Use the factory to create a DeltaEnvironment from the BrAPIStudy - Environment environment = entityFactory.makeEnvironmentBean(brAPIStudy); - assertNotNull(environment); - - // Assert that the DeltaEntity constructor cannot be called to directly instantiate - testConstructorNotAccessible(Environment.class); - // Check that clone makes a correct copy - BrAPIStudy clonedBrAPIStudy = environment.cloneEntity(); - assertNotNull(clonedBrAPIStudy); - assertEquals(clonedBrAPIStudy, brAPIStudy); - } - - @Test - @SneakyThrows - void deltaExperimentTest() { - // Create BrAPITrial - BrAPITrial brAPITrial = createBrAPITrial(); - - // Use the factory to create a DeltaExperiment from the BrAPITrial - Experiment experiment = entityFactory.makeExperimentBean(brAPITrial); - assertNotNull(experiment); - - // Assert that the DeltaEntity constructor cannot be called to directly instantiate - testConstructorNotAccessible(Experiment.class); - // Check that clone makes a correct copy - BrAPITrial clonedBrAPITrial = experiment.cloneEntity(); - assertNotNull(clonedBrAPITrial); - assertEquals(clonedBrAPITrial, brAPITrial); - } - - private void testConstructorNotAccessible(Class clazz) { - Constructor[] constructors = clazz.getDeclaredConstructors(); - for (Constructor constructor : constructors) { - constructor.setAccessible(true); - Class[] parameterTypes = constructor.getParameterTypes(); - Object[] args = new Object[parameterTypes.length]; - - try { - // Attempt to create default values for each parameter - for (int i = 0; i < parameterTypes.length; i++) { - args[i] = getDefaultValue(parameterTypes[i]); - } - - // Attempt to create an instance - Object instance = constructor.newInstance(args); - fail("Expected constructor to be inaccessible, but was able to create an instance: " + instance); - } catch (IllegalAccessException e) { - // This is the expected exception for inaccessible constructor - return; - } catch (InstantiationException e) { - // This can happen if the class is abstract or an interface - log.error("Cannot instantiate " + clazz.getSimpleName() + ": " + e.getMessage()); - return; - } catch (InvocationTargetException e) { - // This can happen if the constructor throws an exception - log.error("Constructor threw an exception: " + e.getCause().getMessage()); - return; - } catch (IllegalArgumentException e) { - // This is likely due to argument type mismatch - log.error("Argument type mismatch for " + clazz.getSimpleName() + ": " + e.getMessage()); - log.error("Expected types: " + Arrays.toString(parameterTypes)); - log.error("Provided args: " + Arrays.toString(args)); - return; - } - } - fail("No constructors found or all constructors are accessible for " + clazz.getSimpleName()); - } - - private Object getDefaultValue(Class type) { - if (type.isPrimitive()) { - if (type == boolean.class) return false; - if (type == char.class) return '\u0000'; - if (type == byte.class) return (byte) 0; - if (type == short.class) return (short) 0; - if (type == int.class) return 0; - if (type == long.class) return 0L; - if (type == float.class) return 0.0f; - if (type == double.class) return 0.0d; - } - return null; // For reference types - } - private BrAPIGermplasm createBrAPIGermplasm() { - String additionalInfoString = "{\"additionalInfo\":{\"createdBy\":{\"userId\":\"101e7314-ba2c-466b-a1e0-f02409ab0d3d\",\"userName\":\"BI-DEV Admin\"},\"createdDate\":\"14/06/2024 19:17:40\",\"femaleParentUUID\":\"2927a4a5-c204-4255-850b-a1eb2c291263\",\"listEntryNumbers\":{\"fa0f1715-84b8-4ca7-8abc-ff191f221048\":\"38356\"},\"importEntryNumber\":\"38356\",\"maleParentUnknown\":false}}"; - - List donors = new ArrayList<>(); - donors.add(new BrAPIGermplasmDonors().donorAccessionNumber("abc").donorInstituteCode("institution")); - donors.add(new BrAPIGermplasmDonors().donorAccessionNumber("xyz").donorInstituteCode("institution")); - - List synonyms = new ArrayList<>(); - synonyms.add(new BrAPIGermplasmSynonyms().synonym("germA").type("synonym")); - synonyms.add(new BrAPIGermplasmSynonyms().synonym("germB").type("synonym")); - - List taxonIds = new ArrayList<>(); - taxonIds.add(new BrAPITaxonID().taxonId("testTaxon1").sourceName("test taxon source")); - taxonIds.add(new BrAPITaxonID().taxonId("testTaxon2").sourceName("test taxon source")); - - var coordinates = BrApiGeoJSON.builder().geometry(null).type("Feature").build(); - var origin = new BrAPIGermplasmOrigin().coordinates(coordinates).coordinateUncertainty("+/- 10"); - var storageType = new BrAPIGermplasmStorageTypes(BrAPIGermplasmStorageTypesEnum._10); - storageType.setDescription("storage type description"); - - return new BrAPIGermplasm() - .germplasmDbId("0d14b3ee-980c-41e3-ad7e-f9fb597e2eb6") - .accessionNumber("1") - .acquisitionDate(LocalDate.of(2020, Month.APRIL, 1)) - .additionalInfo(toJsonObject(additionalInfoString)) - .biologicalStatusOfAccessionCode(BrAPIBiologicalStatusOfAccessionCode._100) - .biologicalStatusOfAccessionDescription("biological status") - .breedingMethodDbId("50ce8bcd-5c24-4e89-b7ad-5af127a8d99b") - .breedingMethodName("biparental") - .collection("collection") - .commonCropName("Snail") - .countryOfOriginCode("USA") - .defaultDisplayName("germ 1") - .documentationURL("http://localhost") - .donors(donors) - .externalReferences(createExternalReferences()) - .genus("Helix") - .germplasmName("germ1") - .germplasmOrigin(List.of(origin)) - .germplasmPUI("germ1PUI") - .germplasmPreprocessing("germ1Preprocessing") - .instituteCode("Corn") - .instituteCode("Cornell") - .pedigree("mommy snail/daddy snail") - .seedSource("snail farm") - .seedSourceDescription("it's a snail farm") - .species("snail") - .speciesAuthority("authority") - .storageTypes(List.of(storageType)) - .subtaxa("subtaxa") - .subtaxaAuthority("subtaxa authority") - .synonyms(synonyms) - .taxonIds(taxonIds); - } - - - private ProgramLocation createProgramLocation() { - return ProgramLocation.builder() - .locationDbId("522bcd9c-2b75-4c88-89d3-5059d5ac713b") - .country(Country.builder().id(UUID.fromString("970303d1-90be-41e8-a605-7b65d29076cb")).name("USA").build()) - .accessibility(Accessibility.builder().id(UUID.fromString("1921ab25-1596-4058-8a7a-77b4c932b67f")).name("private").build()) - .environmentType(EnvironmentType.builder().id(UUID.fromString("e8dff19f-d3bc-45af-b218-a68199e39feb")).name("forest").build()) - .topography(Topography.builder().id(UUID.fromString("3841a576-0765-455d-954a-07ab350983ff")).name("hill").build()) - .build(); - } - - private BrAPILocation createBrAPILocation() { - return new BrAPILocation() - .locationDbId("522bcd9c-2b75-4c88-89d3-5059d5ac713b") - .abbreviation("f1") - .additionalInfo(toJsonObject("{\"key\":\"value\"}")) - .coordinateDescription("description") - .coordinateUncertainty("12") - .coordinates(BrApiGeoJSON.builder().geometry(null).type("Feature").build()) - .countryCode("US") - .countryName("United States") - .documentationURL("http://localhost") - .environmentType("env type") - .exposure("S") - .externalReferences(createExternalReferences()) - .instituteAddress("119 CALS Surge Facility 525 Tower Road Ithaca, NY 14853-2703") - .instituteName("Breeding Insight") - .locationName("Field 1") - .locationType("field") - .parentLocationDbId("31fd8d8c-0a6f-486a-825c-4aff4249770e") - .parentLocationName("parent field") - .siteStatus("active") - .slope("3.1") - .topography("topo"); - } - - private BrAPIObservationUnit createBrAPIObservationUnit() { - String positionJson = "{\"entryType\":\"TEST\",\"geoCoordinates\":null,\"observationLevel\":{\"levelName\":\"plot\",\"levelOrder\":0,\"levelCode\":\"1186 [SKTEST-2]\"},\"observationLevelRelationships\":[{\"levelName\":\"rep\",\"levelOrder\":null,\"levelCode\":\"3\",\"observationUnitDbId\":\"a677de20-a1cd-4982-ac71-1bc17ef08424\"},{\"levelName\":\"block\",\"levelOrder\":null,\"levelCode\":\"1\",\"observationUnitDbId\":\"a677de20-a1cd-4982-ac71-1bc17ef08424\"}],\"positionCoordinateX\":null,\"positionCoordinateXType\":null,\"positionCoordinateY\":null,\"positionCoordinateYType\":null}"; - BrAPIObservationUnitPosition position = gson.fromJson(positionJson, BrAPIObservationUnitPosition.class); - - List treatments = new ArrayList<>(); - treatments.add(new BrAPIObservationTreatment().factor("factor1").modality("modality1")); - treatments.add(new BrAPIObservationTreatment().factor("factor2").modality("modality2")); - - return new BrAPIObservationUnit() - .observationUnitDbId("0d12951f-cc68-436b-8493-060611383ef2") - .additionalInfo(toJsonObject("{\"gid\":\"94\", \"observationLevel\":\"Plot\"}")) - .externalReferences(createExternalReferences()) - .germplasmDbId("8cbbbc0f-f3e2-4f24-82e6-5315d7bd9c8c") - .germplasmName("lucky") - .locationDbId("862d3950-0184-4f2e-a729-fc2688877482") - .locationName("location") - .observationUnitName("plot 1") - .observationUnitPUI("abcdefg") - .observationUnitPosition(position) - .programDbId("65182b12-0771-4deb-b5c5-2f8d87efebbd") - .programName("Bigger Snails") - .seedLotDbId("38569c7b-140b-4ae3-8a1e-7a503d18d854") - .seedLotName("seedlot") - .studyDbId("212a90c5-bef9-40b7-9a5b-5b90749388a7") - .studyName("snail size") - .treatments(treatments) - .trialDbId("ecad3e67-3e2c-4b73-b84a-663cdda6cb2f") - .trialName("snail trial") - .observations(List.of(createBrAPIObservation())) - .crossName("mix") - .crossDbId("f1a3b726-d9e3-4e12-b59c-9d03cc3c671a"); - } - - private BrAPIObservationVariable createBrAPIObservationVariable() { - return new BrAPIObservationVariable() - .observationVariableDbId("9f362177-a30f-42e1-993f-ba905767f481") - .observationVariableName("Height") - .observationVariablePUI("xyz") - .additionalInfo(toJsonObject("{\"fullname\":\"Snail Height\"}")) - .commonCropName("Snail") - .contextOfUse(List.of("first", "second")) - .defaultValue("default") - .documentationURL("http://localhost") - .externalReferences(createExternalReferences()) - .growthStage("stage") - .institution("Cornell") - .language("en-us") - .method(createBrAPIMethod()) - .ontologyReference(createBrAPIOntologyReference()) - .scale(createBrAPIScale()) - .scientist("Scientist") - .status("active") - .submissionTimestamp(OffsetDateTime.now()) - .synonyms(List.of("n1", "n2", "n3")) - .trait(createBrAPITrait()); - } - - private BrAPIStudy createBrAPIStudy() { - return new BrAPIStudy() - .studyDbId("9d73d864-d0b4-45bd-a994-e4d2daf437fc") - .active(true) - .additionalInfo(toJsonObject("{\"environmentNumber\": \"2\"}")) - .commonCropName("Snail") - .contacts(List.of(createBrAPIContact())) - .culturalPractices("practices") - .dataLinks(List.of(createBrAPIDataLink())) - .documentationURL("http://localhost") - .endDate(OffsetDateTime.now()) - .environmentParameters(List.of(creatBrAPIEnvironmentParameter())) - .experimentalDesign(createBrAPIStudyExperimentalDesign()) - .externalReferences(createExternalReferences()) - .growthFacility(createBrAPIStudyGrowthFacility()) - .lastUpdate(createBrAPIStudyLastUpdate()) - .license("license") - .locationDbId("4abea286-a93a-44ca-812f-5420106a71c3") - .locationName("location") - .observationLevels(List.of(createBrAPIObservationUnitHierarchyLevel())) - .observationUnitsDescription("units description") - .observationVariableDbIds(List.of("67f6449b-b79f-4126-91bf-1b3903571a3f", "0684c99e-a01a-4f59-8c14-bbe3e550c8c5")) - .seasons(List.of("2021Fall", "2022Fall")) - .startDate(OffsetDateTime.now()) - .studyCode("study code") - .studyDescription("description") - .studyName("Study name") - .studyPUI("d5295e32-da72-433f-b3bd-a40cf5b93bbb") - .studyType("phenotyping trial") - .trialDbId("703cb8d8-9167-457c-8934-6d065415d312") - .trialName("Snail Trial"); - } - - private BrAPITrial createBrAPITrial() { - String additionalInfoString = "{\"datasets\":[{\"id\":\"0d9f03bf-4b0c-40e8-95b9-9ca0a107f30f\",\"name\":\"Plot\",\"level\":\"0\"}],\"createdBy\":{\"userId\":\"e3f92938-fae4-4d57-93c6-11970a8128a6\",\"userName\":\"BI-DEV Admin\"},\"createdDate\":\"2024-07-12\",\"experimentType\":\"Disease resistance screening\",\"experimentNumber\":\"1\",\"defaultObservationLevel\":\"Plot\"}"; - - return new BrAPITrial() - .trialDbId("429ff1cd-1a4a-4be7-bd6c-d10047d03230") - .active(true) - .additionalInfo(toJsonObject(additionalInfoString)) - .commonCropName("Snail") - .contacts(List.of(createBrAPIContact())) - .datasetAuthorships(List.of(createBrAPITrialDatasetAuthorship())) - .documentationURL("http://localhost") - .endDate(LocalDate.now()) - .externalReferences(createExternalReferences()) - .programDbId("401ab961-4c37-47f0-910b-0ea39648f547") - .programName("Large Program") - .publications(List.of(createBrAPITrialPublication())) - .startDate(LocalDate.now()) - .trialDescription("the description") - .trialName("Snail Trial") - .trialPUI("9bfa3dba-5195-4a08-a891-cd2b105116fa"); - - } - - private BrAPIObservation createBrAPIObservation() { - String additionalInfoString = "{\"createdBy\":{\"userId\":\"e3f92938-fae4-4d57-93c6-11970a8128a6\",\"userName\":\"BI-DEV Admin\"},\"studyName\":\"Salinas, CA 2022\",\"createdDate\":\"2024-07-12T09:44:23.34292-04:00\"}"; - - return new BrAPIObservation() - .observationDbId("192161b2-3f89-499b-8f04-7298128e9f1a") - .additionalInfo(toJsonObject(additionalInfoString)) - .collector("intern") - .externalReferences(createExternalReferences()) -// .geoCoordinates(BrApiGeoJSON.builder().geometry(null).type("Feature").build()) - .germplasmDbId("2bb19ef2-fcc5-406d-b9c3-4517c665b699") - .germplasmName("lucky") - .observationTimeStamp(OffsetDateTime.now()) - .observationUnitDbId("f067b8d4-a9ae-4fed-91a0-beb240e0c17d") - .observationUnitName("plot1") - .observationVariableDbId("a5d03d72-8bab-4ff1-b991-57c8b563f8a4") - .observationVariableName("height") - .season(new BrAPISeason().seasonDbId("704b36bc-7477-41f2-b59f-2a878e279fb8").seasonName("fall 2020").year(2020)) - .studyDbId("301caddb-a853-4f8b-9c80-7483d0444767") - .uploadedBy("uploader") - .value("45"); - } - - private BrAPITrait createBrAPITrait() { - String jsonString = "{\"additionalInfo\":null,\"externalReferences\":[{\"referenceID\":\"89c76477-51fc-401d-9ef0-aa1a8676db67\",\"referenceId\":\"89c76477-51fc-401d-9ef0-aa1a8676db67\",\"referenceSource\":\"breedinginsight.org\"}],\"alternativeAbbreviations\":[],\"attribute\":\"INSV severity mean\",\"attributePUI\":null,\"entity\":\"Plot\",\"entityPUI\":null,\"mainAbbreviation\":null,\"ontologyReference\":null,\"status\":\"active\",\"synonyms\":[\"INSVSEVW7AVE\",\"Week 7 INSV severity mean\"],\"traitClass\":null,\"traitDescription\":\"Mean INSV severity of 10 plants per plot at Week 7\",\"traitName\":\"Plot INSV severity mean [SKTEST]\",\"traitPUI\":null,\"traitDbId\":\"5d6132dd-5298-4563-a9e1-d80136347338\"}"; - return gson.fromJson(jsonString, BrAPITrait.class); - } - - private BrAPIMethod createBrAPIMethod() { - String jsonString = "{\"additionalInfo\":null,\"externalReferences\":[{\"referenceID\":\"2edb0f01-2968-4ec2-93d6-f8ed2b31f262\",\"referenceId\":\"2edb0f01-2968-4ec2-93d6-f8ed2b31f262\",\"referenceSource\":\"breedinginsight.org\"}],\"bibliographicalReference\":null,\"description\":null,\"formula\":\"Mean INSVSEVW7\",\"methodClass\":\"Computation\",\"methodName\":\"Computation [SKTEST]\",\"methodPUI\":null,\"ontologyReference\":null,\"methodDbId\":\"260ff729-74c5-4893-9b0c-097a756da882\"}"; - return gson.fromJson(jsonString, BrAPIMethod.class); - } - - private BrAPIScale createBrAPIScale() { - String jsonString = "{\"additionalInfo\":null,\"externalReferences\":[{\"referenceID\":\"a23c9b8a-6206-42e6-95ba-6c4616d3fd5b\",\"referenceId\":\"a23c9b8a-6206-42e6-95ba-6c4616d3fd5b\",\"referenceSource\":\"breedinginsight.org\"}],\"dataType\":\"Numerical\",\"decimalPlaces\":null,\"units\":\"index\",\"ontologyReference\":null,\"scaleName\":\"index [SKTEST]\",\"scalePUI\":null,\"validValues\":{\"categories\":[{\"label\":\"No visible symptoms\",\"value\":\"0\"},{\"label\":\"Slight yellowing\",\"value\":\"1\"},{\"label\":\"Necrotic spots showing\",\"value\":\"2\"},{\"label\":\"Necrotic spots on majority of leaves\",\"value\":\"3\"},{\"label\":\"Plant nearly dead from INSV, few green leaves remaining\",\"value\":\"4\"},{\"label\":\"Plant dead from INSV\",\"value\":\"5\"}],\"max\":5,\"min\":0,\"maximumValue\":\"5\",\"minimumValue\":\"0\"},\"scaleDbId\":\"cc3bcf16-f22a-4ee8-a2ff-2e2f210f5233\"}"; - return gson.fromJson(jsonString, BrAPIScale.class); - } - - private BrAPIOntologyReference createBrAPIOntologyReference() { - return new BrAPIOntologyReference() - .ontologyDbId("776000c4-45d0-46fc-b527-5d6abe67eb99") - .ontologyName("nameabc") - .version("v4") - .addDocumentationLinksItem( - new BrAPIOntologyReferenceDocumentationLinks() - .type(BrAPIOntologyReferenceTypeEnum.WEBPAGE) - .URL("http://breedinginsight.org") - ); - } - - private BrAPIDataLink createBrAPIDataLink() { - return new BrAPIDataLink() - .dataFormat("table") - .description("desc") - .fileFormat("CSV") - .name("DataName") - .provenance("provenance") - .scientificType("scitype") - .url("http://localhost:8080") - .version("v7.5.2"); - } - - private BrAPIContact createBrAPIContact() { - return new BrAPIContact() - .contactDbId("4bc0b024-3734-41a8-a90b-78873fcaa512") - .type("contact type") - .instituteName("Example") - .name("contact") - .email("contact@example.com") - .orcid("0000-1234-5678-9101"); - } - - private BrAPITrialDatasetAuthorships createBrAPITrialDatasetAuthorship() { - return new BrAPITrialDatasetAuthorships() - .datasetPUI("5a2661e9-bd26-48b6-8fa4-79e15d242286") - .license("dataset license") - .publicReleaseDate(LocalDate.now()) - .submissionDate(LocalDate.now()); - } - - private BrAPITrialPublications createBrAPITrialPublication() { - return new BrAPITrialPublications() - .publicationPUI("f8f745e7-efad-4837-b77b-c34e7392918a") - .publicationReference("breedinginsight.org"); - } - - private BrAPIEnvironmentParameter creatBrAPIEnvironmentParameter() { - return new BrAPIEnvironmentParameter() - .parameterName("parameter") - .parameterPUI("845ea09e-cd01-4b97-b7cb-40c53d2011ef") - .description("env param") - .unit("unit") - .unitPUI("490ebd7e-e99e-47be-96ec-af264cb273ac") - .value("44") - .valuePUI("b718b07f-242e-4121-8794-e65edade4a8a"); - } - - private BrAPIStudyExperimentalDesign createBrAPIStudyExperimentalDesign() { - return new BrAPIStudyExperimentalDesign() - .description("the experiment design") - .PUI("3944e242-a927-42a0-ae80-d6a666d380e1"); - } - - private BrAPIStudyGrowthFacility createBrAPIStudyGrowthFacility() { - return new BrAPIStudyGrowthFacility() - .description("the growth facility") - .PUI("9bcbea88-4d68-4f7f-93a3-c75a3fba031b"); - } - - private BrAPIStudyLastUpdate createBrAPIStudyLastUpdate() { - return new BrAPIStudyLastUpdate() - .version("4.32.5") - .timestamp(OffsetDateTime.now()); - } - - private BrAPIObservationUnitHierarchyLevel createBrAPIObservationUnitHierarchyLevel() { - return new BrAPIObservationUnitHierarchyLevel() - .levelName("top level") - .levelOrder(0); - } - - private List createExternalReferences() { - List xrefs = new ArrayList<>(); - xrefs.add(new BrAPIExternalReference().referenceSource("breedinginsight.org").referenceId("e2d530b5-184a-4ef3-8b3a-1acd80a07a00")); - xrefs.add(new BrAPIExternalReference().referenceSource("breedinginsight.org/programs").referenceId("e1d21849-6107-4be9-984f-195b029c14e0")); - return xrefs; - } - - private JsonObject toJsonObject(String s) { - return JsonParser.parseString(s).getAsJsonObject(); - } - -} From bcc25c44f536d3218681653b2808c02d148715ba Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Fri, 27 Sep 2024 13:17:44 -0400 Subject: [PATCH 006/132] [BI-2203] modified .env.template to include ORCID_SANDBOX_AUTHENTICATION --- .env.template | 2 ++ .../experiment/services/ExperimentPhenotypeService.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.env.template b/.env.template index 407b99d50..66a62fb5d 100644 --- a/.env.template +++ b/.env.template @@ -3,6 +3,8 @@ USER_ID= GROUP_ID= +ORCID_SANDBOX_AUTHENTICATION=use the Sandbox Orcid, false=>use the Production Orcid. Defaults to false.> + # Authentication variables OAUTH_CLIENT_ID= OAUTH_CLIENT_SECRET= diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/services/ExperimentPhenotypeService.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/services/ExperimentPhenotypeService.java index 32bc8c348..ce744234d 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/services/ExperimentPhenotypeService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/services/ExperimentPhenotypeService.java @@ -56,7 +56,7 @@ public ProcessedPhenotypeData extractPhenotypes(ImportContext importContext) { Table data = importContext.getData(); ImportUpload upload = importContext.getUpload(); Program program = importContext.getProgram(); - String[] cols = upload.getDynamicColumnNames(); + DynamicColumnParser.DynamicColumnParseResult result = DynamicColumnParser.parse(data, upload.getDynamicColumnNames()); List traits = experimentValidateService.verifyTraits(program.getId(), result); From bcd19fa7480e4b079a485476f11040910e36c760 Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Mon, 30 Sep 2024 15:41:55 -0400 Subject: [PATCH 007/132] [BI-2203] delete the user 'Chris Tucker' when it has a NULL email --- .../db/migration/V1_31_0__Set_Dev_Admin_Email.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java b/src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java index c8bd1697d..1c7dadc87 100644 --- a/src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java +++ b/src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java @@ -42,6 +42,7 @@ public void migrate(Context context) throws Exception { Map placeholders = context.getConfiguration().getPlaceholders(); boolean isOrcidSandboxAuthentication = Boolean.parseBoolean( placeholders.get(ORCID_SANDBOX_AUTHENTICATION) ); updateDevAdminUser(context, isOrcidSandboxAuthentication); + deleteUserChris(context); } private void updateDevAdminUser(Context context, boolean isOrcidSandboxAuthentication) throws SQLException { @@ -53,4 +54,12 @@ private void updateDevAdminUser(Context context, boolean isOrcidSandboxAuthentic update.executeUpdate(sql); } } + + private void deleteUserChris(Context context) throws SQLException { + try (Statement delete = context.getConnection().createStatement()) { + String sql = "DELETE FROM bi_user WHERE name = 'Chris Tucker' AND email IS NULL"; + log.debug(sql); + delete.executeUpdate(sql); + } + } } \ No newline at end of file From 50f67df143b2c8dc38f872c6a56be79dd4db2492 Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Tue, 1 Oct 2024 13:06:09 -0400 Subject: [PATCH 008/132] [BI-2203]Moved logic from SQL migration to Java migration, to insure delete happens before adding constraint --- .../V1_31_0__Set_Dev_Admin_Email.java | 22 +++++++++++++++ .../V1.31.1__set_email_orcid_constraint.sql | 28 ------------------- 2 files changed, 22 insertions(+), 28 deletions(-) delete mode 100644 src/main/resources/db/migration/V1.31.1__set_email_orcid_constraint.sql diff --git a/src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java b/src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java index 1c7dadc87..36b47b302 100644 --- a/src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java +++ b/src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java @@ -42,7 +42,11 @@ public void migrate(Context context) throws Exception { Map placeholders = context.getConfiguration().getPlaceholders(); boolean isOrcidSandboxAuthentication = Boolean.parseBoolean( placeholders.get(ORCID_SANDBOX_AUTHENTICATION) ); updateDevAdminUser(context, isOrcidSandboxAuthentication); + //Must delete user "Chris Tucker" before adding the new Constraint (User "Chris Tucker" is added by migration V0.5.2__populate-user-data.sql. + // It would violate the new constraint) + // PS -- We miss Chris Tucker. deleteUserChris(context); + addConstraint(context); } private void updateDevAdminUser(Context context, boolean isOrcidSandboxAuthentication) throws SQLException { @@ -62,4 +66,22 @@ private void deleteUserChris(Context context) throws SQLException { delete.executeUpdate(sql); } } + + private void addConstraint(Context context) throws SQLException { + final String CONSTRAINT_NAME = "email_orcid"; + // First, drop the constraint if it already exist. + try (Statement altTable = context.getConnection().createStatement()) { + String sql = "ALTER TABLE bi_user DROP CONSTRAINT IF EXISTS "+ CONSTRAINT_NAME; + log.debug(sql); + altTable.executeUpdate(sql); + } + + // Add new constraint + try (Statement altTable = context.getConnection().createStatement()) { + String sql = "ALTER TABLE bi_user\n" + + "ADD CONSTRAINT " +CONSTRAINT_NAME+ " CHECK ( (email IS NOT NULL ) OR (orcid IS NULL) ) ;"; + log.debug(sql); + altTable.executeUpdate(sql); + } + } } \ No newline at end of file diff --git a/src/main/resources/db/migration/V1.31.1__set_email_orcid_constraint.sql b/src/main/resources/db/migration/V1.31.1__set_email_orcid_constraint.sql deleted file mode 100644 index bd465ee4b..000000000 --- a/src/main/resources/db/migration/V1.31.1__set_email_orcid_constraint.sql +++ /dev/null @@ -1,28 +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. - */ - -/* - From https://breedinginsight.atlassian.net/jira/software/c/projects/BI/boards/1?selectedIssue=BI-2203 - - ...constraint should be added to the bi_user table that prevents the orcid id from being non-null if the email is null. - The reverse (non-null email and null orcid) must still be allowed since it is the state for a user pending verification. - */ - -ALTER TABLE bi_user DROP CONSTRAINT IF EXISTS email_orcid; - -ALTER TABLE bi_user -ADD CONSTRAINT email_orcid CHECK ( (email IS NOT NULL ) OR (orcid IS NULL) ) ; \ No newline at end of file From 43ac16b22c3a01155cf00471d0e49bbca91e7671 Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Thu, 10 Oct 2024 14:59:39 -0400 Subject: [PATCH 009/132] [BI-2203] fixed unit test to deal with one less user --- .../v1/controller/metadata/MetadataFilterIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/breedinginsight/api/v1/controller/metadata/MetadataFilterIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/metadata/MetadataFilterIntegrationTest.java index 2d56ffb3b..4257f90d6 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/metadata/MetadataFilterIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/metadata/MetadataFilterIntegrationTest.java @@ -132,7 +132,7 @@ public void getDataResponseMetadataFilterSuccess() { // Check our page numbers weren't altered by the filtered assertEquals(1, data.getAsJsonPrimitive("totalPages").getAsInt(), "Default total pages is incorrect"); - assertEquals(38, data.getAsJsonPrimitive("totalCount").getAsInt(), "Default total count is incorrect"); + assertEquals(37, data.getAsJsonPrimitive("totalCount").getAsInt(), "Default total count is incorrect"); assertEquals(50, data.getAsJsonPrimitive("pageSize").getAsInt(), "Default page size is incorrect"); assertEquals(1, data.getAsJsonPrimitive("currentPage").getAsInt(), "Default current page is incorrect"); } From 5976aabfe28a9f87027f4b2437c6f150e4d8ade6 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Thu, 10 Oct 2024 20:23:04 +0000 Subject: [PATCH 010/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 419e1b1df..f8c7a5f73 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -14,5 +14,5 @@ # limitations under the License. # -version=v1.1.0+839 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/7d237259f0eae02cced1d0a02b690a514d37de4e +version=v1.1.0+842 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/011f00611846b917e5f9a1521308e06dc127873b From a761163cfd9f9490c679eb43276e93e7911a5d0c Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Wed, 9 Oct 2024 11:53:32 -0400 Subject: [PATCH 011/132] [BI-2328] Added new validation for periods in a trait name --- .../validators/TraitFileValidatorError.java | 5 ++++ .../validators/TraitValidatorError.java | 4 ++++ .../TraitValidatorErrorInterface.java | 1 + .../validators/TraitValidatorService.java | 21 ++++++++++++++++- .../delta/DeltaEntityFactoryUnitTest.java | 1 - .../validators/TraitValidatorUnitTest.java | 23 +++++++++++++++++++ 6 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/breedinginsight/services/validators/TraitFileValidatorError.java b/src/main/java/org/breedinginsight/services/validators/TraitFileValidatorError.java index 4ba0f99d7..40807cf03 100644 --- a/src/main/java/org/breedinginsight/services/validators/TraitFileValidatorError.java +++ b/src/main/java/org/breedinginsight/services/validators/TraitFileValidatorError.java @@ -65,6 +65,11 @@ public ValidationError getMissingScaleDataTypeMsg() { return new ValidationError("Scale Class", "Missing scale class", HttpStatus.UNPROCESSABLE_ENTITY); } + @Override + public ValidationError getPeriodObsVarNameMsg() { + return new ValidationError("Name", "Period is invalid", HttpStatus.UNPROCESSABLE_ENTITY); + } + @Override public ValidationError getMissingObsVarNameMsg() { return new ValidationError("Name", "Missing name", HttpStatus.UNPROCESSABLE_ENTITY); diff --git a/src/main/java/org/breedinginsight/services/validators/TraitValidatorError.java b/src/main/java/org/breedinginsight/services/validators/TraitValidatorError.java index b2db156f6..c9765ed82 100644 --- a/src/main/java/org/breedinginsight/services/validators/TraitValidatorError.java +++ b/src/main/java/org/breedinginsight/services/validators/TraitValidatorError.java @@ -66,6 +66,10 @@ public ValidationError getMissingScaleDataTypeMsg() { } @Override + public ValidationError getPeriodObsVarNameMsg() { + return new ValidationError("observationVariableName", "Period in name is invalid", HttpStatus.BAD_REQUEST); + } + public ValidationError getMissingObsVarNameMsg() { return new ValidationError("observationVariableName", "Missing Name", HttpStatus.BAD_REQUEST); } diff --git a/src/main/java/org/breedinginsight/services/validators/TraitValidatorErrorInterface.java b/src/main/java/org/breedinginsight/services/validators/TraitValidatorErrorInterface.java index 49ef88da5..27400e2c5 100644 --- a/src/main/java/org/breedinginsight/services/validators/TraitValidatorErrorInterface.java +++ b/src/main/java/org/breedinginsight/services/validators/TraitValidatorErrorInterface.java @@ -31,6 +31,7 @@ public interface TraitValidatorErrorInterface { ValidationError getMissingScaleUnitMsg(); ValidationError getMissingScaleDataTypeMsg(); ValidationError getMissingObsVarNameMsg(); + ValidationError getPeriodObsVarNameMsg(); ValidationError getMissingTraitEntityMsg(); ValidationError getMissingTraitAttributeMsg(); ValidationError getMissingTraitDescriptionMsg(); diff --git a/src/main/java/org/breedinginsight/services/validators/TraitValidatorService.java b/src/main/java/org/breedinginsight/services/validators/TraitValidatorService.java index e778898bd..de88755d2 100644 --- a/src/main/java/org/breedinginsight/services/validators/TraitValidatorService.java +++ b/src/main/java/org/breedinginsight/services/validators/TraitValidatorService.java @@ -16,6 +16,7 @@ */ package org.breedinginsight.services.validators; +import io.micronaut.http.HttpStatus; import org.breedinginsight.api.model.v1.response.ValidationError; import org.breedinginsight.api.model.v1.response.ValidationErrors; import org.breedinginsight.dao.db.enums.DataType; @@ -207,6 +208,23 @@ public ValidationErrors checkTraitFieldsLength(List traits, TraitValidato return errors; } + public ValidationErrors checkTraitFieldsFormat(List traits, TraitValidatorErrorInterface traitValidatorErrors) { + + ValidationErrors errors = new ValidationErrors(); + + for (int i = 0; i < traits.size(); i++) { + + Trait trait = traits.get(i); + String name = trait.getObservationVariableName(); + + if (name != null && name.contains(".")){ + ValidationError error = traitValidatorErrors.getPeriodObsVarNameMsg(); + errors.addError(traitValidatorErrors.getRowNumber(i), error); + } + } + return errors; + } + public List checkDuplicateTraitsExistingByName(UUID programId, List traits){ List duplicates = new ArrayList<>(); @@ -273,7 +291,8 @@ public Optional checkAllTraitValidations(List traits, T ValidationErrors dataConsistencyErrors = checkTraitDataConsistency(traits, traitValidatorError); ValidationErrors duplicateTraitsInFile = checkDuplicateTraitsInFile(traits, traitValidatorError); ValidationErrors fieldLengthError = checkTraitFieldsLength(traits, traitValidatorError); - validationErrors.mergeAll(requiredFieldErrors, dataConsistencyErrors, duplicateTraitsInFile, fieldLengthError); + ValidationErrors fieldFormatErrors = checkTraitFieldsFormat(traits, traitValidatorError); + validationErrors.mergeAll(requiredFieldErrors, dataConsistencyErrors, duplicateTraitsInFile, fieldLengthError, fieldFormatErrors); if (validationErrors.hasErrors()){ return Optional.of(validationErrors); diff --git a/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java b/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java index 809b5df86..48066c923 100644 --- a/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java +++ b/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java @@ -426,7 +426,6 @@ private BrAPIObservation createBrAPIObservation() { .additionalInfo(toJsonObject(additionalInfoString)) .collector("intern") .externalReferences(createExternalReferences()) - .geoCoordinates(BrApiGeoJSON.builder().geometry(null).type("Feature").build()) .germplasmDbId("2bb19ef2-fcc5-406d-b9c3-4517c665b699") .germplasmName("lucky") .observationTimeStamp(OffsetDateTime.now()) diff --git a/src/test/java/org/breedinginsight/services/validators/TraitValidatorUnitTest.java b/src/test/java/org/breedinginsight/services/validators/TraitValidatorUnitTest.java index 184e7308e..22bd5dfa8 100644 --- a/src/test/java/org/breedinginsight/services/validators/TraitValidatorUnitTest.java +++ b/src/test/java/org/breedinginsight/services/validators/TraitValidatorUnitTest.java @@ -348,5 +348,28 @@ public void charLimitExceeded() { } } + @Test + @SneakyThrows + public void periodInName() { + + Trait trait = new Trait(); + trait.setObservationVariableName("Period.1"); + + ValidationErrors validationErrors = traitValidatorService.checkTraitFieldsFormat(List.of(trait), new TraitValidatorError()); + + assertEquals(1, validationErrors.getRowErrors().size(), "Wrong number of row errors returned"); + RowValidationErrors rowValidationErrors = validationErrors.getRowErrors().get(0); + assertEquals(1, rowValidationErrors.getErrors().size(), "Wrong number of errors for row"); + assertEquals(400, rowValidationErrors.getErrors().get(0).getHttpStatusCode(), "Wrong error code"); + assertEquals("Name", rowValidationErrors.getErrors().get(0).getField(), "Wrong error column"); + + //There should be no errors + Trait noPeriodTrait = new Trait(); + noPeriodTrait.setObservationVariableName("NoPeriod"); + validationErrors = traitValidatorService.checkTraitFieldsFormat(List.of(noPeriodTrait), new TraitValidatorError()); + assertEquals(0, validationErrors.getRowErrors().size(), "Wrong number of row errors returned"); + } + + } From f437b970b5e5ab5ad78ed4b8a8ae2658235642c3 Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Fri, 11 Oct 2024 09:22:27 -0400 Subject: [PATCH 012/132] [BI-2328] fixed error I created in previous commit --- .../breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java b/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java index 48066c923..809b5df86 100644 --- a/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java +++ b/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java @@ -426,6 +426,7 @@ private BrAPIObservation createBrAPIObservation() { .additionalInfo(toJsonObject(additionalInfoString)) .collector("intern") .externalReferences(createExternalReferences()) + .geoCoordinates(BrApiGeoJSON.builder().geometry(null).type("Feature").build()) .germplasmDbId("2bb19ef2-fcc5-406d-b9c3-4517c665b699") .germplasmName("lucky") .observationTimeStamp(OffsetDateTime.now()) From beaee3ad4a805eeb1ce291c9e979cb0f6f7b7682 Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Wed, 23 Oct 2024 11:52:29 -0400 Subject: [PATCH 013/132] [BI-2328] fixed test, future proofed some code --- .../services/validators/TraitValidatorService.java | 8 +++++++- .../model/delta/DeltaEntityFactoryUnitTest.java | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/breedinginsight/services/validators/TraitValidatorService.java b/src/main/java/org/breedinginsight/services/validators/TraitValidatorService.java index de88755d2..d8bd6bf7e 100644 --- a/src/main/java/org/breedinginsight/services/validators/TraitValidatorService.java +++ b/src/main/java/org/breedinginsight/services/validators/TraitValidatorService.java @@ -27,6 +27,8 @@ import javax.inject.Inject; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -217,7 +219,11 @@ public ValidationErrors checkTraitFieldsFormat(List traits, TraitValidato Trait trait = traits.get(i); String name = trait.getObservationVariableName(); - if (name != null && name.contains(".")){ + Pattern pattern = Pattern.compile("\\."); + Matcher matcher = pattern.matcher(name); + boolean containsInvalidCharacter = matcher.find(); + + if (name != null && containsInvalidCharacter){ ValidationError error = traitValidatorErrors.getPeriodObsVarNameMsg(); errors.addError(traitValidatorErrors.getRowNumber(i), error); } diff --git a/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java b/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java index 809b5df86..cd8f71028 100644 --- a/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java +++ b/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java @@ -426,7 +426,7 @@ private BrAPIObservation createBrAPIObservation() { .additionalInfo(toJsonObject(additionalInfoString)) .collector("intern") .externalReferences(createExternalReferences()) - .geoCoordinates(BrApiGeoJSON.builder().geometry(null).type("Feature").build()) +// .geoCoordinates(BrApiGeoJSON.builder().geometry(null).type("Feature").build()) .germplasmDbId("2bb19ef2-fcc5-406d-b9c3-4517c665b699") .germplasmName("lucky") .observationTimeStamp(OffsetDateTime.now()) From e4177a026ae86674917ab48c5505bf729748c085 Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Thu, 24 Oct 2024 09:17:13 -0400 Subject: [PATCH 014/132] [BI-2328] fixed bug and unit test --- .../breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java | 2 +- .../services/validators/TraitValidatorUnitTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java b/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java index cd8f71028..809b5df86 100644 --- a/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java +++ b/src/test/java/org/breedinginsight/model/delta/DeltaEntityFactoryUnitTest.java @@ -426,7 +426,7 @@ private BrAPIObservation createBrAPIObservation() { .additionalInfo(toJsonObject(additionalInfoString)) .collector("intern") .externalReferences(createExternalReferences()) -// .geoCoordinates(BrApiGeoJSON.builder().geometry(null).type("Feature").build()) + .geoCoordinates(BrApiGeoJSON.builder().geometry(null).type("Feature").build()) .germplasmDbId("2bb19ef2-fcc5-406d-b9c3-4517c665b699") .germplasmName("lucky") .observationTimeStamp(OffsetDateTime.now()) diff --git a/src/test/java/org/breedinginsight/services/validators/TraitValidatorUnitTest.java b/src/test/java/org/breedinginsight/services/validators/TraitValidatorUnitTest.java index 22bd5dfa8..ea60ac285 100644 --- a/src/test/java/org/breedinginsight/services/validators/TraitValidatorUnitTest.java +++ b/src/test/java/org/breedinginsight/services/validators/TraitValidatorUnitTest.java @@ -361,7 +361,7 @@ public void periodInName() { RowValidationErrors rowValidationErrors = validationErrors.getRowErrors().get(0); assertEquals(1, rowValidationErrors.getErrors().size(), "Wrong number of errors for row"); assertEquals(400, rowValidationErrors.getErrors().get(0).getHttpStatusCode(), "Wrong error code"); - assertEquals("Name", rowValidationErrors.getErrors().get(0).getField(), "Wrong error column"); + assertEquals("observationVariableName", rowValidationErrors.getErrors().get(0).getField(), "Wrong error column"); //There should be no errors Trait noPeriodTrait = new Trait(); From 9c4c2d17c002bcd4955fc5bf5b51ce5d44d800be Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Thu, 24 Oct 2024 19:38:15 +0000 Subject: [PATCH 015/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index f8c7a5f73..b95a101f2 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -14,5 +14,5 @@ # limitations under the License. # -version=v1.1.0+842 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/011f00611846b917e5f9a1521308e06dc127873b +version=v1.1.0+846 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/d6bcf37e346a4af515f1500a275914b0634294ab From 9d4f7c706f25e8654a0120454826583f02b98980 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Tue, 29 Oct 2024 14:52:25 +0000 Subject: [PATCH 016/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index b95a101f2..952159cc6 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -14,5 +14,5 @@ # limitations under the License. # -version=v1.1.0+846 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/d6bcf37e346a4af515f1500a275914b0634294ab +version=v1.1.0+852 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/3fbb75f662c5600da816270d53699aa1192777b9 From 862f819e5c2668262c80fdaf0ceb6b09fa452937 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Wed, 30 Oct 2024 18:48:03 +0000 Subject: [PATCH 017/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index a4ad700ce..65ecfebaa 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+852 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/3fbb75f662c5600da816270d53699aa1192777b9 +version=v1.1.0+856 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/a98bbf813a1a0dda99c6273cf571c83a75ffaf9f From 6f656265b7dc6bbd7c5ccc9fa124ef23e316ca1f Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Mon, 4 Nov 2024 16:41:07 +0000 Subject: [PATCH 018/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 65ecfebaa..b0c2c8d37 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+856 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/a98bbf813a1a0dda99c6273cf571c83a75ffaf9f +version=v1.1.0+864 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/99507a6d27aae4f689364b944357a2fc71d2f041 From 02acaae61973af3eb9fa77dbb6a483d067c3f459 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Mon, 4 Nov 2024 17:48:54 +0000 Subject: [PATCH 019/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index b0c2c8d37..32ceac4bf 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+864 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/99507a6d27aae4f689364b944357a2fc71d2f041 +version=v1.1.0+868 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/2f32914be2408d1b7511ba83e79b62553bfe2287 From 5ac1ba9bdc93ba0d0607a7211b2f3e6e58775326 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:52:52 -0500 Subject: [PATCH 020/132] [BI-2156] - updated apache tika also removed hard-coded transitive dependencies --- pom.xml | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/pom.xml b/pom.xml index 1bfde3251..75dd35c02 100644 --- a/pom.xml +++ b/pom.xml @@ -91,10 +91,7 @@ 4.3.1 2.1-SNAPSHOT 2.11.0 - 2.2.1 - - 4.1.2 - 4.1.2 + 2.9.2 1.6.2 4.3.1 1.0.0-SNAPSHOT @@ -378,31 +375,6 @@ brapi-java-client ${brapi-java-client.version} - - org.apache.commons - commons-csv - ${apache-commons-csv.version} - - - org.apache.poi - poi - ${apache-poi.version} - - - org.apache.poi - poi-ooxml - ${apache-poi-ooxml.version} - - - org.apache.commons - commons-lang3 - ${apache-commons-lang.version} - - - commons-io - commons-io - ${commons-io.version} - org.apache.tika tika-app From 92b08d063d5674409eabf8f0ab1f066dbcf41c48 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:26:34 -0500 Subject: [PATCH 021/132] [BI-2156] - hard-coded apache commons compress version --- pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pom.xml b/pom.xml index 75dd35c02..d0efae5cd 100644 --- a/pom.xml +++ b/pom.xml @@ -92,6 +92,8 @@ 2.1-SNAPSHOT 2.11.0 2.9.2 + + 1.26.1 1.6.2 4.3.1 1.0.0-SNAPSHOT @@ -380,6 +382,11 @@ tika-app ${tika-app.version} + + org.apache.commons + commons-compress + ${commons-compress.version} + com.sun.mail javax.mail From da84b7c301a030bab34b447b30c8a75a1195d1ba Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:51:31 -0500 Subject: [PATCH 022/132] [BI-2203] - renamed migration version 1.31.0 was already used --- ...t_Dev_Admin_Email.java => V1_32_0__Set_Dev_Admin_Email.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/org/breedinginsight/db/migration/{V1_31_0__Set_Dev_Admin_Email.java => V1_32_0__Set_Dev_Admin_Email.java} (98%) diff --git a/src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java b/src/main/java/org/breedinginsight/db/migration/V1_32_0__Set_Dev_Admin_Email.java similarity index 98% rename from src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java rename to src/main/java/org/breedinginsight/db/migration/V1_32_0__Set_Dev_Admin_Email.java index 36b47b302..8ffcc02e7 100644 --- a/src/main/java/org/breedinginsight/db/migration/V1_31_0__Set_Dev_Admin_Email.java +++ b/src/main/java/org/breedinginsight/db/migration/V1_32_0__Set_Dev_Admin_Email.java @@ -29,7 +29,7 @@ import java.util.*; @Slf4j -public class V1_31_0__Set_Dev_Admin_Email extends BaseJavaMigration { +public class V1_32_0__Set_Dev_Admin_Email extends BaseJavaMigration { @Inject private DSLContext dsl; From 893ebe04662e07d4c3b5090869b999e198a8f70c Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Mon, 18 Nov 2024 20:00:17 +0000 Subject: [PATCH 023/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 32ceac4bf..b872e8d73 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+868 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/2f32914be2408d1b7511ba83e79b62553bfe2287 +version=v1.1.0+887 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/b7d65b01c976e1c729ef9bb450143e63dad4c014 From 369f421f0e72181a2cf9b9640b997973517c3d90 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:05:31 -0500 Subject: [PATCH 024/132] [BI-2376] - added method signatures --- .../v1/controller/ExperimentController.java | 33 +++++++++++++++++++ .../brapi/v2/services/BrAPITrialService.java | 6 ++++ 2 files changed, 39 insertions(+) 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 a6e172c94..de40bdf46 100644 --- a/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java @@ -298,4 +298,37 @@ public HttpResponse deleteExperimentalCollaborator( } } + + /** + * Deletes an experiment. + * @param programId The UUID of the program + * @param experimentId The UUID of the experiment + * @param hard Specifies hard or soft delete + * @return A Http Response + */ + @Delete("/${micronaut.bi.api.version}/programs/{programId}/experiments/{experimentId}{?hard}") + @ProgramSecured(roles = {ProgramSecuredRole.PROGRAM_ADMIN, ProgramSecuredRole.SYSTEM_ADMIN}) + @Produces(MediaType.APPLICATION_JSON) + public HttpResponse deleteExperiment( + @PathVariable("programId") UUID programId, + @PathVariable("experimentId") UUID experimentId, + @QueryValue(defaultValue = "false") Boolean hard + ) throws ApiException { + try { + Optional program = programService.getById(programId); + if(program.isEmpty()) { + return HttpResponse.notFound(); + } + + experimentService.deleteExperiment(program.get(), experimentId, hard); + + return HttpResponse.ok(); + } catch (Exception e) { + log.error("Error deleting experiment.\n\tprogramId: " + programId + "\n\texperimentId: " + experimentId + "\n\thard: " + hard); + throw e; + } + + } + + } 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 7c37f8f55..c6b55fc76 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java @@ -667,6 +667,12 @@ public BrAPITrial getExperiment(Program program, UUID experimentId) throws ApiEx return experiments.get(0); } + public boolean deleteExperiment(Program program, UUID experimentId, boolean hard) throws ApiException { + // TODO: make BrAPI request to delete experiment. + // TODO: make BrAPI request to delete list for default observation dataset. + + } + private Map createExportRow( BrAPITrial experiment, Program program, From beae4476b34b6398d3ec5dc203dcef31abbab9c3 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Mon, 18 Nov 2024 20:36:14 +0000 Subject: [PATCH 025/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index b872e8d73..323e40196 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+887 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/b7d65b01c976e1c729ef9bb450143e63dad4c014 +version=v1.1.0+889 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/6afa35526f279ed0938cb12ce6bf53d3e67f3d92 From 3716bfc6892192c8802fa5c81952ae079d312c2e Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Mon, 18 Nov 2024 21:06:54 +0000 Subject: [PATCH 026/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 323e40196..cba537926 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+889 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/6afa35526f279ed0938cb12ce6bf53d3e67f3d92 +version=v1.1.0+891 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/75e978c62c555b745b4779800c350d9780bbcb75 From 257e848db941edf4093a7421e090c67fc40c68d8 Mon Sep 17 00:00:00 2001 From: HMS17 Date: Tue, 19 Nov 2024 16:51:50 -0500 Subject: [PATCH 027/132] [BI-2343] - GID filtering not informative --- .../utilities/response/ResponseUtils.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java b/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java index 6b97984f7..2b783b90c 100644 --- a/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java +++ b/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java @@ -193,6 +193,13 @@ private static List search(List data, SearchRequest searchRequest, Abstrac .collect(Collectors.toList()); } + //To enable checking for the case of Germplasm Search when the filter is accessionNumber and thereby needs to do exact match + String accessionNumFilter; + if (mapper.exists("accessionNumber")) accessionNumFilter = mapper.getField("accessionNumber").toString(); + else { + accessionNumFilter = ""; + } + if (filterFields.size() > 0){ // Apply filters List finalFilterFields = filterFields; @@ -211,6 +218,11 @@ private static List search(List data, SearchRequest searchRequest, Abstrac return recordList.stream() .anyMatch(listValue -> listValue.toString().toLowerCase().contains(filterField.getValue().toLowerCase())); + } + else if (!accessionNumFilter.isEmpty() && filterField.getField().toString().equals(accessionNumFilter)) { + //enable exact match in case of GID + return filterField.getField().apply(record).toString() + .toLowerCase().equalsIgnoreCase(filterField.getValue().toLowerCase()); } else { return filterField.getField().apply(record).toString() .toLowerCase().contains(filterField.getValue().toLowerCase()); From 22e00037c7eab715201de32b19fdfcdf21df96aa Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 19 Nov 2024 21:15:01 -0500 Subject: [PATCH 028/132] [BI-2156] - removed unused variable from pom.xml --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index d0efae5cd..4280fd0b1 100644 --- a/pom.xml +++ b/pom.xml @@ -90,7 +90,6 @@ 4.9.3 4.3.1 2.1-SNAPSHOT - 2.11.0 2.9.2 1.26.1 From 87d0962dcbfa649f5b136753d01f8f2e6cfebec8 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Fri, 15 Nov 2024 12:33:02 -0500 Subject: [PATCH 029/132] add delete endpoint to list controller --- .../brapi/v2/BrAPIGermplasmController.java | 37 ---- .../brapi/v2/BrAPIListController.java | 183 +++++++++++++++--- .../org/breedinginsight/daos/ListDAO.java | 102 ++++++++++ .../model/delta/DeltaEntity.java | 3 +- .../model/delta/DeltaEntityFactory.java | 55 +++++- .../delta/DeltaGermplasmListDetails.java | 62 ++++++ .../delta/DeltaGermplasmListSummary.java | 16 ++ .../model/delta/DeltaListDetails.java | 78 ++++++++ .../model/delta/DeltaListSummary.java | 20 ++ .../breedinginsight/services/ListService.java | 33 ++++ .../breedinginsight/utilities/Utilities.java | 2 + 11 files changed, 521 insertions(+), 70 deletions(-) create mode 100644 src/main/java/org/breedinginsight/daos/ListDAO.java create mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java create mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java create mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java create mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java create mode 100644 src/main/java/org/breedinginsight/services/ListService.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java index 948e68c20..4d67e2a00 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java @@ -221,43 +221,6 @@ public HttpResponse>>> getGermplasm( } } - @Get("/programs/{programId}/germplasm/lists/{listDbId}/records{?queryParams*}") - @Produces(MediaType.APPLICATION_JSON) - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - public HttpResponse>>> getGermplasmListRecords( - @PathVariable("programId") UUID programId, - @PathVariable("listDbId") String listDbId, - @QueryValue @QueryValid(using = GermplasmQueryMapper.class) @Valid GermplasmQuery queryParams) { - try { - List germplasm = germplasmService.getGermplasmByList(programId, listDbId); - SearchRequest searchRequest = queryParams.constructSearchRequest(); - return ResponseUtils.getBrapiQueryResponse(germplasm, germplasmQueryMapper, queryParams, searchRequest); - } catch (Exception e) { - log.info(e.getMessage(), e); - return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving germplasm list records"); - } - } - - @Get("/programs/{programId}/germplasm/lists/{listDbId}/export{?fileExtension}") - @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - public HttpResponse germplasmListExport( - @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, @QueryValue(defaultValue = "XLSX") String fileExtension) { - String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; - try { - FileType extension = Enum.valueOf(FileType.class, fileExtension); - DownloadFile germplasmListFile = germplasmService.exportGermplasmList(programId, listDbId, extension); - HttpResponse germplasmListExport = HttpResponse.ok(germplasmListFile.getStreamedFile()).header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="+germplasmListFile.getFileName()+extension.getExtension()); - return germplasmListExport; - } - catch (Exception e) { - log.info(e.getMessage(), e); - e.printStackTrace(); - HttpResponse response = HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, downloadErrorMessage).contentType(MediaType.TEXT_PLAIN).body(downloadErrorMessage); - return response; - } - } - @Get("/programs/{programId}/germplasm/export{?fileExtension}") @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 3dd6fd1f4..0eebc602d 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -1,26 +1,10 @@ -/* - * See the NOTICE file distributed with this work for additional information - * regarding copyright ownership. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.breedinginsight.brapi.v2; -import io.micronaut.http.HttpResponse; -import io.micronaut.http.HttpStatus; -import io.micronaut.http.MediaType; +import io.micronaut.core.beans.BeanIntrospection; +import io.micronaut.core.beans.BeanProperty; +import io.micronaut.http.*; import io.micronaut.http.annotation.*; +import io.micronaut.http.server.types.files.StreamedFile; import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; @@ -29,43 +13,55 @@ import org.brapi.v2.model.core.BrAPIListTypes; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; -import org.breedinginsight.api.model.v1.request.query.SearchRequest; import org.breedinginsight.api.model.v1.response.DataResponse; import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.api.model.v1.validators.QueryValid; import org.breedinginsight.brapi.v1.controller.BrapiVersion; +import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; import org.breedinginsight.brapi.v2.model.request.query.ListQuery; import org.breedinginsight.brapi.v2.services.BrAPIListService; +import org.breedinginsight.brapps.importer.model.exports.FileType; +import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.Program; +import org.breedinginsight.model.delta.DeltaListDetails; +import org.breedinginsight.services.ListService; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.response.ResponseUtils; +import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; import org.breedinginsight.utilities.response.mappers.ListQueryMapper; +import org.breedinginsight.api.model.v1.request.query.SearchRequest; import javax.inject.Inject; +import javax.validation.ConstraintViolation; import javax.validation.Valid; +import javax.validation.Validator; import java.util.List; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; @Slf4j -@Controller +@Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @Secured(SecurityRule.IS_AUTHENTICATED) public class BrAPIListController { - private final ProgramService programService; - private final BrAPIListService listService; + private final BrAPIListService brapiListService; + private final ListService listService; private final ListQueryMapper listQueryMapper; + private final Validator validator; @Inject - public BrAPIListController(ProgramService programService, BrAPIListService listService, - ListQueryMapper listQueryMapper) { + public BrAPIListController(ProgramService programService, BrAPIListService brapiListService, ListService listService, + ListQueryMapper listQueryMapper, Validator validator) { this.programService = programService; + this.brapiListService = brapiListService; this.listService = listService; this.listQueryMapper = listQueryMapper; + this.validator = validator; } - //@Get(BrapiVersion.BRAPI_V2 + "/lists") - @Get("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/lists{?queryParams*}") + @Get("/deltalists{?queryParams*}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse>> getLists( @@ -77,7 +73,6 @@ public HttpResponse>> getLists( .getById(programId) .orElseThrow(() -> new DoesNotExistException("Program does not exist")); - // get germplasm lists by default BrAPIListTypes type = BrAPIListTypes.fromValue(queryParams.getListType()); String source = null; String id = null; @@ -93,7 +88,7 @@ public HttpResponse>> getLists( if (dateFormatParam != null) { listQueryMapper.setDateDisplayFormat(dateFormatParam); } - List brapiLists = listService.getListSummariesByTypeAndXref(type, source, id, program); + List brapiLists = brapiListService.getListSummariesByTypeAndXref(type, source, id, program); SearchRequest searchRequest = queryParams.constructSearchRequest(); return ResponseUtils.getBrapiQueryResponse(brapiLists, listQueryMapper, queryParams, searchRequest); @@ -105,4 +100,132 @@ public HttpResponse>> getLists( throw new RuntimeException(e); } } + + @Delete("/deltalists/{listDbId}") + @Produces(MediaType.APPLICATION_JSON) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) + public HttpResponse>> deleteListById( + @PathVariable("programId") UUID programId, + @PathVariable("listDbId") String listDbId, + HttpRequest request + ) throws DoesNotExistException, ApiException { + boolean hardDelete = false; + if (request.getParameters().contains("hardDelete")) { + String paramValue = request.getParameters().get("hardDelete"); + hardDelete = "true".equals(paramValue); + } + try { + listService.deleteBrAPIList(listDbId, programId, hardDelete); + return HttpResponse.status(HttpStatus.NO_CONTENT); + } catch (Exception e) { + log.info(e.getMessage(), e); + return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving germplasm list records"); + } + } + + @Get("/deltalists/{listDbId}") + @Produces(MediaType.APPLICATION_JSON) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) + @SuppressWarnings("unchecked") + public HttpResponse>>> getListById( + @PathVariable("programId") UUID programId, + @PathVariable("listDbId") String listDbId, + HttpRequest request) { + try { + // Get the list from the BrAPI service + DeltaListDetails details = listService.getDeltaListDetails(listDbId, programId); + + // Get a new instance of BrAPI query matching the type of list contents + T queryParams = (T) details.getQuery(); + + // Bind query parameters to the object + bindQueryParams(queryParams, request); + + // Perform standard bean validation + Set> violations = validator.validate(queryParams); + if (!violations.isEmpty()) { + List errorMessages = violations.stream() + .map(ConstraintViolation::getMessage) + .collect(Collectors.toList()); + log.info(String.join(", ", errorMessages)); + return HttpResponse.status(HttpStatus.BAD_REQUEST, "Error with list contents search parameters"); + } + + // Fetch the list contents from the BrAPI service + List listContentsBrAPIObjects = (List) details.getDataObjects(); + + // Construct a search request for sorting the list contents + SearchRequest searchRequest = details.constructSearchRequest(queryParams); + + // Get the map used to connect query sorting keys to contents object values + AbstractQueryMapper contentsQueryMapper = details.getQueryMapper(); + + return ResponseUtils.getBrapiQueryResponse(listContentsBrAPIObjects, contentsQueryMapper, queryParams, searchRequest); + } catch (Exception e) { + log.info(e.getMessage(), e); + return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving list records"); + } + } + + @Get("/deltalists/{listDbId}/export{?fileExtension}") + @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) + public HttpResponse germplasmListExport( + @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, @QueryValue(defaultValue = "XLSX") String fileExtension) { + String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; + try { + // Get the list from the BrAPI service + DeltaListDetails details = listService.getDeltaListDetails(listDbId, programId); + + FileType extension = Enum.valueOf(FileType.class, fileExtension); + DownloadFile listContentsFile = details.exportListObjects(extension); + return HttpResponse.ok(listContentsFile.getStreamedFile()).header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="+listContentsFile.getFileName()+extension.getExtension()); + } + catch (Exception e) { + log.info(e.getMessage(), e); + e.printStackTrace(); + HttpResponse response = HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, downloadErrorMessage).contentType(MediaType.TEXT_PLAIN).body(downloadErrorMessage); + return response; + } + } + + private void bindQueryParams(BrapiQuery queryParams, HttpRequest request) { + BeanIntrospection introspection = BeanIntrospection.getIntrospection(BrapiQuery.class); + for (BeanProperty property : introspection.getBeanProperties()) { + String paramName = property.getName(); + if (request.getParameters().contains(paramName)) { + String paramValue = request.getParameters().get(paramName); + Object convertedValue; + Class propertyType = property.getType(); + + if (propertyType.isEnum()) { + convertedValue = convertToEnum(paramValue, (Class>) propertyType); + } else { + convertedValue = convertValue(paramValue, propertyType); + } + + property.set(queryParams, convertedValue); + } + } + } + + private > T convertToEnum(String value, Class> enumClass) { + if (value == null) { + return null; + } + return Enum.valueOf((Class) enumClass, value.toUpperCase()); + } + + + // Convert, if necessary, the values of query parameters to match the type defined for the fields in the BrapiQuery class + private Object convertValue(String value, Class targetType) { + // Implement type conversion logic here + // Other list content types might need more complex logic + if (targetType == String.class) return value; + if (targetType == Integer.class) return Integer.parseInt(value); + if (targetType == Long.class) return Long.parseLong(value); + if (targetType == Boolean.class) return Boolean.parseBoolean(value); + // Add more type conversions as needed + return value; + } } diff --git a/src/main/java/org/breedinginsight/daos/ListDAO.java b/src/main/java/org/breedinginsight/daos/ListDAO.java new file mode 100644 index 000000000..f44a8757f --- /dev/null +++ b/src/main/java/org/breedinginsight/daos/ListDAO.java @@ -0,0 +1,102 @@ +package org.breedinginsight.daos; + +import io.micronaut.http.HttpStatus; +import io.micronaut.http.exceptions.HttpStatusException; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.brapi.client.v2.ApiResponse; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.client.v2.modules.core.ListsApi; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; +import org.breedinginsight.model.ProgramBrAPIEndpoints; +import org.breedinginsight.model.delta.DeltaEntityFactory; +import org.breedinginsight.model.delta.DeltaListDetails; +import org.breedinginsight.services.ProgramService; +import org.breedinginsight.services.brapi.BrAPIEndpointProvider; +import org.breedinginsight.services.exceptions.DoesNotExistException; +import org.breedinginsight.utilities.BrAPIDAOUtil; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.IOException; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Singleton +public class ListDAO { + private final ProgramDAO programDAO; + private final BrAPIDAOUtil brAPIDAOUtil; + private final BrAPIEndpointProvider brAPIEndpointProvider; + private final DeltaEntityFactory deltaEntityFactory; + private final ProgramService programService; + + @Inject + public ListDAO(ProgramDAO programDAO, + BrAPIDAOUtil brAPIDAOUtil, + BrAPIEndpointProvider brAPIEndpointProvider, + DeltaEntityFactory deltaEntityFactory, ProgramService programService) { + this.programDAO = programDAO; + this.brAPIDAOUtil = brAPIDAOUtil; + this.brAPIEndpointProvider = brAPIEndpointProvider; + this.deltaEntityFactory = deltaEntityFactory; + this.programService = programService; + } + + public DeltaListDetails getDeltaListDetailsByDbId(String listDbId, UUID programId) throws ApiException { + ListsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ListsApi.class); + ApiResponse response = api.listsListDbIdGet(listDbId); + if (Objects.isNull(response.getBody()) || Objects.isNull(response.getBody().getResult())) + { + throw new ApiException(); + } + + BrAPIListDetails details = response.getBody().getResult(); + return deltaEntityFactory.makeDeltaListDetailsBean(details); + } + + public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); + var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); + requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); + HttpUrl url = requestUrl.build(); + var brapiRequest = new Request.Builder().url(url) + .method("DELETE", null) + .addHeader("Content-Type", "application/json") + .build(); + + makeCall(brapiRequest); + } + + private void makeCall(Request brapiRequest) throws ApiException { + OkHttpClient client = new OkHttpClient.Builder() + .readTimeout(5, TimeUnit.MINUTES) + .build(); + try { + client.newCall(brapiRequest).execute(); + } catch (IOException e) { + log.error("Error calling BrAPI Service", e); + throw new ApiException("Error calling BrAPI Service"); + } + } + + private String getProgramBrAPIBaseUrl(UUID programId) { + ProgramBrAPIEndpoints programBrAPIEndpoints; + try { + programBrAPIEndpoints = programService.getBrapiEndpoints(programId); + } catch (DoesNotExistException e) { + throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); + } + + if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { + log.error("Program: " + programId + " is missing BrAPI URL config"); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); + } + var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); + programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; + return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; + } +} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaEntity.java b/src/main/java/org/breedinginsight/model/delta/DeltaEntity.java index 11646f9f8..4f5b8ead9 100644 --- a/src/main/java/org/breedinginsight/model/delta/DeltaEntity.java +++ b/src/main/java/org/breedinginsight/model/delta/DeltaEntity.java @@ -7,7 +7,7 @@ public abstract class DeltaEntity { - protected final Gson gson; + protected static final Gson gson = new GsonBuilder().registerTypeAdapterFactory(new GeometryAdapterFactory()).create(); @NonNull protected final T entity; @@ -15,7 +15,6 @@ public abstract class DeltaEntity { // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. protected DeltaEntity(@NonNull T entity) { this.entity = entity; - this.gson = new GsonBuilder().registerTypeAdapterFactory(new GeometryAdapterFactory()).create(); } protected T getEntity() { diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java index 3787c5d71..8d473ce5a 100644 --- a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java +++ b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java @@ -2,8 +2,14 @@ import io.micronaut.context.annotation.Bean; import io.micronaut.context.annotation.Factory; +import io.micronaut.context.annotation.Property; import io.micronaut.context.annotation.Prototype; import lombok.NonNull; +import org.brapi.v2.model.core.BrAPIListSummary; +import org.brapi.v2.model.core.BrAPIListTypes; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; +import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; import org.breedinginsight.brapps.importer.services.processors.experiment.service.ObservationUnitService; import org.brapi.v2.model.core.BrAPIStudy; @@ -13,6 +19,7 @@ import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.brapi.v2.model.pheno.BrAPIObservationVariable; import org.breedinginsight.model.ProgramLocation; +import org.breedinginsight.utilities.Utilities; import javax.inject.Inject; @@ -20,10 +27,16 @@ public class DeltaEntityFactory { private final ObservationUnitService observationUnitService; + private final BrAPIGermplasmService germplasmService; + private final String applicationReferenceSourceBase; @Inject - public DeltaEntityFactory(ObservationUnitService observationUnitService) { + public DeltaEntityFactory(ObservationUnitService observationUnitService, + BrAPIGermplasmService germplasmService, + @Property(name = "brapi.server.reference-source") String applicationReferenceSourceBase) { this.observationUnitService = observationUnitService; + this.germplasmService = germplasmService; + this.applicationReferenceSourceBase = applicationReferenceSourceBase; } private Experiment makeExperiment(BrAPITrial brAPIObject) { @@ -54,6 +67,46 @@ private DeltaObservationVariable makeDeltaObservationVariable(BrAPIObservationVa return new DeltaObservationVariable(brAPIObject); } + private DeltaGermplasmListSummary makeDeltaGermplasmListSummary(BrAPIListSummary brAPIObject) { + return new DeltaGermplasmListSummary(brAPIObject); + } + + private DeltaGermplasmListDetails makeDeltaGermplasmListDetails(BrAPIListDetails brAPIObject, String referenceSourceBase, BrAPIGermplasmService germplasmService) { + return new DeltaGermplasmListDetails(brAPIObject, referenceSourceBase, germplasmService); + } + + @Bean + @Prototype + public DeltaListSummary makeDeltaListSummaryBean(@NonNull BrAPIListSummary brAPIObject) { + BrAPIListTypes listType = brAPIObject.getListType(); + switch (listType) { + case GERMPLASM: return makeDeltaGermplasmListSummary(brAPIObject); + default: return null; + } + } + + @Bean + @Prototype + public DeltaListDetails makeDeltaListDetailsBean(@NonNull BrAPIListDetails brAPIObject) { + BrAPIListTypes listType = brAPIObject.getListType(); + switch (listType) { + case GERMPLASM: return makeDeltaGermplasmListDetailsBean(brAPIObject); + default: return null; + } + } + + @Bean + @Prototype + public DeltaGermplasmListSummary makeDeltaGermplasmListSummaryBean(@NonNull BrAPIListSummary brAPIObject) { + return makeDeltaGermplasmListSummary(brAPIObject); + } + + @Bean + @Prototype + public DeltaGermplasmListDetails makeDeltaGermplasmListDetailsBean(@NonNull BrAPIListDetails brAPIObject) { + return makeDeltaGermplasmListDetails(brAPIObject, applicationReferenceSourceBase, germplasmService); + } + @Bean @Prototype public Experiment makeExperimentBean(@NonNull BrAPITrial brAPIObject) { diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java new file mode 100644 index 000000000..1f8ea3d9c --- /dev/null +++ b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java @@ -0,0 +1,62 @@ +package org.breedinginsight.model.delta; + +import io.micronaut.context.annotation.Prototype; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.brapi.v2.model.germ.BrAPIGermplasm; +import org.breedinginsight.api.model.v1.request.query.SearchRequest; +import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; +import org.breedinginsight.brapi.v2.model.request.query.GermplasmQuery; +import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; +import org.breedinginsight.brapps.importer.model.exports.FileType; +import org.breedinginsight.model.DownloadFile; +import org.breedinginsight.services.exceptions.DoesNotExistException; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; +import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; +import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; + +import java.io.IOException; +import java.util.List; +import java.util.UUID; + + +@Prototype +public class DeltaGermplasmListDetails extends DeltaListDetails { + + private final BrAPIGermplasmService germplasmService; + + // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. + DeltaGermplasmListDetails(BrAPIListDetails brAPIListDetails, + String referenceSourceBase, + BrAPIGermplasmService germplasmService) { + super(brAPIListDetails, referenceSourceBase); + this.germplasmService = germplasmService; + } + + @Override + public List getDataObjects() throws ApiException, UnprocessableEntityException { + UUID programId = getProgramId().orElseThrow(() -> new UnprocessableEntityException("Program Id not found for list " + getListName())); + return germplasmService.getGermplasmByList(programId, getListDbId()); + } + + @Override + public DownloadFile exportListObjects(FileType extension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException, UnprocessableEntityException { + UUID programId = getProgramId().orElseThrow(() -> new UnprocessableEntityException("Program Id not found for list " + getListName())); + return germplasmService.exportGermplasmList(programId, getListDbId(), extension); + } + + @Override + public AbstractQueryMapper getQueryMapper() { + return new GermplasmQueryMapper(); + } + + @Override + public BrapiQuery getQuery() { + return new GermplasmQuery(); + } + + @Override + public SearchRequest constructSearchRequest(BrapiQuery queryParams) { + return ((GermplasmQuery) queryParams).constructSearchRequest(); + } +} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java new file mode 100644 index 000000000..6ddd2518b --- /dev/null +++ b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java @@ -0,0 +1,16 @@ +package org.breedinginsight.model.delta; + +import io.micronaut.context.annotation.Prototype; +import lombok.Getter; +import lombok.Setter; +import org.brapi.v2.model.core.BrAPIListSummary; +import org.breedinginsight.brapps.importer.model.response.ImportObjectState; + + +@Prototype +public class DeltaGermplasmListSummary extends DeltaListSummary { + + // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. + DeltaGermplasmListSummary(BrAPIListSummary brAPIListSummary) { super(brAPIListSummary); } + +} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java b/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java new file mode 100644 index 000000000..9f2c745bf --- /dev/null +++ b/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java @@ -0,0 +1,78 @@ +package org.breedinginsight.model.delta; + +import io.micronaut.context.annotation.Prototype; +import lombok.Getter; +import lombok.Setter; +import lombok.Value; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.BrAPIExternalReference; +import org.brapi.v2.model.core.BrAPIListTypes; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.breedinginsight.api.model.v1.request.query.SearchRequest; +import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; +import org.breedinginsight.brapps.importer.model.exports.FileType; +import org.breedinginsight.brapps.importer.model.response.ImportObjectState; +import io.micronaut.context.annotation.Property; +import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; +import org.breedinginsight.model.DownloadFile; +import org.breedinginsight.services.exceptions.DoesNotExistException; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; +import org.breedinginsight.utilities.Utilities; +import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + + +@Prototype +public abstract class DeltaListDetails extends DeltaEntity { + private final String referenceSourceBase; + @Getter + @Setter + private ImportObjectState state; + + // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. + DeltaListDetails(BrAPIListDetails brAPIListDetails, String referenceSourceBase) { + super(brAPIListDetails); + this.referenceSourceBase = referenceSourceBase; + } + + public abstract List getDataObjects() throws ApiException, UnprocessableEntityException; + public abstract DownloadFile exportListObjects(FileType extension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException, UnprocessableEntityException; + public abstract AbstractQueryMapper getQueryMapper(); + + public abstract BrapiQuery getQuery(); + + public abstract SearchRequest constructSearchRequest(BrapiQuery queryParams); + + public BrAPIListTypes getListType() { + return entity.getListType(); + } + + public Optional getListId() { + return getXrefId(ExternalReferenceSource.LISTS); + } + + public Optional getProgramId() { + return getXrefId(ExternalReferenceSource.PROGRAMS); + } + + public String getListDbId() { + return entity.getListDbId(); + } + + public String getListName() { + return entity.getListName(); + } + + private Optional getXrefId(ExternalReferenceSource source) { + // Get the external reference if it exists + Optional xrefOptional = Utilities.getExternalReference(entity.getExternalReferences(), referenceSourceBase, source); + + // Parse the Deltabreed ID from the xref + return xrefOptional.map(BrAPIExternalReference::getReferenceId).map(UUID::fromString); + } +} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java b/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java new file mode 100644 index 000000000..80569d077 --- /dev/null +++ b/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java @@ -0,0 +1,20 @@ +package org.breedinginsight.model.delta; + +import io.micronaut.context.annotation.Prototype; +import lombok.Getter; +import lombok.Setter; +import org.brapi.v2.model.core.BrAPIListSummary; +import org.breedinginsight.brapps.importer.model.response.ImportObjectState; + + +@Prototype +public class DeltaListSummary extends DeltaEntity { + + @Getter + @Setter + private ImportObjectState state; + + // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. + DeltaListSummary(BrAPIListSummary brAPIListSummary) { super(brAPIListSummary); } + +} diff --git a/src/main/java/org/breedinginsight/services/ListService.java b/src/main/java/org/breedinginsight/services/ListService.java new file mode 100644 index 000000000..c0c18f734 --- /dev/null +++ b/src/main/java/org/breedinginsight/services/ListService.java @@ -0,0 +1,33 @@ +package org.breedinginsight.services; + +import lombok.extern.slf4j.Slf4j; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; +import org.breedinginsight.daos.ListDAO; +import org.breedinginsight.model.delta.DeltaListDetails; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.UUID; + +@Slf4j +@Singleton +public class ListService { + private final BrAPIListDAO brAPIListDAO; + private final ListDAO listDAO; + + @Inject + public ListService(BrAPIListDAO brAPIListDAO, ListDAO listDAO) { + this.brAPIListDAO = brAPIListDAO; + this.listDAO = listDAO; + } + + public DeltaListDetails getDeltaListDetails(String listDbId, UUID programId) throws ApiException { + return listDAO.getDeltaListDetailsByDbId(listDbId, programId); + } + + public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + listDAO.deleteBrAPIList(listDbId, programId, hardDelete); + } + +} diff --git a/src/main/java/org/breedinginsight/utilities/Utilities.java b/src/main/java/org/breedinginsight/utilities/Utilities.java index 20f3254d6..7ed72cfef 100644 --- a/src/main/java/org/breedinginsight/utilities/Utilities.java +++ b/src/main/java/org/breedinginsight/utilities/Utilities.java @@ -17,6 +17,7 @@ package org.breedinginsight.utilities; +import io.micronaut.context.annotation.Property; import org.apache.commons.lang3.StringUtils; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.BrAPIExternalReference; @@ -24,6 +25,7 @@ import org.breedinginsight.model.Program; import org.flywaydb.core.api.migration.Context; +import javax.inject.Inject; import java.lang.reflect.Field; import java.sql.ResultSet; import java.sql.Statement; From e29d0fe873650ecbe7ffc3f6f6f3c4f22e6e71c8 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:24:31 -0500 Subject: [PATCH 030/132] get germplasm list contents via germplasm controller --- .../brapi/v2/BrAPIGermplasmController.java | 3 +- .../brapi/v2/BrAPIListController.java | 21 ++-- .../brapi/v2/dao/BrAPIListDAO.java | 78 +++++++++++++- .../model/request/query/GermplasmQuery.java | 3 + .../brapi/v2/services/BrAPIListService.java | 10 ++ .../org/breedinginsight/daos/ListDAO.java | 102 ------------------ .../breedinginsight/services/ListService.java | 33 ------ 7 files changed, 97 insertions(+), 153 deletions(-) delete mode 100644 src/main/java/org/breedinginsight/daos/ListDAO.java delete mode 100644 src/main/java/org/breedinginsight/services/ListService.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java index 4d67e2a00..57ead5bd3 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java @@ -209,7 +209,8 @@ public HttpResponse>>> getGermplasm( germplasmQueryMapper.setDateDisplayFormat(dateFormatParam); } - List germplasm = germplasmService.getGermplasm(programId); + // Fetch all germplasm in the program unless a list id is supplied to return only germplasm in that collection + List germplasm = queryParams.getList() == null ? germplasmService.getGermplasm(programId) : germplasmService.getGermplasmByList(programId, queryParams.getList());; SearchRequest searchRequest = queryParams.constructSearchRequest(); return ResponseUtils.getBrapiQueryResponse(germplasm, germplasmQueryMapper, queryParams, searchRequest); } catch (ApiException e) { diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 0eebc602d..2d3324a65 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -13,6 +13,7 @@ import org.brapi.v2.model.core.BrAPIListTypes; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; +import org.breedinginsight.api.model.v1.request.query.SearchRequest; import org.breedinginsight.api.model.v1.response.DataResponse; import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.api.model.v1.validators.QueryValid; @@ -24,13 +25,11 @@ import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.Program; import org.breedinginsight.model.delta.DeltaListDetails; -import org.breedinginsight.services.ListService; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.response.ResponseUtils; import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; import org.breedinginsight.utilities.response.mappers.ListQueryMapper; -import org.breedinginsight.api.model.v1.request.query.SearchRequest; import javax.inject.Inject; import javax.validation.ConstraintViolation; @@ -47,21 +46,19 @@ public class BrAPIListController { private final ProgramService programService; private final BrAPIListService brapiListService; - private final ListService listService; private final ListQueryMapper listQueryMapper; private final Validator validator; @Inject - public BrAPIListController(ProgramService programService, BrAPIListService brapiListService, ListService listService, + public BrAPIListController(ProgramService programService, BrAPIListService brapiListService, ListQueryMapper listQueryMapper, Validator validator) { this.programService = programService; this.brapiListService = brapiListService; - this.listService = listService; this.listQueryMapper = listQueryMapper; this.validator = validator; } - @Get("/deltalists{?queryParams*}") + @Get("/lists{?queryParams*}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse>> getLists( @@ -101,7 +98,7 @@ public HttpResponse>> getLists( } } - @Delete("/deltalists/{listDbId}") + @Delete("/lists/{listDbId}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse>> deleteListById( @@ -115,7 +112,7 @@ public HttpResponse>> deleteListById( hardDelete = "true".equals(paramValue); } try { - listService.deleteBrAPIList(listDbId, programId, hardDelete); + brapiListService.deleteBrAPIList(listDbId, programId, hardDelete); return HttpResponse.status(HttpStatus.NO_CONTENT); } catch (Exception e) { log.info(e.getMessage(), e); @@ -123,7 +120,7 @@ public HttpResponse>> deleteListById( } } - @Get("/deltalists/{listDbId}") + @Get("/lists/{listDbId}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) @SuppressWarnings("unchecked") @@ -133,7 +130,7 @@ public HttpResponse>>> g HttpRequest request) { try { // Get the list from the BrAPI service - DeltaListDetails details = listService.getDeltaListDetails(listDbId, programId); + DeltaListDetails details = brapiListService.getDeltaListDetails(listDbId, programId); // Get a new instance of BrAPI query matching the type of list contents T queryParams = (T) details.getQuery(); @@ -167,7 +164,7 @@ public HttpResponse>>> g } } - @Get("/deltalists/{listDbId}/export{?fileExtension}") + @Get("/lists/{listDbId}/export{?fileExtension}") @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse germplasmListExport( @@ -175,7 +172,7 @@ public HttpResponse germplasmListExport( String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; try { // Get the list from the BrAPI service - DeltaListDetails details = listService.getDeltaListDetails(listDbId, programId); + DeltaListDetails details = brapiListService.getDeltaListDetails(listDbId, programId); FileType extension = Enum.valueOf(FileType.class, fileExtension); DownloadFile listContentsFile = details.exportListObjects(extension); diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index 801b9775c..ed7db131c 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -17,7 +17,12 @@ package org.breedinginsight.brapi.v2.dao; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.exceptions.HttpStatusException; import lombok.extern.slf4j.Slf4j; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; import org.brapi.client.v2.ApiResponse; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.model.queryParams.core.ListQueryParams; @@ -31,19 +36,24 @@ import org.brapi.v2.model.core.request.BrAPIListSearchRequest; import org.brapi.v2.model.core.response.*; import org.brapi.v2.model.pheno.BrAPIObservation; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.daos.ProgramDAO; +import org.breedinginsight.model.ProgramBrAPIEndpoints; +import org.breedinginsight.model.delta.DeltaEntityFactory; +import org.breedinginsight.model.delta.DeltaListDetails; +import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; +import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.BrAPIDAOUtil; import org.breedinginsight.utilities.Utilities; import javax.inject.Inject; import javax.validation.constraints.NotNull; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.TimeUnit; @Slf4j public class BrAPIListDAO { @@ -52,13 +62,17 @@ public class BrAPIListDAO { private ImportDAO importDAO; private final BrAPIDAOUtil brAPIDAOUtil; private final BrAPIEndpointProvider brAPIEndpointProvider; + private final DeltaEntityFactory deltaEntityFactory; + private final ProgramService programService; @Inject - public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider) { + public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, DeltaEntityFactory deltaEntityFactory, ProgramService programService) { this.programDAO = programDAO; this.importDAO = importDAO; this.brAPIDAOUtil = brAPIDAOUtil; this.brAPIEndpointProvider = brAPIEndpointProvider; + this.deltaEntityFactory = deltaEntityFactory; + this.programService = programService; } public List getListByName(List listNames, UUID programId) throws ApiException { @@ -196,4 +210,58 @@ public List createBrAPILists(List brapiLi throw new ApiException("No response after creating list"); } + + public DeltaListDetails getDeltaListDetailsByDbId(String listDbId, UUID programId) throws ApiException { + ListsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ListsApi.class); + ApiResponse response = api.listsListDbIdGet(listDbId); + if (Objects.isNull(response.getBody()) || Objects.isNull(response.getBody().getResult())) + { + throw new ApiException(); + } + + BrAPIListDetails details = response.getBody().getResult(); + return deltaEntityFactory.makeDeltaListDetailsBean(details); + } + + public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); + var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); + requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); + HttpUrl url = requestUrl.build(); + var brapiRequest = new Request.Builder().url(url) + .method("DELETE", null) + .addHeader("Content-Type", "application/json") + .build(); + + makeCall(brapiRequest); + } + + private void makeCall(Request brapiRequest) throws ApiException { + OkHttpClient client = new OkHttpClient.Builder() + .readTimeout(5, TimeUnit.MINUTES) + .build(); + try { + client.newCall(brapiRequest).execute(); + } catch (IOException e) { + log.error("Error calling BrAPI Service", e); + throw new ApiException("Error calling BrAPI Service"); + } + } + + private String getProgramBrAPIBaseUrl(UUID programId) { + ProgramBrAPIEndpoints programBrAPIEndpoints; + try { + programBrAPIEndpoints = programService.getBrapiEndpoints(programId); + } catch (DoesNotExistException e) { + throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); + } + + if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { + log.error("Program: " + programId + " is missing BrAPI URL config"); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); + } + var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); + programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; + return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; + } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java b/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java index 88f6d142c..92752fad6 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java +++ b/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java @@ -27,6 +27,9 @@ public class GermplasmQuery extends BrapiQuery { // This is a meta-parameter, it describes the display format of any date fields. private String dateDisplayFormat; + // The list id used to get a collection of germplasm + private String list; + public SearchRequest constructSearchRequest() { List filters = new ArrayList<>(); if (!StringUtils.isBlank(getImportEntryNumber())) { diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java index 3353ac17c..7dd7c81c6 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java @@ -12,6 +12,7 @@ import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.model.Program; +import org.breedinginsight.model.delta.DeltaListDetails; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.Utilities; @@ -19,6 +20,7 @@ import javax.inject.Singleton; import java.util.List; import java.util.Optional; +import java.util.UUID; import java.util.stream.Collectors; @Slf4j @@ -88,4 +90,12 @@ public List getListSummariesByTypeAndXref( return programLists; } + + public DeltaListDetails getDeltaListDetails(String listDbId, UUID programId) throws ApiException { + return listDAO.getDeltaListDetailsByDbId(listDbId, programId); + } + + public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + listDAO.deleteBrAPIList(listDbId, programId, hardDelete); + } } diff --git a/src/main/java/org/breedinginsight/daos/ListDAO.java b/src/main/java/org/breedinginsight/daos/ListDAO.java deleted file mode 100644 index f44a8757f..000000000 --- a/src/main/java/org/breedinginsight/daos/ListDAO.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.breedinginsight.daos; - -import io.micronaut.http.HttpStatus; -import io.micronaut.http.exceptions.HttpStatusException; -import lombok.extern.slf4j.Slf4j; -import okhttp3.*; -import org.brapi.client.v2.ApiResponse; -import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.client.v2.modules.core.ListsApi; -import org.brapi.v2.model.core.response.BrAPIListDetails; -import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; -import org.breedinginsight.brapi.v1.controller.BrapiVersion; -import org.breedinginsight.model.ProgramBrAPIEndpoints; -import org.breedinginsight.model.delta.DeltaEntityFactory; -import org.breedinginsight.model.delta.DeltaListDetails; -import org.breedinginsight.services.ProgramService; -import org.breedinginsight.services.brapi.BrAPIEndpointProvider; -import org.breedinginsight.services.exceptions.DoesNotExistException; -import org.breedinginsight.utilities.BrAPIDAOUtil; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.io.IOException; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -@Slf4j -@Singleton -public class ListDAO { - private final ProgramDAO programDAO; - private final BrAPIDAOUtil brAPIDAOUtil; - private final BrAPIEndpointProvider brAPIEndpointProvider; - private final DeltaEntityFactory deltaEntityFactory; - private final ProgramService programService; - - @Inject - public ListDAO(ProgramDAO programDAO, - BrAPIDAOUtil brAPIDAOUtil, - BrAPIEndpointProvider brAPIEndpointProvider, - DeltaEntityFactory deltaEntityFactory, ProgramService programService) { - this.programDAO = programDAO; - this.brAPIDAOUtil = brAPIDAOUtil; - this.brAPIEndpointProvider = brAPIEndpointProvider; - this.deltaEntityFactory = deltaEntityFactory; - this.programService = programService; - } - - public DeltaListDetails getDeltaListDetailsByDbId(String listDbId, UUID programId) throws ApiException { - ListsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ListsApi.class); - ApiResponse response = api.listsListDbIdGet(listDbId); - if (Objects.isNull(response.getBody()) || Objects.isNull(response.getBody().getResult())) - { - throw new ApiException(); - } - - BrAPIListDetails details = response.getBody().getResult(); - return deltaEntityFactory.makeDeltaListDetailsBean(details); - } - - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { - var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); - var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); - requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); - HttpUrl url = requestUrl.build(); - var brapiRequest = new Request.Builder().url(url) - .method("DELETE", null) - .addHeader("Content-Type", "application/json") - .build(); - - makeCall(brapiRequest); - } - - private void makeCall(Request brapiRequest) throws ApiException { - OkHttpClient client = new OkHttpClient.Builder() - .readTimeout(5, TimeUnit.MINUTES) - .build(); - try { - client.newCall(brapiRequest).execute(); - } catch (IOException e) { - log.error("Error calling BrAPI Service", e); - throw new ApiException("Error calling BrAPI Service"); - } - } - - private String getProgramBrAPIBaseUrl(UUID programId) { - ProgramBrAPIEndpoints programBrAPIEndpoints; - try { - programBrAPIEndpoints = programService.getBrapiEndpoints(programId); - } catch (DoesNotExistException e) { - throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); - } - - if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { - log.error("Program: " + programId + " is missing BrAPI URL config"); - throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); - } - var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); - programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; - return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; - } -} diff --git a/src/main/java/org/breedinginsight/services/ListService.java b/src/main/java/org/breedinginsight/services/ListService.java deleted file mode 100644 index c0c18f734..000000000 --- a/src/main/java/org/breedinginsight/services/ListService.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.breedinginsight.services; - -import lombok.extern.slf4j.Slf4j; -import org.brapi.client.v2.model.exceptions.ApiException; -import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; -import org.breedinginsight.daos.ListDAO; -import org.breedinginsight.model.delta.DeltaListDetails; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.UUID; - -@Slf4j -@Singleton -public class ListService { - private final BrAPIListDAO brAPIListDAO; - private final ListDAO listDAO; - - @Inject - public ListService(BrAPIListDAO brAPIListDAO, ListDAO listDAO) { - this.brAPIListDAO = brAPIListDAO; - this.listDAO = listDAO; - } - - public DeltaListDetails getDeltaListDetails(String listDbId, UUID programId) throws ApiException { - return listDAO.getDeltaListDetailsByDbId(listDbId, programId); - } - - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { - listDAO.deleteBrAPIList(listDbId, programId, hardDelete); - } - -} From 095342ffefcaa651bc4cae86b557eba0bbaf527c Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:51:38 -0500 Subject: [PATCH 031/132] revert GET list data objects from ListController --- .../brapi/v2/BrAPIGermplasmController.java | 24 ++-- .../brapi/v2/BrAPIListController.java | 124 +----------------- .../brapi/v2/dao/BrAPIListDAO.java | 31 ++--- .../v2/services/BrAPIGermplasmService.java | 14 +- .../brapi/v2/services/BrAPIListService.java | 5 - .../model/delta/DeltaEntityFactory.java | 57 +------- .../delta/DeltaGermplasmListDetails.java | 62 --------- .../delta/DeltaGermplasmListSummary.java | 16 --- .../model/delta/DeltaListDetails.java | 78 ----------- .../model/delta/DeltaListSummary.java | 20 --- .../breedinginsight/utilities/Utilities.java | 2 - 11 files changed, 40 insertions(+), 393 deletions(-) delete mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java delete mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java delete mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java delete mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java index 57ead5bd3..94cf9c9fc 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java @@ -1,5 +1,6 @@ package org.breedinginsight.brapi.v2; +import com.drew.lang.annotations.Nullable; import io.micronaut.context.annotation.Property; import io.micronaut.http.HttpHeaders; import io.micronaut.http.HttpResponse; @@ -19,10 +20,11 @@ import org.brapi.v2.model.BrAPIIndexPagination; import org.brapi.v2.model.BrAPIMetadata; import org.brapi.v2.model.BrAPIStatus; -import org.brapi.v2.model.core.BrAPITrial; import org.brapi.v2.model.germ.*; import org.brapi.v2.model.germ.request.BrAPIGermplasmSearchRequest; -import org.brapi.v2.model.germ.response.*; +import org.brapi.v2.model.germ.response.BrAPIGermplasmListResponse; +import org.brapi.v2.model.germ.response.BrAPIGermplasmPedigreeResponse; +import org.brapi.v2.model.germ.response.BrAPIGermplasmProgenyResponse; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; import org.breedinginsight.api.model.v1.request.query.SearchRequest; @@ -33,27 +35,25 @@ import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; import org.breedinginsight.brapi.v2.model.request.query.GermplasmQuery; -import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; -import org.breedinginsight.model.Program; -import org.breedinginsight.services.ProgramService; -import org.breedinginsight.utilities.Utilities; -import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; import org.breedinginsight.brapps.importer.model.exports.FileType; import org.breedinginsight.daos.ProgramDAO; import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.GermplasmGenotype; +import org.breedinginsight.model.Program; +import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; import org.breedinginsight.services.exceptions.AuthorizationException; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.services.geno.GenotypeService; +import org.breedinginsight.utilities.Utilities; import org.breedinginsight.utilities.response.ResponseUtils; +import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; import javax.inject.Inject; import javax.validation.Valid; import java.util.*; import java.util.regex.Pattern; -import java.util.stream.Collectors; @Slf4j @Controller("/${micronaut.bi.api.version}") @@ -222,15 +222,17 @@ public HttpResponse>>> getGermplasm( } } - @Get("/programs/{programId}/germplasm/export{?fileExtension}") + @Get("/programs/{programId}/germplasm/export{?fileExtension,list}") @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse germplasmExport( - @PathVariable("programId") UUID programId, @QueryValue(defaultValue = "XLSX") String fileExtension) { + @PathVariable("programId") UUID programId, + @QueryValue(defaultValue = "XLSX") String fileExtension, + @QueryValue Optional list) { String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; try { FileType extension = Enum.valueOf(FileType.class, fileExtension); - DownloadFile germplasmListFile = germplasmService.exportGermplasm(programId, extension); + DownloadFile germplasmListFile = list.isEmpty() ? germplasmService.exportGermplasm(programId, extension) : germplasmService.exportGermplasmList(programId, list.get(), extension); HttpResponse germplasmExport = HttpResponse.ok(germplasmListFile.getStreamedFile()).header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="+germplasmListFile.getFileName()+extension.getExtension()); return germplasmExport; } diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 2d3324a65..3cb73dbae 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -1,10 +1,10 @@ package org.breedinginsight.brapi.v2; -import io.micronaut.core.beans.BeanIntrospection; -import io.micronaut.core.beans.BeanProperty; -import io.micronaut.http.*; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; import io.micronaut.http.annotation.*; -import io.micronaut.http.server.types.files.StreamedFile; import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; @@ -18,27 +18,19 @@ import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.api.model.v1.validators.QueryValid; import org.breedinginsight.brapi.v1.controller.BrapiVersion; -import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; import org.breedinginsight.brapi.v2.model.request.query.ListQuery; import org.breedinginsight.brapi.v2.services.BrAPIListService; -import org.breedinginsight.brapps.importer.model.exports.FileType; -import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.Program; -import org.breedinginsight.model.delta.DeltaListDetails; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.response.ResponseUtils; -import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; import org.breedinginsight.utilities.response.mappers.ListQueryMapper; import javax.inject.Inject; -import javax.validation.ConstraintViolation; import javax.validation.Valid; import javax.validation.Validator; import java.util.List; -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; @Slf4j @Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @@ -105,7 +97,7 @@ public HttpResponse>> deleteListById( @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, HttpRequest request - ) throws DoesNotExistException, ApiException { + ) { boolean hardDelete = false; if (request.getParameters().contains("hardDelete")) { String paramValue = request.getParameters().get("hardDelete"); @@ -119,110 +111,4 @@ public HttpResponse>> deleteListById( return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving germplasm list records"); } } - - @Get("/lists/{listDbId}") - @Produces(MediaType.APPLICATION_JSON) - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - @SuppressWarnings("unchecked") - public HttpResponse>>> getListById( - @PathVariable("programId") UUID programId, - @PathVariable("listDbId") String listDbId, - HttpRequest request) { - try { - // Get the list from the BrAPI service - DeltaListDetails details = brapiListService.getDeltaListDetails(listDbId, programId); - - // Get a new instance of BrAPI query matching the type of list contents - T queryParams = (T) details.getQuery(); - - // Bind query parameters to the object - bindQueryParams(queryParams, request); - - // Perform standard bean validation - Set> violations = validator.validate(queryParams); - if (!violations.isEmpty()) { - List errorMessages = violations.stream() - .map(ConstraintViolation::getMessage) - .collect(Collectors.toList()); - log.info(String.join(", ", errorMessages)); - return HttpResponse.status(HttpStatus.BAD_REQUEST, "Error with list contents search parameters"); - } - - // Fetch the list contents from the BrAPI service - List listContentsBrAPIObjects = (List) details.getDataObjects(); - - // Construct a search request for sorting the list contents - SearchRequest searchRequest = details.constructSearchRequest(queryParams); - - // Get the map used to connect query sorting keys to contents object values - AbstractQueryMapper contentsQueryMapper = details.getQueryMapper(); - - return ResponseUtils.getBrapiQueryResponse(listContentsBrAPIObjects, contentsQueryMapper, queryParams, searchRequest); - } catch (Exception e) { - log.info(e.getMessage(), e); - return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving list records"); - } - } - - @Get("/lists/{listDbId}/export{?fileExtension}") - @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - public HttpResponse germplasmListExport( - @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, @QueryValue(defaultValue = "XLSX") String fileExtension) { - String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; - try { - // Get the list from the BrAPI service - DeltaListDetails details = brapiListService.getDeltaListDetails(listDbId, programId); - - FileType extension = Enum.valueOf(FileType.class, fileExtension); - DownloadFile listContentsFile = details.exportListObjects(extension); - return HttpResponse.ok(listContentsFile.getStreamedFile()).header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="+listContentsFile.getFileName()+extension.getExtension()); - } - catch (Exception e) { - log.info(e.getMessage(), e); - e.printStackTrace(); - HttpResponse response = HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, downloadErrorMessage).contentType(MediaType.TEXT_PLAIN).body(downloadErrorMessage); - return response; - } - } - - private void bindQueryParams(BrapiQuery queryParams, HttpRequest request) { - BeanIntrospection introspection = BeanIntrospection.getIntrospection(BrapiQuery.class); - for (BeanProperty property : introspection.getBeanProperties()) { - String paramName = property.getName(); - if (request.getParameters().contains(paramName)) { - String paramValue = request.getParameters().get(paramName); - Object convertedValue; - Class propertyType = property.getType(); - - if (propertyType.isEnum()) { - convertedValue = convertToEnum(paramValue, (Class>) propertyType); - } else { - convertedValue = convertValue(paramValue, propertyType); - } - - property.set(queryParams, convertedValue); - } - } - } - - private > T convertToEnum(String value, Class> enumClass) { - if (value == null) { - return null; - } - return Enum.valueOf((Class) enumClass, value.toUpperCase()); - } - - - // Convert, if necessary, the values of query parameters to match the type defined for the fields in the BrapiQuery class - private Object convertValue(String value, Class targetType) { - // Implement type conversion logic here - // Other list content types might need more complex logic - if (targetType == String.class) return value; - if (targetType == Integer.class) return Integer.parseInt(value); - if (targetType == Long.class) return Long.parseLong(value); - if (targetType == Boolean.class) return Boolean.parseBoolean(value); - // Add more type conversions as needed - return value; - } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index ed7db131c..55b395ea3 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -25,7 +25,6 @@ import okhttp3.Request; import org.brapi.client.v2.ApiResponse; import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.client.v2.model.queryParams.core.ListQueryParams; import org.brapi.client.v2.modules.core.ListsApi; import org.brapi.v2.model.BrAPIExternalReference; import org.brapi.v2.model.BrAPIResponse; @@ -34,15 +33,15 @@ import org.brapi.v2.model.core.BrAPIListTypes; import org.brapi.v2.model.core.request.BrAPIListNewRequest; import org.brapi.v2.model.core.request.BrAPIListSearchRequest; -import org.brapi.v2.model.core.response.*; -import org.brapi.v2.model.pheno.BrAPIObservation; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.brapi.v2.model.core.response.BrAPIListsListResponse; +import org.brapi.v2.model.core.response.BrAPIListsListResponseResult; +import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.daos.ProgramDAO; import org.breedinginsight.model.ProgramBrAPIEndpoints; -import org.breedinginsight.model.delta.DeltaEntityFactory; -import org.breedinginsight.model.delta.DeltaListDetails; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; import org.breedinginsight.services.exceptions.DoesNotExistException; @@ -52,7 +51,10 @@ import javax.inject.Inject; import javax.validation.constraints.NotNull; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; import java.util.concurrent.TimeUnit; @Slf4j @@ -62,16 +64,14 @@ public class BrAPIListDAO { private ImportDAO importDAO; private final BrAPIDAOUtil brAPIDAOUtil; private final BrAPIEndpointProvider brAPIEndpointProvider; - private final DeltaEntityFactory deltaEntityFactory; private final ProgramService programService; @Inject - public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, DeltaEntityFactory deltaEntityFactory, ProgramService programService) { + public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, ProgramService programService) { this.programDAO = programDAO; this.importDAO = importDAO; this.brAPIDAOUtil = brAPIDAOUtil; this.brAPIEndpointProvider = brAPIEndpointProvider; - this.deltaEntityFactory = deltaEntityFactory; this.programService = programService; } @@ -211,19 +211,8 @@ public List createBrAPILists(List brapiLi throw new ApiException("No response after creating list"); } - public DeltaListDetails getDeltaListDetailsByDbId(String listDbId, UUID programId) throws ApiException { - ListsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ListsApi.class); - ApiResponse response = api.listsListDbIdGet(listDbId); - if (Objects.isNull(response.getBody()) || Objects.isNull(response.getBody().getResult())) - { - throw new ApiException(); - } - - BrAPIListDetails details = response.getBody().getResult(); - return deltaEntityFactory.makeDeltaListDetailsBean(details); - } - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + // TODO: Switch to using the ListsApi from the BrAPI client library once the delete endpoints are merged into it var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java index 8023327d7..231978368 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java @@ -82,7 +82,7 @@ public Optional getGermplasmByDBID(UUID programId, String germpl return germplasmDAO.getGermplasmByDBID(germplasmId, programId); } - public List> processListData(List germplasm, BrAPIListDetails germplasmList, Program program){ + public List> processListData(List germplasm, List listData, Program program){ Map germplasmByName = new HashMap<>(); for (BrAPIGermplasm g: germplasm) { // Use the full, unique germplasmName with programKey and accessionNumber (GID) for 2 reasons: @@ -97,14 +97,14 @@ public List> processListData(List germplasm, // This holds the BrAPI list items or all germplasm in a program if the list is null. List orderedGermplasmNames = new ArrayList<>(); - if (germplasmList == null) { + if (listData == null) { orderedGermplasmNames = germplasm.stream().sorted((left, right) -> { Integer leftAccessionNumber = Integer.parseInt(left.getAccessionNumber()); Integer rightAccessionNumber = Integer.parseInt(right.getAccessionNumber()); return leftAccessionNumber.compareTo(rightAccessionNumber); }).map(BrAPIGermplasm::getGermplasmName).collect(Collectors.toList()); } else { - orderedGermplasmNames = germplasmList.getData(); + orderedGermplasmNames = listData; } // For export, assign entry number sequentially based on BrAPI list order. @@ -124,7 +124,7 @@ public List> processListData(List germplasm, row.put("Source", source); // Use the entry number in the list map if generated - if(germplasmList == null) { + if(listData == null) { // Not downloading a real list, use GID (https://breedinginsight.atlassian.net/browse/BI-2266). row.put("Entry No", Integer.valueOf(germplasmEntry.getAccessionNumber())); } else { @@ -254,7 +254,9 @@ public DownloadFile exportGermplasm(UUID programId, FileType fileExtension) thro return new DownloadFile(fileName, downloadFile); } - public DownloadFile exportGermplasmList(UUID programId, String listId, FileType fileExtension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException { + public DownloadFile exportGermplasmList(UUID programId, + String listId, + FileType fileExtension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException { List columns = GermplasmFileColumns.getOrderedColumns(); //Retrieve germplasm list data @@ -270,7 +272,7 @@ public DownloadFile exportGermplasmList(UUID programId, String listId, FileType String fileName = createFileName(listData, listName); StreamedFile downloadFile; //Convert list data to List> data to pass into file writer - List> processedData = processListData(germplasm, listData, program); + List> processedData = processListData(germplasm, germplasmNames, program); if (fileExtension == FileType.CSV){ downloadFile = CSVWriter.writeToDownload(columns, processedData, fileExtension); diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java index 7dd7c81c6..1ee77e310 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java @@ -12,7 +12,6 @@ import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.model.Program; -import org.breedinginsight.model.delta.DeltaListDetails; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.Utilities; @@ -91,10 +90,6 @@ public List getListSummariesByTypeAndXref( return programLists; } - public DeltaListDetails getDeltaListDetails(String listDbId, UUID programId) throws ApiException { - return listDAO.getDeltaListDetailsByDbId(listDbId, programId); - } - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { listDAO.deleteBrAPIList(listDbId, programId, hardDelete); } diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java index 8d473ce5a..f1724ef12 100644 --- a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java +++ b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java @@ -7,19 +7,16 @@ import lombok.NonNull; import org.brapi.v2.model.core.BrAPIListSummary; import org.brapi.v2.model.core.BrAPIListTypes; -import org.brapi.v2.model.core.response.BrAPIListDetails; -import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; -import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; -import org.breedinginsight.brapps.importer.services.processors.experiment.service.ObservationUnitService; - import org.brapi.v2.model.core.BrAPIStudy; import org.brapi.v2.model.core.BrAPITrial; +import org.brapi.v2.model.core.response.BrAPIListDetails; import org.brapi.v2.model.germ.BrAPIGermplasm; import org.brapi.v2.model.pheno.BrAPIObservation; import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.brapi.v2.model.pheno.BrAPIObservationVariable; +import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; +import org.breedinginsight.brapps.importer.services.processors.experiment.service.ObservationUnitService; import org.breedinginsight.model.ProgramLocation; -import org.breedinginsight.utilities.Utilities; import javax.inject.Inject; @@ -27,16 +24,10 @@ public class DeltaEntityFactory { private final ObservationUnitService observationUnitService; - private final BrAPIGermplasmService germplasmService; - private final String applicationReferenceSourceBase; @Inject - public DeltaEntityFactory(ObservationUnitService observationUnitService, - BrAPIGermplasmService germplasmService, - @Property(name = "brapi.server.reference-source") String applicationReferenceSourceBase) { + public DeltaEntityFactory(ObservationUnitService observationUnitService) { this.observationUnitService = observationUnitService; - this.germplasmService = germplasmService; - this.applicationReferenceSourceBase = applicationReferenceSourceBase; } private Experiment makeExperiment(BrAPITrial brAPIObject) { @@ -67,46 +58,6 @@ private DeltaObservationVariable makeDeltaObservationVariable(BrAPIObservationVa return new DeltaObservationVariable(brAPIObject); } - private DeltaGermplasmListSummary makeDeltaGermplasmListSummary(BrAPIListSummary brAPIObject) { - return new DeltaGermplasmListSummary(brAPIObject); - } - - private DeltaGermplasmListDetails makeDeltaGermplasmListDetails(BrAPIListDetails brAPIObject, String referenceSourceBase, BrAPIGermplasmService germplasmService) { - return new DeltaGermplasmListDetails(brAPIObject, referenceSourceBase, germplasmService); - } - - @Bean - @Prototype - public DeltaListSummary makeDeltaListSummaryBean(@NonNull BrAPIListSummary brAPIObject) { - BrAPIListTypes listType = brAPIObject.getListType(); - switch (listType) { - case GERMPLASM: return makeDeltaGermplasmListSummary(brAPIObject); - default: return null; - } - } - - @Bean - @Prototype - public DeltaListDetails makeDeltaListDetailsBean(@NonNull BrAPIListDetails brAPIObject) { - BrAPIListTypes listType = brAPIObject.getListType(); - switch (listType) { - case GERMPLASM: return makeDeltaGermplasmListDetailsBean(brAPIObject); - default: return null; - } - } - - @Bean - @Prototype - public DeltaGermplasmListSummary makeDeltaGermplasmListSummaryBean(@NonNull BrAPIListSummary brAPIObject) { - return makeDeltaGermplasmListSummary(brAPIObject); - } - - @Bean - @Prototype - public DeltaGermplasmListDetails makeDeltaGermplasmListDetailsBean(@NonNull BrAPIListDetails brAPIObject) { - return makeDeltaGermplasmListDetails(brAPIObject, applicationReferenceSourceBase, germplasmService); - } - @Bean @Prototype public Experiment makeExperimentBean(@NonNull BrAPITrial brAPIObject) { diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java deleted file mode 100644 index 1f8ea3d9c..000000000 --- a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.breedinginsight.model.delta; - -import io.micronaut.context.annotation.Prototype; -import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.v2.model.core.response.BrAPIListDetails; -import org.brapi.v2.model.germ.BrAPIGermplasm; -import org.breedinginsight.api.model.v1.request.query.SearchRequest; -import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; -import org.breedinginsight.brapi.v2.model.request.query.GermplasmQuery; -import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; -import org.breedinginsight.brapps.importer.model.exports.FileType; -import org.breedinginsight.model.DownloadFile; -import org.breedinginsight.services.exceptions.DoesNotExistException; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; -import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; -import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; - -import java.io.IOException; -import java.util.List; -import java.util.UUID; - - -@Prototype -public class DeltaGermplasmListDetails extends DeltaListDetails { - - private final BrAPIGermplasmService germplasmService; - - // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. - DeltaGermplasmListDetails(BrAPIListDetails brAPIListDetails, - String referenceSourceBase, - BrAPIGermplasmService germplasmService) { - super(brAPIListDetails, referenceSourceBase); - this.germplasmService = germplasmService; - } - - @Override - public List getDataObjects() throws ApiException, UnprocessableEntityException { - UUID programId = getProgramId().orElseThrow(() -> new UnprocessableEntityException("Program Id not found for list " + getListName())); - return germplasmService.getGermplasmByList(programId, getListDbId()); - } - - @Override - public DownloadFile exportListObjects(FileType extension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException, UnprocessableEntityException { - UUID programId = getProgramId().orElseThrow(() -> new UnprocessableEntityException("Program Id not found for list " + getListName())); - return germplasmService.exportGermplasmList(programId, getListDbId(), extension); - } - - @Override - public AbstractQueryMapper getQueryMapper() { - return new GermplasmQueryMapper(); - } - - @Override - public BrapiQuery getQuery() { - return new GermplasmQuery(); - } - - @Override - public SearchRequest constructSearchRequest(BrapiQuery queryParams) { - return ((GermplasmQuery) queryParams).constructSearchRequest(); - } -} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java deleted file mode 100644 index 6ddd2518b..000000000 --- a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.breedinginsight.model.delta; - -import io.micronaut.context.annotation.Prototype; -import lombok.Getter; -import lombok.Setter; -import org.brapi.v2.model.core.BrAPIListSummary; -import org.breedinginsight.brapps.importer.model.response.ImportObjectState; - - -@Prototype -public class DeltaGermplasmListSummary extends DeltaListSummary { - - // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. - DeltaGermplasmListSummary(BrAPIListSummary brAPIListSummary) { super(brAPIListSummary); } - -} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java b/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java deleted file mode 100644 index 9f2c745bf..000000000 --- a/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.breedinginsight.model.delta; - -import io.micronaut.context.annotation.Prototype; -import lombok.Getter; -import lombok.Setter; -import lombok.Value; -import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.v2.model.BrAPIExternalReference; -import org.brapi.v2.model.core.BrAPIListTypes; -import org.brapi.v2.model.core.response.BrAPIListDetails; -import org.breedinginsight.api.model.v1.request.query.SearchRequest; -import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; -import org.breedinginsight.brapps.importer.model.exports.FileType; -import org.breedinginsight.brapps.importer.model.response.ImportObjectState; -import io.micronaut.context.annotation.Property; -import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; -import org.breedinginsight.model.DownloadFile; -import org.breedinginsight.services.exceptions.DoesNotExistException; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; -import org.breedinginsight.utilities.Utilities; -import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; - -import java.io.IOException; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; - - -@Prototype -public abstract class DeltaListDetails extends DeltaEntity { - private final String referenceSourceBase; - @Getter - @Setter - private ImportObjectState state; - - // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. - DeltaListDetails(BrAPIListDetails brAPIListDetails, String referenceSourceBase) { - super(brAPIListDetails); - this.referenceSourceBase = referenceSourceBase; - } - - public abstract List getDataObjects() throws ApiException, UnprocessableEntityException; - public abstract DownloadFile exportListObjects(FileType extension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException, UnprocessableEntityException; - public abstract AbstractQueryMapper getQueryMapper(); - - public abstract BrapiQuery getQuery(); - - public abstract SearchRequest constructSearchRequest(BrapiQuery queryParams); - - public BrAPIListTypes getListType() { - return entity.getListType(); - } - - public Optional getListId() { - return getXrefId(ExternalReferenceSource.LISTS); - } - - public Optional getProgramId() { - return getXrefId(ExternalReferenceSource.PROGRAMS); - } - - public String getListDbId() { - return entity.getListDbId(); - } - - public String getListName() { - return entity.getListName(); - } - - private Optional getXrefId(ExternalReferenceSource source) { - // Get the external reference if it exists - Optional xrefOptional = Utilities.getExternalReference(entity.getExternalReferences(), referenceSourceBase, source); - - // Parse the Deltabreed ID from the xref - return xrefOptional.map(BrAPIExternalReference::getReferenceId).map(UUID::fromString); - } -} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java b/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java deleted file mode 100644 index 80569d077..000000000 --- a/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.breedinginsight.model.delta; - -import io.micronaut.context.annotation.Prototype; -import lombok.Getter; -import lombok.Setter; -import org.brapi.v2.model.core.BrAPIListSummary; -import org.breedinginsight.brapps.importer.model.response.ImportObjectState; - - -@Prototype -public class DeltaListSummary extends DeltaEntity { - - @Getter - @Setter - private ImportObjectState state; - - // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. - DeltaListSummary(BrAPIListSummary brAPIListSummary) { super(brAPIListSummary); } - -} diff --git a/src/main/java/org/breedinginsight/utilities/Utilities.java b/src/main/java/org/breedinginsight/utilities/Utilities.java index 7ed72cfef..20f3254d6 100644 --- a/src/main/java/org/breedinginsight/utilities/Utilities.java +++ b/src/main/java/org/breedinginsight/utilities/Utilities.java @@ -17,7 +17,6 @@ package org.breedinginsight.utilities; -import io.micronaut.context.annotation.Property; import org.apache.commons.lang3.StringUtils; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.BrAPIExternalReference; @@ -25,7 +24,6 @@ import org.breedinginsight.model.Program; import org.flywaydb.core.api.migration.Context; -import javax.inject.Inject; import java.lang.reflect.Field; import java.sql.ResultSet; import java.sql.Statement; From e778c65690012e6871e14d9cd626f3aec821c267 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Wed, 20 Nov 2024 21:44:18 +0000 Subject: [PATCH 032/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index cba537926..e73ad356d 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+891 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/75e978c62c555b745b4779800c350d9780bbcb75 +version=v1.1.0+893 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/16cdff9a3f15138b35958dd4aee554ed1a7dc26c From 0a3fda74ccc5236ab60ee82755e9c1bcc5056f35 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:39:09 -0500 Subject: [PATCH 033/132] update tests --- .../org/breedinginsight/model/delta/DeltaEntityFactory.java | 5 ----- .../brapi/v2/GermplasmControllerIntegrationTest.java | 4 ++-- .../brapi/v2/ListControllerIntegrationTest.java | 5 +---- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java index f1724ef12..059f91e66 100644 --- a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java +++ b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java @@ -2,19 +2,14 @@ import io.micronaut.context.annotation.Bean; import io.micronaut.context.annotation.Factory; -import io.micronaut.context.annotation.Property; import io.micronaut.context.annotation.Prototype; import lombok.NonNull; -import org.brapi.v2.model.core.BrAPIListSummary; -import org.brapi.v2.model.core.BrAPIListTypes; import org.brapi.v2.model.core.BrAPIStudy; import org.brapi.v2.model.core.BrAPITrial; -import org.brapi.v2.model.core.response.BrAPIListDetails; import org.brapi.v2.model.germ.BrAPIGermplasm; import org.brapi.v2.model.pheno.BrAPIObservation; import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.brapi.v2.model.pheno.BrAPIObservationVariable; -import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; import org.breedinginsight.brapps.importer.services.processors.experiment.service.ObservationUnitService; import org.breedinginsight.model.ProgramLocation; diff --git a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java index 23e8f54d4..7c80482d9 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java @@ -245,7 +245,7 @@ public void getAllGermplasmByListSuccess() { String germplasmListDbId = fetchGermplasmListDbId(programId); // Build the endpoint to get germplasm by germplasm list. - String endpoint = String.format("/programs/%s/germplasm/lists/%s/records", programId, germplasmListDbId); + String endpoint = String.format("/programs/%s/brapi/v2/germplasm?list=%s", programId, germplasmListDbId); // Get germplasm by list. Flowable> call = client.exchange( @@ -258,7 +258,7 @@ public void getAllGermplasmByListSuccess() { JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject().getAsJsonObject("result"); JsonArray data = result.getAsJsonArray("data"); - assertEquals(data.size(), 3, "Wrong number of germplasm were returned"); + assertEquals(3, data.size(), "Wrong number of germplasm were returned"); } @Test diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index 9515b72a6..e29ce7e24 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -145,9 +145,7 @@ public void setup() { newExp.put(traits.get(0).getObservationVariableName(), "1"); JsonObject result = importTestUtils.uploadAndFetchWorkflow( - importTestUtils.writeExperimentDataToFile(List.of(newExp), traits), null, true, client, program, mappingId, newExperimentWorkflowId - ); - + importTestUtils.writeExperimentDataToFile(List.of(newExp), traits), null, true, client, program, mappingId, newExperimentWorkflowId); } @Test @@ -189,5 +187,4 @@ public void getAllListsSuccess() { } } } - } From f6d67453aafa968067032d2f94f1a99701be878d Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Fri, 22 Nov 2024 15:22:47 -0500 Subject: [PATCH 034/132] [BI-2376] - renamed lists methods --- .../java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java | 6 ++---- .../breedinginsight/brapi/v2/services/BrAPIListService.java | 2 +- .../brapi/v2/services/BrAPITrialService.java | 2 +- .../importer/services/processors/ExperimentProcessor.java | 4 ++-- .../steps/PopulateExistingPendingImportObjectsStep.java | 2 +- .../processors/experiment/service/DatasetService.java | 6 +----- 6 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index 801b9775c..5f64eb5f5 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -20,7 +20,6 @@ import lombok.extern.slf4j.Slf4j; import org.brapi.client.v2.ApiResponse; import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.client.v2.model.queryParams.core.ListQueryParams; import org.brapi.client.v2.modules.core.ListsApi; import org.brapi.v2.model.BrAPIExternalReference; import org.brapi.v2.model.BrAPIResponse; @@ -30,7 +29,6 @@ import org.brapi.v2.model.core.request.BrAPIListNewRequest; import org.brapi.v2.model.core.request.BrAPIListSearchRequest; import org.brapi.v2.model.core.response.*; -import org.brapi.v2.model.pheno.BrAPIObservation; import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.daos.ProgramDAO; @@ -82,7 +80,7 @@ public BrAPIListsSingleResponse getListById(String listId, UUID programId) throw return response.getBody(); } - public List getListBySearch(@NotNull BrAPIListSearchRequest searchRequest, UUID programId) throws ApiException { + public List getListsBySearch(@NotNull BrAPIListSearchRequest searchRequest, UUID programId) throws ApiException { ListsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ListsApi.class); List programLists = brAPIDAOUtil.search(api::searchListsPost, api::searchListsSearchResultsDbIdGet, searchRequest); if (searchRequest.getExternalReferenceSources() != null && searchRequest.getExternalReferenceIDs() != null) { @@ -94,7 +92,7 @@ public List getListBySearch(@NotNull BrAPIListSearchRequest se } - public List getListByTypeAndExternalRef(@NotNull BrAPIListTypes listType, UUID programId, String externalReferenceSource, UUID externalReferenceId) throws ApiException { + public List getListsByTypeAndExternalRef(@NotNull BrAPIListTypes listType, UUID programId, String externalReferenceSource, UUID externalReferenceId) throws ApiException { BrAPIListSearchRequest searchRequest = new BrAPIListSearchRequest() .externalReferenceIDs(List.of(externalReferenceId.toString())) .externalReferenceSources(List.of(externalReferenceSource)) diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java index 3353ac17c..23d37fc15 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java @@ -53,7 +53,7 @@ public List getListSummariesByTypeAndXref( if (xrefId != null && !xrefId.isEmpty()) { searchRequest.externalReferenceIDs(List.of(xrefId)); } - List lists = listDAO.getListBySearch(searchRequest, program.getId()); + List lists = listDAO.getListsBySearch(searchRequest, program.getId()); if (lists == null) { throw new DoesNotExistException("list not returned from BrAPI service"); } 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 c6b55fc76..b1c7333c3 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java @@ -638,7 +638,7 @@ private void addObsVarDataToRow( } public List getDatasetObsVars(String datasetId, Program program) throws ApiException, DoesNotExistException { - List lists = listDAO.getListByTypeAndExternalRef( + List lists = listDAO.getListsByTypeAndExternalRef( BrAPIListTypes.OBSERVATIONVARIABLES, program.getId(), String.format("%s/%s", referenceSource, ExternalReferenceSource.DATASET.getName()), 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 34030b795..0666870c6 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 @@ -2091,7 +2091,7 @@ private Map> initializeObsVarDatas try { List existingDatasets = brAPIListDAO - .getListByTypeAndExternalRef(BrAPIListTypes.OBSERVATIONVARIABLES, + .getListsByTypeAndExternalRef(BrAPIListTypes.OBSERVATIONVARIABLES, program.getId(), String.format("%s/%s", BRAPI_REFERENCE_SOURCE, ExternalReferenceSource.DATASET.getName()), UUID.fromString(datasetId)); @@ -2121,7 +2121,7 @@ private Map> initializeObsVarDatas ).getId().toString(); try { List existingDatasets = brAPIListDAO - .getListByTypeAndExternalRef(BrAPIListTypes.OBSERVATIONVARIABLES, + .getListsByTypeAndExternalRef(BrAPIListTypes.OBSERVATIONVARIABLES, program.getId(), String.format("%s/%s", BRAPI_REFERENCE_SOURCE, ExternalReferenceSource.DATASET.getName()), UUID.fromString(datasetId)); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/create/workflow/steps/PopulateExistingPendingImportObjectsStep.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/create/workflow/steps/PopulateExistingPendingImportObjectsStep.java index ab4233b0a..b6bca19d0 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/create/workflow/steps/PopulateExistingPendingImportObjectsStep.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/create/workflow/steps/PopulateExistingPendingImportObjectsStep.java @@ -383,7 +383,7 @@ private Map> initializeObsVarDatas try { List existingDatasets = brAPIListDAO - .getListByTypeAndExternalRef(BrAPIListTypes.OBSERVATIONVARIABLES, + .getListsByTypeAndExternalRef(BrAPIListTypes.OBSERVATIONVARIABLES, program.getId(), String.format("%s/%s", BRAPI_REFERENCE_SOURCE, ExternalReferenceSource.DATASET.getName()), UUID.fromString(datasetId)); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/DatasetService.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/DatasetService.java index f85137e15..68aca9e2b 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/DatasetService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/DatasetService.java @@ -23,21 +23,17 @@ import org.brapi.v2.model.BrAPIExternalReference; import org.brapi.v2.model.core.BrAPIListSummary; import org.brapi.v2.model.core.BrAPIListTypes; -import org.brapi.v2.model.core.BrAPITrial; import org.brapi.v2.model.core.response.BrAPIListDetails; -import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.model.response.ImportObjectState; import org.breedinginsight.brapps.importer.model.response.PendingImportObject; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.model.Program; -import org.breedinginsight.model.Trait; import org.breedinginsight.utilities.Utilities; import javax.inject.Inject; import javax.inject.Singleton; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -74,7 +70,7 @@ public Optional fetchDatasetById(String id, Program program) t // Retrieve existing dataset summaries based on program ID and external reference List existingDatasets = brAPIListDAO - .getListByTypeAndExternalRef(BrAPIListTypes.OBSERVATIONVARIABLES, + .getListsByTypeAndExternalRef(BrAPIListTypes.OBSERVATIONVARIABLES, program.getId(), String.format("%s/%s", BRAPI_REFERENCE_SOURCE, ExternalReferenceSource.DATASET.getName()), UUID.fromString(id)); From cd9498fe0fa0c420531d4471dcd699feed34619c Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Fri, 22 Nov 2024 15:23:42 -0500 Subject: [PATCH 035/132] [BI-2376] - added method stubs --- .../java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java | 5 +++++ .../org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java | 2 ++ .../brapi/v2/dao/impl/BrAPITrialDAOImpl.java | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index 5f64eb5f5..a5a7ed5c6 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -18,6 +18,7 @@ package org.breedinginsight.brapi.v2.dao; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.NotImplementedException; import org.brapi.client.v2.ApiResponse; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.modules.core.ListsApi; @@ -194,4 +195,8 @@ public List createBrAPILists(List brapiLi throw new ApiException("No response after creating list"); } + + public void deleteBrAPIList(String listDbId, UUID id, boolean hard) { + throw new NotImplementedException(); + } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java index 83ee3f47b..046ca5aad 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java @@ -45,4 +45,6 @@ List createBrAPITrials(List brAPITrialList, UUID program List getTrialsByDbIds(Collection trialDbIds, Program program) throws ApiException; List getTrialsByExperimentIds(Collection experimentIds, Program program) throws ApiException; + + void deleteExperiment(UUID experimentId, boolean hard); } diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java index 066c7017d..0a39d29aa 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java @@ -21,6 +21,7 @@ import io.micronaut.http.server.exceptions.InternalServerException; import io.micronaut.scheduling.annotation.Scheduled; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.NotImplementedException; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.modules.core.TrialsApi; import org.brapi.v2.model.BrAPIExternalReference; @@ -277,4 +278,9 @@ public List getTrialsByExperimentIds(Collection experimentIds, trialSearch ), program.getKey()); } + + @Override + public void deleteExperiment(UUID experimentId, boolean hard) { + throw new NotImplementedException(); + } } From 098219249048350fa770b301ea67215bc8d5cfbc Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:24:18 -0500 Subject: [PATCH 036/132] add httpok helper methods to BrAPIDAOUtil --- .../brapi/v2/dao/BrAPIListDAO.java | 39 +++------------ .../utilities/BrAPIDAOUtil.java | 47 +++++++++++++++++-- .../v2/ListControllerIntegrationTest.java | 32 +++++++++++++ .../daos/BrAPIDAOUtilUnitTest.java | 10 +++- ...gwaGenotypeServiceImplIntegrationTest.java | 7 ++- 5 files changed, 96 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index 55b395ea3..11e079842 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -64,15 +64,16 @@ public class BrAPIListDAO { private ImportDAO importDAO; private final BrAPIDAOUtil brAPIDAOUtil; private final BrAPIEndpointProvider brAPIEndpointProvider; - private final ProgramService programService; @Inject - public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, ProgramService programService) { + public BrAPIListDAO(ProgramDAO programDAO, + ImportDAO importDAO, + BrAPIDAOUtil brAPIDAOUtil, + BrAPIEndpointProvider brAPIEndpointProvider) { this.programDAO = programDAO; this.importDAO = importDAO; this.brAPIDAOUtil = brAPIDAOUtil; this.brAPIEndpointProvider = brAPIEndpointProvider; - this.programService = programService; } public List getListByName(List listNames, UUID programId) throws ApiException { @@ -213,7 +214,7 @@ public List createBrAPILists(List brapiLi public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { // TODO: Switch to using the ListsApi from the BrAPI client library once the delete endpoints are merged into it - var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); + var programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(programId); var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); HttpUrl url = requestUrl.build(); @@ -222,35 +223,7 @@ public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) .addHeader("Content-Type", "application/json") .build(); - makeCall(brapiRequest); + brAPIDAOUtil.makeCall(brapiRequest); } - private void makeCall(Request brapiRequest) throws ApiException { - OkHttpClient client = new OkHttpClient.Builder() - .readTimeout(5, TimeUnit.MINUTES) - .build(); - try { - client.newCall(brapiRequest).execute(); - } catch (IOException e) { - log.error("Error calling BrAPI Service", e); - throw new ApiException("Error calling BrAPI Service"); - } - } - - private String getProgramBrAPIBaseUrl(UUID programId) { - ProgramBrAPIEndpoints programBrAPIEndpoints; - try { - programBrAPIEndpoints = programService.getBrapiEndpoints(programId); - } catch (DoesNotExistException e) { - throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); - } - - if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { - log.error("Program: " + programId + " is missing BrAPI URL config"); - throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); - } - var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); - programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; - return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; - } } diff --git a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java index e03a67017..075d6bb2b 100644 --- a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java +++ b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java @@ -18,25 +18,33 @@ package org.breedinginsight.utilities; import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.exceptions.HttpStatusException; import io.micronaut.http.server.exceptions.InternalServerException; import io.reactivex.functions.*; import lombok.extern.slf4j.Slf4j; +import okhttp3.OkHttpClient; +import okhttp3.Request; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.brapi.client.v2.ApiResponse; import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.client.v2.modules.germplasm.GermplasmApi; import org.brapi.v2.model.*; -import org.brapi.v2.model.germ.BrAPIGermplasm; -import org.brapi.v2.model.germ.response.BrAPIGermplasmSingleResponse; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapps.importer.model.ImportUpload; +import org.breedinginsight.model.ProgramBrAPIEndpoints; +import org.breedinginsight.services.ProgramService; +import org.breedinginsight.services.exceptions.DoesNotExistException; import javax.inject.Inject; import javax.inject.Singleton; +import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.TimeUnit; import static org.brapi.v2.model.BrAPIWSMIMEDataTypes.APPLICATION_JSON; @@ -48,16 +56,19 @@ public class BrAPIDAOUtil { private final Duration searchTimeout; private final int pageSize; private final int postGroupSize; + private final ProgramService programService; @Inject public BrAPIDAOUtil(@Property(name = "brapi.search.wait-time") int searchWaitTime, @Property(name = "brapi.read-timeout") Duration searchTimeout, @Property(name = "brapi.page-size") int pageSize, - @Property(name = "brapi.post-group-size") int postGroupSize) { + @Property(name = "brapi.post-group-size") int postGroupSize, + ProgramService programService) { this.searchWaitTime = searchWaitTime; this.searchTimeout = searchTimeout; this.pageSize = pageSize; this.postGroupSize = postGroupSize; + this.programService = programService; } public List search(Function, Optional>>> searchMethod, @@ -366,4 +377,32 @@ public List post(List brapiObjects, return post(brapiObjects, null, postMethod, null); } + public void makeCall(Request brapiRequest) throws ApiException { + OkHttpClient client = new OkHttpClient.Builder() + .readTimeout(5, TimeUnit.MINUTES) + .build(); + try { + client.newCall(brapiRequest).execute(); + } catch (IOException e) { + log.error("Error calling BrAPI Service", e); + throw new ApiException("Error calling BrAPI Service"); + } + } + + public String getProgramBrAPIBaseUrl(UUID programId) { + ProgramBrAPIEndpoints programBrAPIEndpoints; + try { + programBrAPIEndpoints = programService.getBrapiEndpoints(programId); + } catch (DoesNotExistException e) { + throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); + } + + if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { + log.error("Program: " + programId + " is missing BrAPI URL config"); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); + } + var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); + programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; + return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; + } } diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index e29ce7e24..84f7385b9 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -49,6 +49,7 @@ import java.time.OffsetDateTime; import java.util.*; +import static io.micronaut.http.HttpRequest.DELETE; import static io.micronaut.http.HttpRequest.GET; import static org.junit.jupiter.api.Assertions.*; @@ -187,4 +188,35 @@ public void getAllListsSuccess() { } } } + + + @Test + @SneakyThrows + public void deleteListSuccess() { + // A GET request to the brapi/v2/lists endpoint with no query params should return all lists. + Flowable> getCall = client.exchange( + GET(String.format("/programs/%s/brapi/v2/lists", program.getId().toString())) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + + // Ensure 200 OK response for fetching lists. + HttpResponse beforeResponse = getCall.blockingFirst(); + assertEquals(HttpStatus.OK, beforeResponse.getStatus()); + + // Parse result. + JsonObject beforeResult = JsonParser.parseString(beforeResponse.body()).getAsJsonObject().getAsJsonObject("result"); + JsonArray beforeData = beforeResult.getAsJsonArray("data"); + + // A DELETE request to the brapi/v2/lists/ endpoint with no query params should delete the list. + String deleteListDbId = beforeData.get(0).getAsJsonObject().get("listDbId").getAsString(); + Flowable> deleteCall = client.exchange( + DELETE(String.format("/programs/%s/brapi/v2/lists/%s", program.getId().toString(), deleteListDbId)) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + + // Ensure 204 NO_CONTENT response for deleting a list. + HttpResponse deleteResponse = deleteCall.blockingFirst(); + assertEquals(HttpStatus.NO_CONTENT, deleteResponse.getStatus()); + + } } diff --git a/src/test/java/org/breedinginsight/daos/BrAPIDAOUtilUnitTest.java b/src/test/java/org/breedinginsight/daos/BrAPIDAOUtilUnitTest.java index 0cb51387e..427ed6d88 100644 --- a/src/test/java/org/breedinginsight/daos/BrAPIDAOUtilUnitTest.java +++ b/src/test/java/org/breedinginsight/daos/BrAPIDAOUtilUnitTest.java @@ -1,6 +1,7 @@ package org.breedinginsight.daos; import com.google.gson.JsonObject; +import io.micronaut.test.annotation.MockBean; import lombok.SneakyThrows; import org.apache.commons.lang3.tuple.Pair; import org.brapi.client.v2.ApiResponse; @@ -11,6 +12,7 @@ import org.brapi.v2.model.germ.response.BrAPIGermplasmListResponseResult; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.model.Program; +import org.breedinginsight.services.ProgramService; import org.breedinginsight.utilities.BrAPIDAOUtil; import org.breedinginsight.utilities.Utilities; import org.junit.jupiter.api.BeforeEach; @@ -21,6 +23,8 @@ import java.time.temporal.ChronoUnit; import java.util.*; +import static org.mockito.Mockito.mock; + @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class BrAPIDAOUtilUnitTest { @@ -30,6 +34,10 @@ public class BrAPIDAOUtilUnitTest { private Program testProgram; private List paginatedGermplasm; private BrAPIGermplasmSearchRequest germplasmSearch; + @MockBean(ProgramService.class) + ProgramService programService() { + return mock(ProgramService.class); + } public Integer fetchPaginatedGermplasm(int page, int pageSize) { paginatedGermplasm = new ArrayList<>(); @@ -62,7 +70,7 @@ public ApiResponse, Optional Date: Tue, 26 Nov 2024 12:07:10 -0500 Subject: [PATCH 037/132] [BI-2343] - added entry number matching --- .../utilities/response/ResponseUtils.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java b/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java index 2b783b90c..4cee82f99 100644 --- a/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java +++ b/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java @@ -193,12 +193,17 @@ private static List search(List data, SearchRequest searchRequest, Abstrac .collect(Collectors.toList()); } - //To enable checking for the case of Germplasm Search when the filter is accessionNumber and thereby needs to do exact match + //To enable checking for the case of Germplasm Search when the filter is accessionNumber or list entry number and thereby needs to do exact match String accessionNumFilter; if (mapper.exists("accessionNumber")) accessionNumFilter = mapper.getField("accessionNumber").toString(); else { accessionNumFilter = ""; } + String entryNumFilter; + if (mapper.exists("importEntryNumber")) entryNumFilter = mapper.getField("importEntryNumber").toString(); + else { + entryNumFilter = ""; + } if (filterFields.size() > 0){ // Apply filters @@ -223,6 +228,11 @@ else if (!accessionNumFilter.isEmpty() && filterField.getField().toString().equa //enable exact match in case of GID return filterField.getField().apply(record).toString() .toLowerCase().equalsIgnoreCase(filterField.getValue().toLowerCase()); + } + else if (!entryNumFilter.isEmpty() && filterField.getField().toString().equals(entryNumFilter)) { + //enable exact match in case of entry number + return filterField.getField().apply(record).toString() + .toLowerCase().equalsIgnoreCase(filterField.getValue().toLowerCase()); } else { return filterField.getField().apply(record).toString() .toLowerCase().contains(filterField.getValue().toLowerCase()); From dae48ce1e57675ff5fb58bcca630c788dd806db2 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:45:29 -0500 Subject: [PATCH 038/132] [BI-2376] - initial implementation --- .../v1/controller/ExperimentController.java | 9 ++++-- .../brapi/v2/dao/BrAPITrialDAO.java | 2 +- .../brapi/v2/dao/impl/BrAPITrialDAOImpl.java | 16 ++++++++-- .../brapi/v2/services/BrAPITrialService.java | 30 +++++++++++++++++-- 4 files changed, 47 insertions(+), 10 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 de40bdf46..059b0428e 100644 --- a/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java @@ -319,9 +319,12 @@ public HttpResponse deleteExperiment( if(program.isEmpty()) { return HttpResponse.notFound(); } - - experimentService.deleteExperiment(program.get(), experimentId, hard); - + // TODO: If hard and non-zero result, return 409 Conflict. + int observationCount = experimentService.deleteExperiment(program.get(), experimentId, hard); + if (observationCount > 0 && hard) { + // 409 Conflict. https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409 + return HttpResponse.status(HttpStatus.CONFLICT); + } return HttpResponse.ok(); } catch (Exception e) { log.error("Error deleting experiment.\n\tprogramId: " + programId + "\n\texperimentId: " + experimentId + "\n\thard: " + hard); diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java index 046ca5aad..49315cb82 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java @@ -46,5 +46,5 @@ List createBrAPITrials(List brAPITrialList, UUID program List getTrialsByExperimentIds(Collection experimentIds, Program program) throws ApiException; - void deleteExperiment(UUID experimentId, boolean hard); + void deleteBrAPITrial(Program program, BrAPITrial trial, boolean hard) throws ApiException; } diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java index 0a39d29aa..d62d36876 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java @@ -21,7 +21,8 @@ import io.micronaut.http.server.exceptions.InternalServerException; import io.micronaut.scheduling.annotation.Scheduled; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.NotImplementedException; +import okhttp3.HttpUrl; +import okhttp3.Request; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.modules.core.TrialsApi; import org.brapi.v2.model.BrAPIExternalReference; @@ -280,7 +281,16 @@ public List getTrialsByExperimentIds(Collection experimentIds, } @Override - public void deleteExperiment(UUID experimentId, boolean hard) { - throw new NotImplementedException(); + public void deleteBrAPITrial(Program program, BrAPITrial trial, boolean hard) throws ApiException { + var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(program.getId()); + var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/trials/" + trial.getTrialDbId()).newBuilder(); + requestUrl.addQueryParameter("hardDelete", Boolean.toString(hard)); + HttpUrl url = requestUrl.build(); + var brapiRequest = new Request.Builder().url(url) + .method("DELETE", null) + .addHeader("Content-Type", "application/json") + .build(); + + makeCall(brapiRequest); } } 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 b1c7333c3..a63d4632c 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java @@ -667,10 +667,34 @@ public BrAPITrial getExperiment(Program program, UUID experimentId) throws ApiEx return experiments.get(0); } - public boolean deleteExperiment(Program program, UUID experimentId, boolean hard) throws ApiException { - // TODO: make BrAPI request to delete experiment. - // TODO: make BrAPI request to delete list for default observation dataset. + // TODO: create a result type for delete requests? + // The caller could infer from the number of obs and hard whether delete succeeded. + public int deleteExperiment(Program program, UUID experimentId, boolean hard) throws ApiException { + // TODO: check for observations! + BrAPITrial trial = trialDAO.getTrialsByExperimentIds(List.of(experimentId), program).get(0); + List existingObservations = observationDAO.getObservationsByTrialDbId(List.of(trial.getTrialDbId()), program); + // If there are no observations or a soft delete is requested, proceed. + if (existingObservations.isEmpty() || !hard) { + // Make request to delete experiment. + trialDAO.deleteBrAPITrial(program, trial, hard); + // Get all lists for the trial. + List lists = listDAO + .getListsByTypeAndExternalRef(BrAPIListTypes.OBSERVATIONVARIABLES, + program.getId(), + Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.TRIALS), + experimentId); + // TODO: replace with a single call to a batch delete method if that becomes available. + // Iterate over lists, delete each by listDbId. + for (BrAPIListSummary list : lists) { + listDAO.deleteBrAPIList(list.getListDbId(), program.getId(), hard); // TODO: not yet implemented. + } + } else { + // Trying to hard delete a trial with existing observations, return 409 Conflict response. + // TODO: remove if unused. + } + // Successful or not, return the number of observations in this experiment. + return existingObservations.size(); } private Map createExportRow( From a0c5e9196aa41d366027f3cc9f8abc7cbad7c8b7 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:27:35 -0500 Subject: [PATCH 039/132] [BI-2376] - updated method reference --- .../breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java index d62d36876..191e48f5a 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java @@ -282,7 +282,7 @@ public List getTrialsByExperimentIds(Collection experimentIds, @Override public void deleteBrAPITrial(Program program, BrAPITrial trial, boolean hard) throws ApiException { - var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(program.getId()); + var programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(program.getId()); var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/trials/" + trial.getTrialDbId()).newBuilder(); requestUrl.addQueryParameter("hardDelete", Boolean.toString(hard)); HttpUrl url = requestUrl.build(); @@ -291,6 +291,6 @@ public void deleteBrAPITrial(Program program, BrAPITrial trial, boolean hard) th .addHeader("Content-Type", "application/json") .build(); - makeCall(brapiRequest); + brAPIDAOUtil.makeCall(brapiRequest); } } From fa60291357875831127a5be5c62a1ccca1d5a889 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Wed, 27 Nov 2024 16:15:08 +0000 Subject: [PATCH 040/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index e73ad356d..07307c36c 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+893 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/16cdff9a3f15138b35958dd4aee554ed1a7dc26c +version=v1.1.0+895 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/8e7ec0c2ef021f8f9a5e5e6ead6f666e25a51cdb From 7dbaf0961eb928796781abf441ad2931d0849bd2 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:41:20 -0500 Subject: [PATCH 041/132] [BI-2376] - added cache invalidation --- .../brapi/v2/dao/BrAPICachedDAO.java | 16 ++++++++++++++++ .../brapi/v2/dao/BrAPIObservationDAO.java | 16 +++++++--------- .../brapi/v2/dao/BrAPIObservationUnitDAO.java | 15 ++++++--------- .../brapi/v2/dao/BrAPIStudyDAO.java | 15 ++++++--------- .../brapi/v2/dao/BrAPITrialDAO.java | 2 ++ .../brapi/v2/dao/impl/BrAPITrialDAOImpl.java | 7 +++++++ .../brapi/v2/services/BrAPITrialService.java | 6 ++++++ 7 files changed, 50 insertions(+), 27 deletions(-) create mode 100644 src/main/java/org/breedinginsight/brapi/v2/dao/BrAPICachedDAO.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPICachedDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPICachedDAO.java new file mode 100644 index 000000000..fb35bd448 --- /dev/null +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPICachedDAO.java @@ -0,0 +1,16 @@ +package org.breedinginsight.brapi.v2.dao; + +import org.breedinginsight.daos.cache.ProgramCache; + +import java.util.UUID; + +public abstract class BrAPICachedDAO { + + protected ProgramCache programCache; + + public void repopulateCache(UUID programId) { + // TODO: test calling populate alone (without invalidate first). + this.programCache.invalidate(programId); + this.programCache.populate(programId); + } +} diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java index e32dac6d9..fdfbd18a1 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java @@ -36,7 +36,6 @@ import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.daos.ProgramDAO; -import org.breedinginsight.daos.cache.ProgramCache; import org.breedinginsight.daos.cache.ProgramCacheProvider; import org.breedinginsight.model.Program; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; @@ -54,7 +53,7 @@ @Singleton @Slf4j -public class BrAPIObservationDAO { +public class BrAPIObservationDAO extends BrAPICachedDAO { private ProgramDAO programDAO; private ImportDAO importDAO; @@ -63,7 +62,6 @@ public class BrAPIObservationDAO { private final BrAPIEndpointProvider brAPIEndpointProvider; private final String referenceSource; private boolean runScheduledTasks; - private final ProgramCache programObservationCache; @Inject public BrAPIObservationDAO(ProgramDAO programDAO, @@ -81,7 +79,7 @@ public BrAPIObservationDAO(ProgramDAO programDAO, this.brAPIEndpointProvider = brAPIEndpointProvider; this.referenceSource = referenceSource; this.runScheduledTasks = runScheduledTasks; - this.programObservationCache = programCacheProvider.getProgramCache(this::fetchProgramObservations, BrAPIObservation.class); + this.programCache = programCacheProvider.getProgramCache(this::fetchProgramObservations, BrAPIObservation.class); } @Scheduled(initialDelay = "3s") @@ -93,7 +91,7 @@ public void setup() { log.debug("populating observation cache"); List programs = programDAO.getActive(); if (programs != null) { - programObservationCache.populate(programs.stream().map(Program::getId).collect(Collectors.toList())); + programCache.populate(programs.stream().map(Program::getId).collect(Collectors.toList())); } } @@ -163,7 +161,7 @@ private void processObservations(String programKey, List obser * Get all observations for a program from the cache. */ private Map getProgramObservations(UUID programId) throws ApiException { - return programObservationCache.get(programId); + return programCache.get(programId); } // Note: not using cache, because unique studyName (with "[ProgramKey-ExtraInfo]") is not stored directly on Observation. @@ -259,7 +257,7 @@ public List createBrAPIObservations(List brA List postResponse = brAPIDAOUtil.post(brAPIObservationList, upload, api::observationsPost, importDAO::update); return processObservationsForCache(postResponse, program.getKey()); }; - return programObservationCache.post(programId, postFunction); + return programCache.post(programId, postFunction); } return new ArrayList<>(); } catch (Exception e) { @@ -287,7 +285,7 @@ public BrAPIObservation updateBrAPIObservation(String dbId, BrAPIObservation obs } return processObservationsForCache(List.of(updatedObservation), program.getKey()); }; - return programObservationCache.post(programId, postFunction).get(0); + return programCache.post(programId, postFunction).get(0); } catch (ApiException e) { log.error(Utilities.generateApiExceptionLogMessage(e)); throw new InternalServerException("Unknown error has occurred: " + e.getMessage(), e); @@ -343,7 +341,7 @@ public List updateBrAPIObservation(Map processedObservations = processObservationsForCache(updatedObservations, program.getKey()); - return programObservationCache.postThese(programId,processedObservations); + return programCache.postThese(programId,processedObservations); } catch (ApiException e) { log.error("Error updating observation: " + Utilities.generateApiExceptionLogMessage(e), e); throw e; 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 aeb919a2f..9ef12b371 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java @@ -41,7 +41,6 @@ import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.daos.ProgramDAO; -import org.breedinginsight.daos.cache.ProgramCache; import org.breedinginsight.daos.cache.ProgramCacheProvider; import org.breedinginsight.model.Program; import org.breedinginsight.services.ProgramService; @@ -61,7 +60,7 @@ @Slf4j @Singleton -public class BrAPIObservationUnitDAO { +public class BrAPIObservationUnitDAO extends BrAPICachedDAO { private final ProgramDAO programDAO; private final ImportDAO importDAO; private final BrAPIDAOUtil brAPIDAOUtil; @@ -75,8 +74,6 @@ public class BrAPIObservationUnitDAO { private final Gson gson = new JSON().getGson(); private final Type treatmentlistType = new TypeToken>(){}.getType(); - private final ProgramCache programObservationUnitCache; - @Inject public BrAPIObservationUnitDAO(ProgramDAO programDAO, ImportDAO importDAO, @@ -95,7 +92,7 @@ public BrAPIObservationUnitDAO(ProgramDAO programDAO, this.runScheduledTasks = runScheduledTasks; this.programService = programService; this.germplasmService = germplasmService; - this.programObservationUnitCache = programCacheProvider.getProgramCache(this::fetchProgramObservationUnits, BrAPIObservationUnit.class); + this.programCache = programCacheProvider.getProgramCache(this::fetchProgramObservationUnits, BrAPIObservationUnit.class); } @Scheduled(initialDelay = "3s") @@ -107,7 +104,7 @@ public void setup() { log.debug("populating observation unit cache"); List programs = programDAO.getActive(); if(programs != null) { - programObservationUnitCache.populate(programs.stream().map(Program::getId).collect(Collectors.toList())); + programCache.populate(programs.stream().map(Program::getId).collect(Collectors.toList())); } } @@ -157,7 +154,7 @@ private Map processObservationUnitsForCache(List getProgramObservationUnits(UUID programId) throws ApiException { - return programObservationUnitCache.get(programId); + return programCache.get(programId); } public List getObservationUnitByName(List observationUnitNames, Program program) throws ApiException { @@ -183,7 +180,7 @@ public List createBrAPIObservationUnits(List ous = brAPIDAOUtil.post(brAPIObservationUnitList, upload, api::observationunitsPost, importDAO::update); return processObservationUnitsForCache(ous, program, false); }; - return programObservationUnitCache.post(programId, postFunction); + return programCache.post(programId, postFunction); } return new ArrayList<>(); } catch (Exception e) { @@ -205,7 +202,7 @@ public List createBrAPIObservationUnits(List ous = brAPIDAOUtil.post(brAPIObservationUnitList, api::observationunitsPost); return processObservationUnitsForCache(ous, program, false); }; - return programObservationUnitCache.post(programId, postFunction); + return programCache.post(programId, postFunction); } return new ArrayList<>(); } catch (Exception e) { diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIStudyDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIStudyDAO.java index fdd404ae3..160e8df62 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIStudyDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIStudyDAO.java @@ -30,7 +30,6 @@ import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.daos.ProgramDAO; -import org.breedinginsight.daos.cache.ProgramCache; import org.breedinginsight.daos.cache.ProgramCacheProvider; import org.breedinginsight.model.Program; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; @@ -46,7 +45,7 @@ @Slf4j @Singleton -public class BrAPIStudyDAO { +public class BrAPIStudyDAO extends BrAPICachedDAO { @Property(name = "brapi.server.reference-source") private String referenceSource; @Property(name = "micronaut.bi.api.run-scheduled-tasks") @@ -56,8 +55,6 @@ public class BrAPIStudyDAO { private ImportDAO importDAO; private final BrAPIDAOUtil brAPIDAOUtil; private final BrAPIEndpointProvider brAPIEndpointProvider; - private final ProgramCache programStudyCache; - @Inject public BrAPIStudyDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, ProgramCacheProvider programCacheProvider) { @@ -65,7 +62,7 @@ public BrAPIStudyDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil br this.importDAO = importDAO; this.brAPIDAOUtil = brAPIDAOUtil; this.brAPIEndpointProvider = brAPIEndpointProvider; - this.programStudyCache = programCacheProvider.getProgramCache(this::fetchProgramStudy, BrAPIStudy.class); + this.programCache = programCacheProvider.getProgramCache(this::fetchProgramStudy, BrAPIStudy.class); } @Scheduled(initialDelay = "2s") @@ -77,7 +74,7 @@ public void setup() { log.debug("populating study cache"); List programs = programDAO.getActive(); if(programs != null) { - programStudyCache.populate(programs.stream().map(Program::getId).collect(Collectors.toList())); + programCache.populate(programs.stream().map(Program::getId).collect(Collectors.toList())); } } @@ -115,7 +112,7 @@ private Map fetchProgramStudy(UUID programId) throws ApiExce * @throws ApiException */ public List getStudies(UUID programId) throws ApiException { - return new ArrayList<>(programStudyCache.get(programId).values()); + return new ArrayList<>(programCache.get(programId).values()); } public Optional getStudyByName(String studyName, Program program) throws ApiException { @@ -153,7 +150,7 @@ public List getStudiesByExperimentID(@NotNull UUID experimentId, Pro } public List getStudiesByEnvironmentIds(@NotNull Collection environmentIds, Program program) throws ApiException { - return programStudyCache.get(program.getId()) + return programCache.get(program.getId()) .entrySet() .stream() .filter(entry -> environmentIds.contains(UUID.fromString(entry.getKey()))) @@ -193,7 +190,7 @@ public List createBrAPIStudies(List brAPIStudyList, UUID .post(brAPIStudyList, upload, api::studiesPost, importDAO::update); return environmentById(postedStudies); }; - createdStudies.addAll(programStudyCache.post(programId, postCallback)); + createdStudies.addAll(programCache.post(programId, postCallback)); } return createdStudies; diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java index 49315cb82..85298c158 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPITrialDAO.java @@ -47,4 +47,6 @@ List createBrAPITrials(List brAPITrialList, UUID program List getTrialsByExperimentIds(Collection experimentIds, Program program) throws ApiException; void deleteBrAPITrial(Program program, BrAPITrial trial, boolean hard) throws ApiException; + + void repopulateCache(UUID programId); } diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java index 191e48f5a..d703f97e4 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java @@ -282,6 +282,7 @@ public List getTrialsByExperimentIds(Collection experimentIds, @Override public void deleteBrAPITrial(Program program, BrAPITrial trial, boolean hard) throws ApiException { + // TODO: Switch to using the TrialsApi from the BrAPI client library once the delete endpoints are merged into it. var programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(program.getId()); var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/trials/" + trial.getTrialDbId()).newBuilder(); requestUrl.addQueryParameter("hardDelete", Boolean.toString(hard)); @@ -293,4 +294,10 @@ public void deleteBrAPITrial(Program program, BrAPITrial trial, boolean hard) th brAPIDAOUtil.makeCall(brapiRequest); } + + @Override + public void repopulateCache(UUID programId) { + this.programExperimentCache.invalidate(programId); + this.programExperimentCache.populate(programId); + } } 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 a63d4632c..395471c41 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java @@ -688,6 +688,12 @@ public int deleteExperiment(Program program, UUID experimentId, boolean hard) th for (BrAPIListSummary list : lists) { listDAO.deleteBrAPIList(list.getListDbId(), program.getId(), hard); // TODO: not yet implemented. } + // TODO: if performance is poor, implement more precise invalidation, possibly using hierarchical cache keys. + // Invalidate and repopulate cache for Trial, Study, Observation, ObservationUnit. + trialDAO.repopulateCache(program.getId()); + studyDAO.repopulateCache(program.getId()); + observationDAO.repopulateCache(program.getId()); + observationUnitDAO.repopulateCache(program.getId()); } else { // Trying to hard delete a trial with existing observations, return 409 Conflict response. // TODO: remove if unused. From e5c6fea0d17dcdaa4843f07eb0ab5eec04cae610 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:31:27 -0500 Subject: [PATCH 042/132] [BI-2376] - used 204 No Content --- .../api/v1/controller/ExperimentController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 059b0428e..01a39ea92 100644 --- a/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java @@ -325,7 +325,8 @@ public HttpResponse deleteExperiment( // 409 Conflict. https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409 return HttpResponse.status(HttpStatus.CONFLICT); } - return HttpResponse.ok(); + // 204 No Content indicates successful delete. https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204 + return HttpResponse.noContent(); } catch (Exception e) { log.error("Error deleting experiment.\n\tprogramId: " + programId + "\n\texperimentId: " + experimentId + "\n\thard: " + hard); throw e; From 270b85813ae2b2bf35e90bf167469e2f316522e2 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 17 Dec 2024 15:19:35 -0500 Subject: [PATCH 043/132] [BI-2376] - improved exception handling --- .../api/v1/controller/ExperimentController.java | 10 ++++++---- .../brapi/v2/services/BrAPITrialService.java | 13 ++++++------- 2 files changed, 12 insertions(+), 11 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 01a39ea92..8619a140a 100644 --- a/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/ExperimentController.java @@ -319,7 +319,6 @@ public HttpResponse deleteExperiment( if(program.isEmpty()) { return HttpResponse.notFound(); } - // TODO: If hard and non-zero result, return 409 Conflict. int observationCount = experimentService.deleteExperiment(program.get(), experimentId, hard); if (observationCount > 0 && hard) { // 409 Conflict. https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409 @@ -327,11 +326,14 @@ public HttpResponse deleteExperiment( } // 204 No Content indicates successful delete. https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204 return HttpResponse.noContent(); - } catch (Exception e) { + } catch (ApiException e) { log.error("Error deleting experiment.\n\tprogramId: " + programId + "\n\texperimentId: " + experimentId + "\n\thard: " + hard); - throw e; + if (e.getCode() == 404) { + return HttpResponse.notFound(); + } else { + return HttpResponse.serverError(); + } } - } 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 395471c41..9aec86320 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java @@ -667,11 +667,13 @@ public BrAPITrial getExperiment(Program program, UUID experimentId) throws ApiEx return experiments.get(0); } - // TODO: create a result type for delete requests? - // The caller could infer from the number of obs and hard whether delete succeeded. public int deleteExperiment(Program program, UUID experimentId, boolean hard) throws ApiException { - // TODO: check for observations! - BrAPITrial trial = trialDAO.getTrialsByExperimentIds(List.of(experimentId), program).get(0); + List trials = trialDAO.getTrialsByExperimentIds(List.of(experimentId), program); + if (trials.isEmpty()) { + throw new ApiException(404, "Experiment with UUID " + experimentId + " not found"); + } + BrAPITrial trial = trials.get(0); + List existingObservations = observationDAO.getObservationsByTrialDbId(List.of(trial.getTrialDbId()), program); // If there are no observations or a soft delete is requested, proceed. if (existingObservations.isEmpty() || !hard) { @@ -694,9 +696,6 @@ public int deleteExperiment(Program program, UUID experimentId, boolean hard) th studyDAO.repopulateCache(program.getId()); observationDAO.repopulateCache(program.getId()); observationUnitDAO.repopulateCache(program.getId()); - } else { - // Trying to hard delete a trial with existing observations, return 409 Conflict response. - // TODO: remove if unused. } // Successful or not, return the number of observations in this experiment. From 3a2a1d3aad2b8a23889c177c323299d10879c14b Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 17 Dec 2024 16:03:54 -0500 Subject: [PATCH 044/132] [BI-2376] - added integration tests --- .../ExperimentControllerIntegrationTest.java | 164 +++++++++++++++++- 1 file changed, 159 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/breedinginsight/api/v1/controller/ExperimentControllerIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/ExperimentControllerIntegrationTest.java index e2961bb9f..a3c47e573 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/ExperimentControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/ExperimentControllerIntegrationTest.java @@ -73,6 +73,8 @@ public class ExperimentControllerIntegrationTest extends BrAPITest { private List traits; private User testUser; private User otherTestUser; + private String mappingId; + private final String experimentTitle = "Test Exp"; @Property(name = "brapi.server.reference-source") private String BRAPI_REFERENCE_SOURCE; @@ -145,7 +147,7 @@ void setup() throws Exception { .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class ); HttpResponse response = call.blockingFirst(); - String mappingId = JsonParser.parseString(Objects.requireNonNull(response.body())).getAsJsonObject() + mappingId = JsonParser.parseString(Objects.requireNonNull(response.body())).getAsJsonObject() .getAsJsonObject("result") .getAsJsonArray("data") .get(0).getAsJsonObject().get("id").getAsString(); @@ -171,8 +173,8 @@ void setup() throws Exception { germplasmDAO.createBrAPIGermplasm(germplasm, program.getId(), null); // Make test experiment import - Map row1 = makeExpImportRow("Env1"); - Map row2 = makeExpImportRow("Env2"); + Map row1 = makeExpImportRow(experimentTitle, "Env1"); + Map row2 = makeExpImportRow(experimentTitle, "Env2"); // Add test observation data for (Trait trait : traits) { @@ -209,6 +211,37 @@ void setup() throws Exception { envIds.add(getEnvId(importResult, 1)); } + // Create an experiment with no observations. + private String uploadExperimentWithoutObs() throws Exception { + ImportTestUtils importTestUtils = new ImportTestUtils(); + List> expRows = new ArrayList<>(); + + // Make test experiment import. + Map row1 = makeExpImportRow("Without Obs", "NewEnv1"); + Map row2 = makeExpImportRow("Without Obs", "NewEnv2"); + + expRows.add(row1); + expRows.add(row2); + + // Import test experiment, environments. + JsonObject importResult = importTestUtils.uploadAndFetchWorkflow( + writeDataToFile(expRows, null), + null, + true, + client, + program, + mappingId, + newExperimentWorkflowId); + String expId = importResult + .get("preview").getAsJsonObject() + .get("rows").getAsJsonArray() + .get(0).getAsJsonObject() + .get("trial").getAsJsonObject() + .get("id").getAsString(); + + return expId; + } + /** * Tests all 18 permutations of * 3 formats: [CSV, XLS, XLSX], @@ -690,6 +723,92 @@ public void getExperimentalCollaboratorsDeactivated(boolean active) { dsl.execute(securityFp.get("DeleteProgramUser"), otherTestUser.getId().toString()); } + /** + * A delete request with an invalid trialDbId should result in a 404 Not Found response. + */ + @Test + @SneakyThrows + public void deleteExperimentInvalid() { + // A DELETE request endpoint with an invalid experimentId. + Flowable> invalidDeleteCall = client.exchange( + DELETE(String.format("/programs/%s/experiments/%s", program.getId().toString(), "00000000-1111-2222-3333-444444444444")) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + + // Ensure 404 NOT_FOUND response for requesting to delete a non-existant trial. + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse response = invalidDeleteCall.blockingFirst(); + }); + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } + + /* Test the following 4 Cases: + * 1. hard delete with obs - failure + * 2. soft delete with obs - success + * 3. hard delete without obs - success + * 4. soft delete without obs - success + */ + @ParameterizedTest + @CsvSource(value = {"true,true", "false,true", "true,false", "false,false"}) + @SneakyThrows + public void deleteExperimentSuccess(boolean hardDelete, boolean withObservations) { + // Set up a test trial and get the trialDbId. + String trialDbId; + if (withObservations) { + JsonArray beforeData = getProgramTrials(program.getId().toString()); + + // The trial created by setup has observations. + trialDbId = beforeData.get(0).getAsJsonObject().get("trialDbId").getAsString(); + } else { + // Create a trial without observations. + trialDbId = uploadExperimentWithoutObs(); + } + + // A DELETE request should delete an experiment with observations unless there are observations and hardDelete = true. + Flowable> deleteCall = client.exchange( + DELETE(String.format("/programs/%s/experiments/%s?hard=%s", program.getId().toString(), trialDbId, hardDelete)) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + + // Ensure 204 NO_CONTENT response after successfully deleting a trial, + // unless there are observations and hard delete is requested, then ensure 409 Conflict response. + if (hardDelete && withObservations) { + // Ensure 404 NOT_FOUND response for requesting to delete a non-existant trial. + HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { + HttpResponse response = deleteCall.blockingFirst(); + }); + assertEquals(HttpStatus.CONFLICT, e.getStatus()); + + // Check that the trial was not deleted. + JsonArray trials = getProgramTrials(program.getId().toString()); + assertEquals(1, trials.size()); + + // Check that the studies were not deleted. + JsonArray studies = getProgramStudies(program.getId().toString()); + assertEquals(2, studies.size()); + + // Check that lists were not deleted. + JsonArray lists = getProgramObsVarLists(program.getId().toString()); + assertEquals(1, lists.size()); + } else { + HttpResponse deleteResponse = deleteCall.blockingFirst(); + + assertEquals(HttpStatus.NO_CONTENT, deleteResponse.getStatus()); + + // Check that the trial was deleted. + JsonArray trials = getProgramTrials(program.getId().toString()); + assertEquals(0, trials.size()); + + // Check that the studies were deleted. + JsonArray studies = getProgramStudies(program.getId().toString()); + assertEquals(0, studies.size()); + + // Check that the BrAPI lists were deleted. + JsonArray lists = getProgramObsVarLists(program.getId().toString()); + assertEquals(0, lists.size()); + } + + } private List> buildSubEntityRows(List> topLevelRows, String entityName, int repeatedMeasures) { List> plantRows = new ArrayList<>(); @@ -726,11 +845,11 @@ private File writeDataToFile(List> data, List traits) return file; } - private Map makeExpImportRow(String environment) { + private Map makeExpImportRow(String title, 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_TITLE, title); row.put(ExperimentObservation.Columns.EXP_UNIT, "Plot"); //row.put(ExperimentObservation.Columns.SUB_OBS_UNIT, ""); row.put(ExperimentObservation.Columns.EXP_TYPE, "Phenotyping"); @@ -931,4 +1050,39 @@ private String getEnvId(JsonObject result, int index) { .get("referenceId").getAsString(); } + private JsonArray getProgramTrials(String programId) { + Flowable> getCall = client.exchange( + GET(String.format("/programs/%s/brapi/v2/trials", programId)) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + HttpResponse response = getCall.blockingFirst(); + + // Parse result. + JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject().getAsJsonObject("result"); + return result.getAsJsonArray("data"); + } + + private JsonArray getProgramStudies(String programId) { + Flowable> getCall = client.exchange( + GET(String.format("/programs/%s/brapi/v2/studies", programId)) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + HttpResponse response = getCall.blockingFirst(); + + // Parse result. + JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject().getAsJsonObject("result"); + return result.getAsJsonArray("data"); + } + + private JsonArray getProgramObsVarLists(String programId) { + Flowable> getCall = client.exchange( + GET(String.format("/programs/%s/brapi/v2/lists?listType=observationVariables", programId)) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + HttpResponse response = getCall.blockingFirst(); + + // Parse result. + JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject().getAsJsonObject("result"); + return result.getAsJsonArray("data"); + } } From 98ff599a29ef26e397e532602c0940f2023f8629 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Fri, 15 Nov 2024 12:33:02 -0500 Subject: [PATCH 045/132] add delete endpoint to list controller --- .../brapi/v2/BrAPIGermplasmController.java | 37 ---- .../brapi/v2/BrAPIListController.java | 183 +++++++++++++++--- .../org/breedinginsight/daos/ListDAO.java | 102 ++++++++++ .../model/delta/DeltaEntity.java | 3 +- .../model/delta/DeltaEntityFactory.java | 55 +++++- .../delta/DeltaGermplasmListDetails.java | 62 ++++++ .../delta/DeltaGermplasmListSummary.java | 16 ++ .../model/delta/DeltaListDetails.java | 78 ++++++++ .../model/delta/DeltaListSummary.java | 20 ++ .../breedinginsight/services/ListService.java | 33 ++++ .../breedinginsight/utilities/Utilities.java | 2 + 11 files changed, 521 insertions(+), 70 deletions(-) create mode 100644 src/main/java/org/breedinginsight/daos/ListDAO.java create mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java create mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java create mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java create mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java create mode 100644 src/main/java/org/breedinginsight/services/ListService.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java index 948e68c20..4d67e2a00 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java @@ -221,43 +221,6 @@ public HttpResponse>>> getGermplasm( } } - @Get("/programs/{programId}/germplasm/lists/{listDbId}/records{?queryParams*}") - @Produces(MediaType.APPLICATION_JSON) - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - public HttpResponse>>> getGermplasmListRecords( - @PathVariable("programId") UUID programId, - @PathVariable("listDbId") String listDbId, - @QueryValue @QueryValid(using = GermplasmQueryMapper.class) @Valid GermplasmQuery queryParams) { - try { - List germplasm = germplasmService.getGermplasmByList(programId, listDbId); - SearchRequest searchRequest = queryParams.constructSearchRequest(); - return ResponseUtils.getBrapiQueryResponse(germplasm, germplasmQueryMapper, queryParams, searchRequest); - } catch (Exception e) { - log.info(e.getMessage(), e); - return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving germplasm list records"); - } - } - - @Get("/programs/{programId}/germplasm/lists/{listDbId}/export{?fileExtension}") - @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - public HttpResponse germplasmListExport( - @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, @QueryValue(defaultValue = "XLSX") String fileExtension) { - String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; - try { - FileType extension = Enum.valueOf(FileType.class, fileExtension); - DownloadFile germplasmListFile = germplasmService.exportGermplasmList(programId, listDbId, extension); - HttpResponse germplasmListExport = HttpResponse.ok(germplasmListFile.getStreamedFile()).header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="+germplasmListFile.getFileName()+extension.getExtension()); - return germplasmListExport; - } - catch (Exception e) { - log.info(e.getMessage(), e); - e.printStackTrace(); - HttpResponse response = HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, downloadErrorMessage).contentType(MediaType.TEXT_PLAIN).body(downloadErrorMessage); - return response; - } - } - @Get("/programs/{programId}/germplasm/export{?fileExtension}") @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 3dd6fd1f4..0eebc602d 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -1,26 +1,10 @@ -/* - * See the NOTICE file distributed with this work for additional information - * regarding copyright ownership. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.breedinginsight.brapi.v2; -import io.micronaut.http.HttpResponse; -import io.micronaut.http.HttpStatus; -import io.micronaut.http.MediaType; +import io.micronaut.core.beans.BeanIntrospection; +import io.micronaut.core.beans.BeanProperty; +import io.micronaut.http.*; import io.micronaut.http.annotation.*; +import io.micronaut.http.server.types.files.StreamedFile; import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; @@ -29,43 +13,55 @@ import org.brapi.v2.model.core.BrAPIListTypes; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; -import org.breedinginsight.api.model.v1.request.query.SearchRequest; import org.breedinginsight.api.model.v1.response.DataResponse; import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.api.model.v1.validators.QueryValid; import org.breedinginsight.brapi.v1.controller.BrapiVersion; +import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; import org.breedinginsight.brapi.v2.model.request.query.ListQuery; import org.breedinginsight.brapi.v2.services.BrAPIListService; +import org.breedinginsight.brapps.importer.model.exports.FileType; +import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.Program; +import org.breedinginsight.model.delta.DeltaListDetails; +import org.breedinginsight.services.ListService; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.response.ResponseUtils; +import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; import org.breedinginsight.utilities.response.mappers.ListQueryMapper; +import org.breedinginsight.api.model.v1.request.query.SearchRequest; import javax.inject.Inject; +import javax.validation.ConstraintViolation; import javax.validation.Valid; +import javax.validation.Validator; import java.util.List; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; @Slf4j -@Controller +@Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @Secured(SecurityRule.IS_AUTHENTICATED) public class BrAPIListController { - private final ProgramService programService; - private final BrAPIListService listService; + private final BrAPIListService brapiListService; + private final ListService listService; private final ListQueryMapper listQueryMapper; + private final Validator validator; @Inject - public BrAPIListController(ProgramService programService, BrAPIListService listService, - ListQueryMapper listQueryMapper) { + public BrAPIListController(ProgramService programService, BrAPIListService brapiListService, ListService listService, + ListQueryMapper listQueryMapper, Validator validator) { this.programService = programService; + this.brapiListService = brapiListService; this.listService = listService; this.listQueryMapper = listQueryMapper; + this.validator = validator; } - //@Get(BrapiVersion.BRAPI_V2 + "/lists") - @Get("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/lists{?queryParams*}") + @Get("/deltalists{?queryParams*}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse>> getLists( @@ -77,7 +73,6 @@ public HttpResponse>> getLists( .getById(programId) .orElseThrow(() -> new DoesNotExistException("Program does not exist")); - // get germplasm lists by default BrAPIListTypes type = BrAPIListTypes.fromValue(queryParams.getListType()); String source = null; String id = null; @@ -93,7 +88,7 @@ public HttpResponse>> getLists( if (dateFormatParam != null) { listQueryMapper.setDateDisplayFormat(dateFormatParam); } - List brapiLists = listService.getListSummariesByTypeAndXref(type, source, id, program); + List brapiLists = brapiListService.getListSummariesByTypeAndXref(type, source, id, program); SearchRequest searchRequest = queryParams.constructSearchRequest(); return ResponseUtils.getBrapiQueryResponse(brapiLists, listQueryMapper, queryParams, searchRequest); @@ -105,4 +100,132 @@ public HttpResponse>> getLists( throw new RuntimeException(e); } } + + @Delete("/deltalists/{listDbId}") + @Produces(MediaType.APPLICATION_JSON) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) + public HttpResponse>> deleteListById( + @PathVariable("programId") UUID programId, + @PathVariable("listDbId") String listDbId, + HttpRequest request + ) throws DoesNotExistException, ApiException { + boolean hardDelete = false; + if (request.getParameters().contains("hardDelete")) { + String paramValue = request.getParameters().get("hardDelete"); + hardDelete = "true".equals(paramValue); + } + try { + listService.deleteBrAPIList(listDbId, programId, hardDelete); + return HttpResponse.status(HttpStatus.NO_CONTENT); + } catch (Exception e) { + log.info(e.getMessage(), e); + return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving germplasm list records"); + } + } + + @Get("/deltalists/{listDbId}") + @Produces(MediaType.APPLICATION_JSON) + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) + @SuppressWarnings("unchecked") + public HttpResponse>>> getListById( + @PathVariable("programId") UUID programId, + @PathVariable("listDbId") String listDbId, + HttpRequest request) { + try { + // Get the list from the BrAPI service + DeltaListDetails details = listService.getDeltaListDetails(listDbId, programId); + + // Get a new instance of BrAPI query matching the type of list contents + T queryParams = (T) details.getQuery(); + + // Bind query parameters to the object + bindQueryParams(queryParams, request); + + // Perform standard bean validation + Set> violations = validator.validate(queryParams); + if (!violations.isEmpty()) { + List errorMessages = violations.stream() + .map(ConstraintViolation::getMessage) + .collect(Collectors.toList()); + log.info(String.join(", ", errorMessages)); + return HttpResponse.status(HttpStatus.BAD_REQUEST, "Error with list contents search parameters"); + } + + // Fetch the list contents from the BrAPI service + List listContentsBrAPIObjects = (List) details.getDataObjects(); + + // Construct a search request for sorting the list contents + SearchRequest searchRequest = details.constructSearchRequest(queryParams); + + // Get the map used to connect query sorting keys to contents object values + AbstractQueryMapper contentsQueryMapper = details.getQueryMapper(); + + return ResponseUtils.getBrapiQueryResponse(listContentsBrAPIObjects, contentsQueryMapper, queryParams, searchRequest); + } catch (Exception e) { + log.info(e.getMessage(), e); + return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving list records"); + } + } + + @Get("/deltalists/{listDbId}/export{?fileExtension}") + @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) + public HttpResponse germplasmListExport( + @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, @QueryValue(defaultValue = "XLSX") String fileExtension) { + String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; + try { + // Get the list from the BrAPI service + DeltaListDetails details = listService.getDeltaListDetails(listDbId, programId); + + FileType extension = Enum.valueOf(FileType.class, fileExtension); + DownloadFile listContentsFile = details.exportListObjects(extension); + return HttpResponse.ok(listContentsFile.getStreamedFile()).header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="+listContentsFile.getFileName()+extension.getExtension()); + } + catch (Exception e) { + log.info(e.getMessage(), e); + e.printStackTrace(); + HttpResponse response = HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, downloadErrorMessage).contentType(MediaType.TEXT_PLAIN).body(downloadErrorMessage); + return response; + } + } + + private void bindQueryParams(BrapiQuery queryParams, HttpRequest request) { + BeanIntrospection introspection = BeanIntrospection.getIntrospection(BrapiQuery.class); + for (BeanProperty property : introspection.getBeanProperties()) { + String paramName = property.getName(); + if (request.getParameters().contains(paramName)) { + String paramValue = request.getParameters().get(paramName); + Object convertedValue; + Class propertyType = property.getType(); + + if (propertyType.isEnum()) { + convertedValue = convertToEnum(paramValue, (Class>) propertyType); + } else { + convertedValue = convertValue(paramValue, propertyType); + } + + property.set(queryParams, convertedValue); + } + } + } + + private > T convertToEnum(String value, Class> enumClass) { + if (value == null) { + return null; + } + return Enum.valueOf((Class) enumClass, value.toUpperCase()); + } + + + // Convert, if necessary, the values of query parameters to match the type defined for the fields in the BrapiQuery class + private Object convertValue(String value, Class targetType) { + // Implement type conversion logic here + // Other list content types might need more complex logic + if (targetType == String.class) return value; + if (targetType == Integer.class) return Integer.parseInt(value); + if (targetType == Long.class) return Long.parseLong(value); + if (targetType == Boolean.class) return Boolean.parseBoolean(value); + // Add more type conversions as needed + return value; + } } diff --git a/src/main/java/org/breedinginsight/daos/ListDAO.java b/src/main/java/org/breedinginsight/daos/ListDAO.java new file mode 100644 index 000000000..f44a8757f --- /dev/null +++ b/src/main/java/org/breedinginsight/daos/ListDAO.java @@ -0,0 +1,102 @@ +package org.breedinginsight.daos; + +import io.micronaut.http.HttpStatus; +import io.micronaut.http.exceptions.HttpStatusException; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.brapi.client.v2.ApiResponse; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.client.v2.modules.core.ListsApi; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; +import org.breedinginsight.model.ProgramBrAPIEndpoints; +import org.breedinginsight.model.delta.DeltaEntityFactory; +import org.breedinginsight.model.delta.DeltaListDetails; +import org.breedinginsight.services.ProgramService; +import org.breedinginsight.services.brapi.BrAPIEndpointProvider; +import org.breedinginsight.services.exceptions.DoesNotExistException; +import org.breedinginsight.utilities.BrAPIDAOUtil; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.IOException; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Singleton +public class ListDAO { + private final ProgramDAO programDAO; + private final BrAPIDAOUtil brAPIDAOUtil; + private final BrAPIEndpointProvider brAPIEndpointProvider; + private final DeltaEntityFactory deltaEntityFactory; + private final ProgramService programService; + + @Inject + public ListDAO(ProgramDAO programDAO, + BrAPIDAOUtil brAPIDAOUtil, + BrAPIEndpointProvider brAPIEndpointProvider, + DeltaEntityFactory deltaEntityFactory, ProgramService programService) { + this.programDAO = programDAO; + this.brAPIDAOUtil = brAPIDAOUtil; + this.brAPIEndpointProvider = brAPIEndpointProvider; + this.deltaEntityFactory = deltaEntityFactory; + this.programService = programService; + } + + public DeltaListDetails getDeltaListDetailsByDbId(String listDbId, UUID programId) throws ApiException { + ListsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ListsApi.class); + ApiResponse response = api.listsListDbIdGet(listDbId); + if (Objects.isNull(response.getBody()) || Objects.isNull(response.getBody().getResult())) + { + throw new ApiException(); + } + + BrAPIListDetails details = response.getBody().getResult(); + return deltaEntityFactory.makeDeltaListDetailsBean(details); + } + + public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); + var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); + requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); + HttpUrl url = requestUrl.build(); + var brapiRequest = new Request.Builder().url(url) + .method("DELETE", null) + .addHeader("Content-Type", "application/json") + .build(); + + makeCall(brapiRequest); + } + + private void makeCall(Request brapiRequest) throws ApiException { + OkHttpClient client = new OkHttpClient.Builder() + .readTimeout(5, TimeUnit.MINUTES) + .build(); + try { + client.newCall(brapiRequest).execute(); + } catch (IOException e) { + log.error("Error calling BrAPI Service", e); + throw new ApiException("Error calling BrAPI Service"); + } + } + + private String getProgramBrAPIBaseUrl(UUID programId) { + ProgramBrAPIEndpoints programBrAPIEndpoints; + try { + programBrAPIEndpoints = programService.getBrapiEndpoints(programId); + } catch (DoesNotExistException e) { + throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); + } + + if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { + log.error("Program: " + programId + " is missing BrAPI URL config"); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); + } + var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); + programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; + return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; + } +} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaEntity.java b/src/main/java/org/breedinginsight/model/delta/DeltaEntity.java index 11646f9f8..4f5b8ead9 100644 --- a/src/main/java/org/breedinginsight/model/delta/DeltaEntity.java +++ b/src/main/java/org/breedinginsight/model/delta/DeltaEntity.java @@ -7,7 +7,7 @@ public abstract class DeltaEntity { - protected final Gson gson; + protected static final Gson gson = new GsonBuilder().registerTypeAdapterFactory(new GeometryAdapterFactory()).create(); @NonNull protected final T entity; @@ -15,7 +15,6 @@ public abstract class DeltaEntity { // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. protected DeltaEntity(@NonNull T entity) { this.entity = entity; - this.gson = new GsonBuilder().registerTypeAdapterFactory(new GeometryAdapterFactory()).create(); } protected T getEntity() { diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java index 3787c5d71..8d473ce5a 100644 --- a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java +++ b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java @@ -2,8 +2,14 @@ import io.micronaut.context.annotation.Bean; import io.micronaut.context.annotation.Factory; +import io.micronaut.context.annotation.Property; import io.micronaut.context.annotation.Prototype; import lombok.NonNull; +import org.brapi.v2.model.core.BrAPIListSummary; +import org.brapi.v2.model.core.BrAPIListTypes; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; +import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; import org.breedinginsight.brapps.importer.services.processors.experiment.service.ObservationUnitService; import org.brapi.v2.model.core.BrAPIStudy; @@ -13,6 +19,7 @@ import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.brapi.v2.model.pheno.BrAPIObservationVariable; import org.breedinginsight.model.ProgramLocation; +import org.breedinginsight.utilities.Utilities; import javax.inject.Inject; @@ -20,10 +27,16 @@ public class DeltaEntityFactory { private final ObservationUnitService observationUnitService; + private final BrAPIGermplasmService germplasmService; + private final String applicationReferenceSourceBase; @Inject - public DeltaEntityFactory(ObservationUnitService observationUnitService) { + public DeltaEntityFactory(ObservationUnitService observationUnitService, + BrAPIGermplasmService germplasmService, + @Property(name = "brapi.server.reference-source") String applicationReferenceSourceBase) { this.observationUnitService = observationUnitService; + this.germplasmService = germplasmService; + this.applicationReferenceSourceBase = applicationReferenceSourceBase; } private Experiment makeExperiment(BrAPITrial brAPIObject) { @@ -54,6 +67,46 @@ private DeltaObservationVariable makeDeltaObservationVariable(BrAPIObservationVa return new DeltaObservationVariable(brAPIObject); } + private DeltaGermplasmListSummary makeDeltaGermplasmListSummary(BrAPIListSummary brAPIObject) { + return new DeltaGermplasmListSummary(brAPIObject); + } + + private DeltaGermplasmListDetails makeDeltaGermplasmListDetails(BrAPIListDetails brAPIObject, String referenceSourceBase, BrAPIGermplasmService germplasmService) { + return new DeltaGermplasmListDetails(brAPIObject, referenceSourceBase, germplasmService); + } + + @Bean + @Prototype + public DeltaListSummary makeDeltaListSummaryBean(@NonNull BrAPIListSummary brAPIObject) { + BrAPIListTypes listType = brAPIObject.getListType(); + switch (listType) { + case GERMPLASM: return makeDeltaGermplasmListSummary(brAPIObject); + default: return null; + } + } + + @Bean + @Prototype + public DeltaListDetails makeDeltaListDetailsBean(@NonNull BrAPIListDetails brAPIObject) { + BrAPIListTypes listType = brAPIObject.getListType(); + switch (listType) { + case GERMPLASM: return makeDeltaGermplasmListDetailsBean(brAPIObject); + default: return null; + } + } + + @Bean + @Prototype + public DeltaGermplasmListSummary makeDeltaGermplasmListSummaryBean(@NonNull BrAPIListSummary brAPIObject) { + return makeDeltaGermplasmListSummary(brAPIObject); + } + + @Bean + @Prototype + public DeltaGermplasmListDetails makeDeltaGermplasmListDetailsBean(@NonNull BrAPIListDetails brAPIObject) { + return makeDeltaGermplasmListDetails(brAPIObject, applicationReferenceSourceBase, germplasmService); + } + @Bean @Prototype public Experiment makeExperimentBean(@NonNull BrAPITrial brAPIObject) { diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java new file mode 100644 index 000000000..1f8ea3d9c --- /dev/null +++ b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java @@ -0,0 +1,62 @@ +package org.breedinginsight.model.delta; + +import io.micronaut.context.annotation.Prototype; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.brapi.v2.model.germ.BrAPIGermplasm; +import org.breedinginsight.api.model.v1.request.query.SearchRequest; +import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; +import org.breedinginsight.brapi.v2.model.request.query.GermplasmQuery; +import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; +import org.breedinginsight.brapps.importer.model.exports.FileType; +import org.breedinginsight.model.DownloadFile; +import org.breedinginsight.services.exceptions.DoesNotExistException; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; +import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; +import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; + +import java.io.IOException; +import java.util.List; +import java.util.UUID; + + +@Prototype +public class DeltaGermplasmListDetails extends DeltaListDetails { + + private final BrAPIGermplasmService germplasmService; + + // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. + DeltaGermplasmListDetails(BrAPIListDetails brAPIListDetails, + String referenceSourceBase, + BrAPIGermplasmService germplasmService) { + super(brAPIListDetails, referenceSourceBase); + this.germplasmService = germplasmService; + } + + @Override + public List getDataObjects() throws ApiException, UnprocessableEntityException { + UUID programId = getProgramId().orElseThrow(() -> new UnprocessableEntityException("Program Id not found for list " + getListName())); + return germplasmService.getGermplasmByList(programId, getListDbId()); + } + + @Override + public DownloadFile exportListObjects(FileType extension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException, UnprocessableEntityException { + UUID programId = getProgramId().orElseThrow(() -> new UnprocessableEntityException("Program Id not found for list " + getListName())); + return germplasmService.exportGermplasmList(programId, getListDbId(), extension); + } + + @Override + public AbstractQueryMapper getQueryMapper() { + return new GermplasmQueryMapper(); + } + + @Override + public BrapiQuery getQuery() { + return new GermplasmQuery(); + } + + @Override + public SearchRequest constructSearchRequest(BrapiQuery queryParams) { + return ((GermplasmQuery) queryParams).constructSearchRequest(); + } +} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java new file mode 100644 index 000000000..6ddd2518b --- /dev/null +++ b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java @@ -0,0 +1,16 @@ +package org.breedinginsight.model.delta; + +import io.micronaut.context.annotation.Prototype; +import lombok.Getter; +import lombok.Setter; +import org.brapi.v2.model.core.BrAPIListSummary; +import org.breedinginsight.brapps.importer.model.response.ImportObjectState; + + +@Prototype +public class DeltaGermplasmListSummary extends DeltaListSummary { + + // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. + DeltaGermplasmListSummary(BrAPIListSummary brAPIListSummary) { super(brAPIListSummary); } + +} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java b/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java new file mode 100644 index 000000000..9f2c745bf --- /dev/null +++ b/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java @@ -0,0 +1,78 @@ +package org.breedinginsight.model.delta; + +import io.micronaut.context.annotation.Prototype; +import lombok.Getter; +import lombok.Setter; +import lombok.Value; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.BrAPIExternalReference; +import org.brapi.v2.model.core.BrAPIListTypes; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.breedinginsight.api.model.v1.request.query.SearchRequest; +import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; +import org.breedinginsight.brapps.importer.model.exports.FileType; +import org.breedinginsight.brapps.importer.model.response.ImportObjectState; +import io.micronaut.context.annotation.Property; +import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; +import org.breedinginsight.model.DownloadFile; +import org.breedinginsight.services.exceptions.DoesNotExistException; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; +import org.breedinginsight.utilities.Utilities; +import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + + +@Prototype +public abstract class DeltaListDetails extends DeltaEntity { + private final String referenceSourceBase; + @Getter + @Setter + private ImportObjectState state; + + // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. + DeltaListDetails(BrAPIListDetails brAPIListDetails, String referenceSourceBase) { + super(brAPIListDetails); + this.referenceSourceBase = referenceSourceBase; + } + + public abstract List getDataObjects() throws ApiException, UnprocessableEntityException; + public abstract DownloadFile exportListObjects(FileType extension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException, UnprocessableEntityException; + public abstract AbstractQueryMapper getQueryMapper(); + + public abstract BrapiQuery getQuery(); + + public abstract SearchRequest constructSearchRequest(BrapiQuery queryParams); + + public BrAPIListTypes getListType() { + return entity.getListType(); + } + + public Optional getListId() { + return getXrefId(ExternalReferenceSource.LISTS); + } + + public Optional getProgramId() { + return getXrefId(ExternalReferenceSource.PROGRAMS); + } + + public String getListDbId() { + return entity.getListDbId(); + } + + public String getListName() { + return entity.getListName(); + } + + private Optional getXrefId(ExternalReferenceSource source) { + // Get the external reference if it exists + Optional xrefOptional = Utilities.getExternalReference(entity.getExternalReferences(), referenceSourceBase, source); + + // Parse the Deltabreed ID from the xref + return xrefOptional.map(BrAPIExternalReference::getReferenceId).map(UUID::fromString); + } +} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java b/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java new file mode 100644 index 000000000..80569d077 --- /dev/null +++ b/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java @@ -0,0 +1,20 @@ +package org.breedinginsight.model.delta; + +import io.micronaut.context.annotation.Prototype; +import lombok.Getter; +import lombok.Setter; +import org.brapi.v2.model.core.BrAPIListSummary; +import org.breedinginsight.brapps.importer.model.response.ImportObjectState; + + +@Prototype +public class DeltaListSummary extends DeltaEntity { + + @Getter + @Setter + private ImportObjectState state; + + // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. + DeltaListSummary(BrAPIListSummary brAPIListSummary) { super(brAPIListSummary); } + +} diff --git a/src/main/java/org/breedinginsight/services/ListService.java b/src/main/java/org/breedinginsight/services/ListService.java new file mode 100644 index 000000000..c0c18f734 --- /dev/null +++ b/src/main/java/org/breedinginsight/services/ListService.java @@ -0,0 +1,33 @@ +package org.breedinginsight.services; + +import lombok.extern.slf4j.Slf4j; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; +import org.breedinginsight.daos.ListDAO; +import org.breedinginsight.model.delta.DeltaListDetails; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.UUID; + +@Slf4j +@Singleton +public class ListService { + private final BrAPIListDAO brAPIListDAO; + private final ListDAO listDAO; + + @Inject + public ListService(BrAPIListDAO brAPIListDAO, ListDAO listDAO) { + this.brAPIListDAO = brAPIListDAO; + this.listDAO = listDAO; + } + + public DeltaListDetails getDeltaListDetails(String listDbId, UUID programId) throws ApiException { + return listDAO.getDeltaListDetailsByDbId(listDbId, programId); + } + + public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + listDAO.deleteBrAPIList(listDbId, programId, hardDelete); + } + +} diff --git a/src/main/java/org/breedinginsight/utilities/Utilities.java b/src/main/java/org/breedinginsight/utilities/Utilities.java index 20f3254d6..7ed72cfef 100644 --- a/src/main/java/org/breedinginsight/utilities/Utilities.java +++ b/src/main/java/org/breedinginsight/utilities/Utilities.java @@ -17,6 +17,7 @@ package org.breedinginsight.utilities; +import io.micronaut.context.annotation.Property; import org.apache.commons.lang3.StringUtils; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.BrAPIExternalReference; @@ -24,6 +25,7 @@ import org.breedinginsight.model.Program; import org.flywaydb.core.api.migration.Context; +import javax.inject.Inject; import java.lang.reflect.Field; import java.sql.ResultSet; import java.sql.Statement; From 664083e402b6958a09678a84eb081d5bdbe0e5e7 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:24:31 -0500 Subject: [PATCH 046/132] get germplasm list contents via germplasm controller --- .../brapi/v2/BrAPIGermplasmController.java | 3 +- .../brapi/v2/BrAPIListController.java | 21 ++-- .../brapi/v2/dao/BrAPIListDAO.java | 78 +++++++++++++- .../model/request/query/GermplasmQuery.java | 3 + .../brapi/v2/services/BrAPIListService.java | 10 ++ .../org/breedinginsight/daos/ListDAO.java | 102 ------------------ .../breedinginsight/services/ListService.java | 33 ------ 7 files changed, 97 insertions(+), 153 deletions(-) delete mode 100644 src/main/java/org/breedinginsight/daos/ListDAO.java delete mode 100644 src/main/java/org/breedinginsight/services/ListService.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java index 4d67e2a00..57ead5bd3 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java @@ -209,7 +209,8 @@ public HttpResponse>>> getGermplasm( germplasmQueryMapper.setDateDisplayFormat(dateFormatParam); } - List germplasm = germplasmService.getGermplasm(programId); + // Fetch all germplasm in the program unless a list id is supplied to return only germplasm in that collection + List germplasm = queryParams.getList() == null ? germplasmService.getGermplasm(programId) : germplasmService.getGermplasmByList(programId, queryParams.getList());; SearchRequest searchRequest = queryParams.constructSearchRequest(); return ResponseUtils.getBrapiQueryResponse(germplasm, germplasmQueryMapper, queryParams, searchRequest); } catch (ApiException e) { diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 0eebc602d..2d3324a65 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -13,6 +13,7 @@ import org.brapi.v2.model.core.BrAPIListTypes; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; +import org.breedinginsight.api.model.v1.request.query.SearchRequest; import org.breedinginsight.api.model.v1.response.DataResponse; import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.api.model.v1.validators.QueryValid; @@ -24,13 +25,11 @@ import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.Program; import org.breedinginsight.model.delta.DeltaListDetails; -import org.breedinginsight.services.ListService; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.response.ResponseUtils; import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; import org.breedinginsight.utilities.response.mappers.ListQueryMapper; -import org.breedinginsight.api.model.v1.request.query.SearchRequest; import javax.inject.Inject; import javax.validation.ConstraintViolation; @@ -47,21 +46,19 @@ public class BrAPIListController { private final ProgramService programService; private final BrAPIListService brapiListService; - private final ListService listService; private final ListQueryMapper listQueryMapper; private final Validator validator; @Inject - public BrAPIListController(ProgramService programService, BrAPIListService brapiListService, ListService listService, + public BrAPIListController(ProgramService programService, BrAPIListService brapiListService, ListQueryMapper listQueryMapper, Validator validator) { this.programService = programService; this.brapiListService = brapiListService; - this.listService = listService; this.listQueryMapper = listQueryMapper; this.validator = validator; } - @Get("/deltalists{?queryParams*}") + @Get("/lists{?queryParams*}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse>> getLists( @@ -101,7 +98,7 @@ public HttpResponse>> getLists( } } - @Delete("/deltalists/{listDbId}") + @Delete("/lists/{listDbId}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse>> deleteListById( @@ -115,7 +112,7 @@ public HttpResponse>> deleteListById( hardDelete = "true".equals(paramValue); } try { - listService.deleteBrAPIList(listDbId, programId, hardDelete); + brapiListService.deleteBrAPIList(listDbId, programId, hardDelete); return HttpResponse.status(HttpStatus.NO_CONTENT); } catch (Exception e) { log.info(e.getMessage(), e); @@ -123,7 +120,7 @@ public HttpResponse>> deleteListById( } } - @Get("/deltalists/{listDbId}") + @Get("/lists/{listDbId}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) @SuppressWarnings("unchecked") @@ -133,7 +130,7 @@ public HttpResponse>>> g HttpRequest request) { try { // Get the list from the BrAPI service - DeltaListDetails details = listService.getDeltaListDetails(listDbId, programId); + DeltaListDetails details = brapiListService.getDeltaListDetails(listDbId, programId); // Get a new instance of BrAPI query matching the type of list contents T queryParams = (T) details.getQuery(); @@ -167,7 +164,7 @@ public HttpResponse>>> g } } - @Get("/deltalists/{listDbId}/export{?fileExtension}") + @Get("/lists/{listDbId}/export{?fileExtension}") @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse germplasmListExport( @@ -175,7 +172,7 @@ public HttpResponse germplasmListExport( String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; try { // Get the list from the BrAPI service - DeltaListDetails details = listService.getDeltaListDetails(listDbId, programId); + DeltaListDetails details = brapiListService.getDeltaListDetails(listDbId, programId); FileType extension = Enum.valueOf(FileType.class, fileExtension); DownloadFile listContentsFile = details.exportListObjects(extension); diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index 801b9775c..ed7db131c 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -17,7 +17,12 @@ package org.breedinginsight.brapi.v2.dao; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.exceptions.HttpStatusException; import lombok.extern.slf4j.Slf4j; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; import org.brapi.client.v2.ApiResponse; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.model.queryParams.core.ListQueryParams; @@ -31,19 +36,24 @@ import org.brapi.v2.model.core.request.BrAPIListSearchRequest; import org.brapi.v2.model.core.response.*; import org.brapi.v2.model.pheno.BrAPIObservation; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.daos.ProgramDAO; +import org.breedinginsight.model.ProgramBrAPIEndpoints; +import org.breedinginsight.model.delta.DeltaEntityFactory; +import org.breedinginsight.model.delta.DeltaListDetails; +import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; +import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.BrAPIDAOUtil; import org.breedinginsight.utilities.Utilities; import javax.inject.Inject; import javax.validation.constraints.NotNull; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.TimeUnit; @Slf4j public class BrAPIListDAO { @@ -52,13 +62,17 @@ public class BrAPIListDAO { private ImportDAO importDAO; private final BrAPIDAOUtil brAPIDAOUtil; private final BrAPIEndpointProvider brAPIEndpointProvider; + private final DeltaEntityFactory deltaEntityFactory; + private final ProgramService programService; @Inject - public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider) { + public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, DeltaEntityFactory deltaEntityFactory, ProgramService programService) { this.programDAO = programDAO; this.importDAO = importDAO; this.brAPIDAOUtil = brAPIDAOUtil; this.brAPIEndpointProvider = brAPIEndpointProvider; + this.deltaEntityFactory = deltaEntityFactory; + this.programService = programService; } public List getListByName(List listNames, UUID programId) throws ApiException { @@ -196,4 +210,58 @@ public List createBrAPILists(List brapiLi throw new ApiException("No response after creating list"); } + + public DeltaListDetails getDeltaListDetailsByDbId(String listDbId, UUID programId) throws ApiException { + ListsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ListsApi.class); + ApiResponse response = api.listsListDbIdGet(listDbId); + if (Objects.isNull(response.getBody()) || Objects.isNull(response.getBody().getResult())) + { + throw new ApiException(); + } + + BrAPIListDetails details = response.getBody().getResult(); + return deltaEntityFactory.makeDeltaListDetailsBean(details); + } + + public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); + var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); + requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); + HttpUrl url = requestUrl.build(); + var brapiRequest = new Request.Builder().url(url) + .method("DELETE", null) + .addHeader("Content-Type", "application/json") + .build(); + + makeCall(brapiRequest); + } + + private void makeCall(Request brapiRequest) throws ApiException { + OkHttpClient client = new OkHttpClient.Builder() + .readTimeout(5, TimeUnit.MINUTES) + .build(); + try { + client.newCall(brapiRequest).execute(); + } catch (IOException e) { + log.error("Error calling BrAPI Service", e); + throw new ApiException("Error calling BrAPI Service"); + } + } + + private String getProgramBrAPIBaseUrl(UUID programId) { + ProgramBrAPIEndpoints programBrAPIEndpoints; + try { + programBrAPIEndpoints = programService.getBrapiEndpoints(programId); + } catch (DoesNotExistException e) { + throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); + } + + if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { + log.error("Program: " + programId + " is missing BrAPI URL config"); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); + } + var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); + programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; + return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; + } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java b/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java index 88f6d142c..92752fad6 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java +++ b/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java @@ -27,6 +27,9 @@ public class GermplasmQuery extends BrapiQuery { // This is a meta-parameter, it describes the display format of any date fields. private String dateDisplayFormat; + // The list id used to get a collection of germplasm + private String list; + public SearchRequest constructSearchRequest() { List filters = new ArrayList<>(); if (!StringUtils.isBlank(getImportEntryNumber())) { diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java index 3353ac17c..7dd7c81c6 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java @@ -12,6 +12,7 @@ import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.model.Program; +import org.breedinginsight.model.delta.DeltaListDetails; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.Utilities; @@ -19,6 +20,7 @@ import javax.inject.Singleton; import java.util.List; import java.util.Optional; +import java.util.UUID; import java.util.stream.Collectors; @Slf4j @@ -88,4 +90,12 @@ public List getListSummariesByTypeAndXref( return programLists; } + + public DeltaListDetails getDeltaListDetails(String listDbId, UUID programId) throws ApiException { + return listDAO.getDeltaListDetailsByDbId(listDbId, programId); + } + + public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + listDAO.deleteBrAPIList(listDbId, programId, hardDelete); + } } diff --git a/src/main/java/org/breedinginsight/daos/ListDAO.java b/src/main/java/org/breedinginsight/daos/ListDAO.java deleted file mode 100644 index f44a8757f..000000000 --- a/src/main/java/org/breedinginsight/daos/ListDAO.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.breedinginsight.daos; - -import io.micronaut.http.HttpStatus; -import io.micronaut.http.exceptions.HttpStatusException; -import lombok.extern.slf4j.Slf4j; -import okhttp3.*; -import org.brapi.client.v2.ApiResponse; -import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.client.v2.modules.core.ListsApi; -import org.brapi.v2.model.core.response.BrAPIListDetails; -import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; -import org.breedinginsight.brapi.v1.controller.BrapiVersion; -import org.breedinginsight.model.ProgramBrAPIEndpoints; -import org.breedinginsight.model.delta.DeltaEntityFactory; -import org.breedinginsight.model.delta.DeltaListDetails; -import org.breedinginsight.services.ProgramService; -import org.breedinginsight.services.brapi.BrAPIEndpointProvider; -import org.breedinginsight.services.exceptions.DoesNotExistException; -import org.breedinginsight.utilities.BrAPIDAOUtil; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.io.IOException; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -@Slf4j -@Singleton -public class ListDAO { - private final ProgramDAO programDAO; - private final BrAPIDAOUtil brAPIDAOUtil; - private final BrAPIEndpointProvider brAPIEndpointProvider; - private final DeltaEntityFactory deltaEntityFactory; - private final ProgramService programService; - - @Inject - public ListDAO(ProgramDAO programDAO, - BrAPIDAOUtil brAPIDAOUtil, - BrAPIEndpointProvider brAPIEndpointProvider, - DeltaEntityFactory deltaEntityFactory, ProgramService programService) { - this.programDAO = programDAO; - this.brAPIDAOUtil = brAPIDAOUtil; - this.brAPIEndpointProvider = brAPIEndpointProvider; - this.deltaEntityFactory = deltaEntityFactory; - this.programService = programService; - } - - public DeltaListDetails getDeltaListDetailsByDbId(String listDbId, UUID programId) throws ApiException { - ListsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ListsApi.class); - ApiResponse response = api.listsListDbIdGet(listDbId); - if (Objects.isNull(response.getBody()) || Objects.isNull(response.getBody().getResult())) - { - throw new ApiException(); - } - - BrAPIListDetails details = response.getBody().getResult(); - return deltaEntityFactory.makeDeltaListDetailsBean(details); - } - - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { - var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); - var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); - requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); - HttpUrl url = requestUrl.build(); - var brapiRequest = new Request.Builder().url(url) - .method("DELETE", null) - .addHeader("Content-Type", "application/json") - .build(); - - makeCall(brapiRequest); - } - - private void makeCall(Request brapiRequest) throws ApiException { - OkHttpClient client = new OkHttpClient.Builder() - .readTimeout(5, TimeUnit.MINUTES) - .build(); - try { - client.newCall(brapiRequest).execute(); - } catch (IOException e) { - log.error("Error calling BrAPI Service", e); - throw new ApiException("Error calling BrAPI Service"); - } - } - - private String getProgramBrAPIBaseUrl(UUID programId) { - ProgramBrAPIEndpoints programBrAPIEndpoints; - try { - programBrAPIEndpoints = programService.getBrapiEndpoints(programId); - } catch (DoesNotExistException e) { - throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); - } - - if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { - log.error("Program: " + programId + " is missing BrAPI URL config"); - throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); - } - var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); - programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; - return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; - } -} diff --git a/src/main/java/org/breedinginsight/services/ListService.java b/src/main/java/org/breedinginsight/services/ListService.java deleted file mode 100644 index c0c18f734..000000000 --- a/src/main/java/org/breedinginsight/services/ListService.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.breedinginsight.services; - -import lombok.extern.slf4j.Slf4j; -import org.brapi.client.v2.model.exceptions.ApiException; -import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; -import org.breedinginsight.daos.ListDAO; -import org.breedinginsight.model.delta.DeltaListDetails; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.UUID; - -@Slf4j -@Singleton -public class ListService { - private final BrAPIListDAO brAPIListDAO; - private final ListDAO listDAO; - - @Inject - public ListService(BrAPIListDAO brAPIListDAO, ListDAO listDAO) { - this.brAPIListDAO = brAPIListDAO; - this.listDAO = listDAO; - } - - public DeltaListDetails getDeltaListDetails(String listDbId, UUID programId) throws ApiException { - return listDAO.getDeltaListDetailsByDbId(listDbId, programId); - } - - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { - listDAO.deleteBrAPIList(listDbId, programId, hardDelete); - } - -} From 582c695d8036e3988fc5df69f8cb5985a8132e02 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:51:38 -0500 Subject: [PATCH 047/132] revert GET list data objects from ListController --- .../brapi/v2/BrAPIGermplasmController.java | 24 ++-- .../brapi/v2/BrAPIListController.java | 124 +----------------- .../brapi/v2/dao/BrAPIListDAO.java | 31 ++--- .../v2/services/BrAPIGermplasmService.java | 14 +- .../brapi/v2/services/BrAPIListService.java | 5 - .../model/delta/DeltaEntityFactory.java | 57 +------- .../delta/DeltaGermplasmListDetails.java | 62 --------- .../delta/DeltaGermplasmListSummary.java | 16 --- .../model/delta/DeltaListDetails.java | 78 ----------- .../model/delta/DeltaListSummary.java | 20 --- .../breedinginsight/utilities/Utilities.java | 2 - 11 files changed, 40 insertions(+), 393 deletions(-) delete mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java delete mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java delete mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java delete mode 100644 src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java index 57ead5bd3..94cf9c9fc 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java @@ -1,5 +1,6 @@ package org.breedinginsight.brapi.v2; +import com.drew.lang.annotations.Nullable; import io.micronaut.context.annotation.Property; import io.micronaut.http.HttpHeaders; import io.micronaut.http.HttpResponse; @@ -19,10 +20,11 @@ import org.brapi.v2.model.BrAPIIndexPagination; import org.brapi.v2.model.BrAPIMetadata; import org.brapi.v2.model.BrAPIStatus; -import org.brapi.v2.model.core.BrAPITrial; import org.brapi.v2.model.germ.*; import org.brapi.v2.model.germ.request.BrAPIGermplasmSearchRequest; -import org.brapi.v2.model.germ.response.*; +import org.brapi.v2.model.germ.response.BrAPIGermplasmListResponse; +import org.brapi.v2.model.germ.response.BrAPIGermplasmPedigreeResponse; +import org.brapi.v2.model.germ.response.BrAPIGermplasmProgenyResponse; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; import org.breedinginsight.api.model.v1.request.query.SearchRequest; @@ -33,27 +35,25 @@ import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; import org.breedinginsight.brapi.v2.model.request.query.GermplasmQuery; -import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; -import org.breedinginsight.model.Program; -import org.breedinginsight.services.ProgramService; -import org.breedinginsight.utilities.Utilities; -import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; import org.breedinginsight.brapps.importer.model.exports.FileType; import org.breedinginsight.daos.ProgramDAO; import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.GermplasmGenotype; +import org.breedinginsight.model.Program; +import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; import org.breedinginsight.services.exceptions.AuthorizationException; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.services.geno.GenotypeService; +import org.breedinginsight.utilities.Utilities; import org.breedinginsight.utilities.response.ResponseUtils; +import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; import javax.inject.Inject; import javax.validation.Valid; import java.util.*; import java.util.regex.Pattern; -import java.util.stream.Collectors; @Slf4j @Controller("/${micronaut.bi.api.version}") @@ -222,15 +222,17 @@ public HttpResponse>>> getGermplasm( } } - @Get("/programs/{programId}/germplasm/export{?fileExtension}") + @Get("/programs/{programId}/germplasm/export{?fileExtension,list}") @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) public HttpResponse germplasmExport( - @PathVariable("programId") UUID programId, @QueryValue(defaultValue = "XLSX") String fileExtension) { + @PathVariable("programId") UUID programId, + @QueryValue(defaultValue = "XLSX") String fileExtension, + @QueryValue Optional list) { String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; try { FileType extension = Enum.valueOf(FileType.class, fileExtension); - DownloadFile germplasmListFile = germplasmService.exportGermplasm(programId, extension); + DownloadFile germplasmListFile = list.isEmpty() ? germplasmService.exportGermplasm(programId, extension) : germplasmService.exportGermplasmList(programId, list.get(), extension); HttpResponse germplasmExport = HttpResponse.ok(germplasmListFile.getStreamedFile()).header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="+germplasmListFile.getFileName()+extension.getExtension()); return germplasmExport; } diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 2d3324a65..3cb73dbae 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -1,10 +1,10 @@ package org.breedinginsight.brapi.v2; -import io.micronaut.core.beans.BeanIntrospection; -import io.micronaut.core.beans.BeanProperty; -import io.micronaut.http.*; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; import io.micronaut.http.annotation.*; -import io.micronaut.http.server.types.files.StreamedFile; import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import lombok.extern.slf4j.Slf4j; @@ -18,27 +18,19 @@ import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.api.model.v1.validators.QueryValid; import org.breedinginsight.brapi.v1.controller.BrapiVersion; -import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; import org.breedinginsight.brapi.v2.model.request.query.ListQuery; import org.breedinginsight.brapi.v2.services.BrAPIListService; -import org.breedinginsight.brapps.importer.model.exports.FileType; -import org.breedinginsight.model.DownloadFile; import org.breedinginsight.model.Program; -import org.breedinginsight.model.delta.DeltaListDetails; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.response.ResponseUtils; -import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; import org.breedinginsight.utilities.response.mappers.ListQueryMapper; import javax.inject.Inject; -import javax.validation.ConstraintViolation; import javax.validation.Valid; import javax.validation.Validator; import java.util.List; -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; @Slf4j @Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @@ -105,7 +97,7 @@ public HttpResponse>> deleteListById( @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, HttpRequest request - ) throws DoesNotExistException, ApiException { + ) { boolean hardDelete = false; if (request.getParameters().contains("hardDelete")) { String paramValue = request.getParameters().get("hardDelete"); @@ -119,110 +111,4 @@ public HttpResponse>> deleteListById( return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving germplasm list records"); } } - - @Get("/lists/{listDbId}") - @Produces(MediaType.APPLICATION_JSON) - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - @SuppressWarnings("unchecked") - public HttpResponse>>> getListById( - @PathVariable("programId") UUID programId, - @PathVariable("listDbId") String listDbId, - HttpRequest request) { - try { - // Get the list from the BrAPI service - DeltaListDetails details = brapiListService.getDeltaListDetails(listDbId, programId); - - // Get a new instance of BrAPI query matching the type of list contents - T queryParams = (T) details.getQuery(); - - // Bind query parameters to the object - bindQueryParams(queryParams, request); - - // Perform standard bean validation - Set> violations = validator.validate(queryParams); - if (!violations.isEmpty()) { - List errorMessages = violations.stream() - .map(ConstraintViolation::getMessage) - .collect(Collectors.toList()); - log.info(String.join(", ", errorMessages)); - return HttpResponse.status(HttpStatus.BAD_REQUEST, "Error with list contents search parameters"); - } - - // Fetch the list contents from the BrAPI service - List listContentsBrAPIObjects = (List) details.getDataObjects(); - - // Construct a search request for sorting the list contents - SearchRequest searchRequest = details.constructSearchRequest(queryParams); - - // Get the map used to connect query sorting keys to contents object values - AbstractQueryMapper contentsQueryMapper = details.getQueryMapper(); - - return ResponseUtils.getBrapiQueryResponse(listContentsBrAPIObjects, contentsQueryMapper, queryParams, searchRequest); - } catch (Exception e) { - log.info(e.getMessage(), e); - return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving list records"); - } - } - - @Get("/lists/{listDbId}/export{?fileExtension}") - @Produces(value = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - public HttpResponse germplasmListExport( - @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, @QueryValue(defaultValue = "XLSX") String fileExtension) { - String downloadErrorMessage = "An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu."; - try { - // Get the list from the BrAPI service - DeltaListDetails details = brapiListService.getDeltaListDetails(listDbId, programId); - - FileType extension = Enum.valueOf(FileType.class, fileExtension); - DownloadFile listContentsFile = details.exportListObjects(extension); - return HttpResponse.ok(listContentsFile.getStreamedFile()).header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="+listContentsFile.getFileName()+extension.getExtension()); - } - catch (Exception e) { - log.info(e.getMessage(), e); - e.printStackTrace(); - HttpResponse response = HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, downloadErrorMessage).contentType(MediaType.TEXT_PLAIN).body(downloadErrorMessage); - return response; - } - } - - private void bindQueryParams(BrapiQuery queryParams, HttpRequest request) { - BeanIntrospection introspection = BeanIntrospection.getIntrospection(BrapiQuery.class); - for (BeanProperty property : introspection.getBeanProperties()) { - String paramName = property.getName(); - if (request.getParameters().contains(paramName)) { - String paramValue = request.getParameters().get(paramName); - Object convertedValue; - Class propertyType = property.getType(); - - if (propertyType.isEnum()) { - convertedValue = convertToEnum(paramValue, (Class>) propertyType); - } else { - convertedValue = convertValue(paramValue, propertyType); - } - - property.set(queryParams, convertedValue); - } - } - } - - private > T convertToEnum(String value, Class> enumClass) { - if (value == null) { - return null; - } - return Enum.valueOf((Class) enumClass, value.toUpperCase()); - } - - - // Convert, if necessary, the values of query parameters to match the type defined for the fields in the BrapiQuery class - private Object convertValue(String value, Class targetType) { - // Implement type conversion logic here - // Other list content types might need more complex logic - if (targetType == String.class) return value; - if (targetType == Integer.class) return Integer.parseInt(value); - if (targetType == Long.class) return Long.parseLong(value); - if (targetType == Boolean.class) return Boolean.parseBoolean(value); - // Add more type conversions as needed - return value; - } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index ed7db131c..55b395ea3 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -25,7 +25,6 @@ import okhttp3.Request; import org.brapi.client.v2.ApiResponse; import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.client.v2.model.queryParams.core.ListQueryParams; import org.brapi.client.v2.modules.core.ListsApi; import org.brapi.v2.model.BrAPIExternalReference; import org.brapi.v2.model.BrAPIResponse; @@ -34,15 +33,15 @@ import org.brapi.v2.model.core.BrAPIListTypes; import org.brapi.v2.model.core.request.BrAPIListNewRequest; import org.brapi.v2.model.core.request.BrAPIListSearchRequest; -import org.brapi.v2.model.core.response.*; -import org.brapi.v2.model.pheno.BrAPIObservation; +import org.brapi.v2.model.core.response.BrAPIListDetails; +import org.brapi.v2.model.core.response.BrAPIListsListResponse; +import org.brapi.v2.model.core.response.BrAPIListsListResponseResult; +import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.daos.ProgramDAO; import org.breedinginsight.model.ProgramBrAPIEndpoints; -import org.breedinginsight.model.delta.DeltaEntityFactory; -import org.breedinginsight.model.delta.DeltaListDetails; import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; import org.breedinginsight.services.exceptions.DoesNotExistException; @@ -52,7 +51,10 @@ import javax.inject.Inject; import javax.validation.constraints.NotNull; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; import java.util.concurrent.TimeUnit; @Slf4j @@ -62,16 +64,14 @@ public class BrAPIListDAO { private ImportDAO importDAO; private final BrAPIDAOUtil brAPIDAOUtil; private final BrAPIEndpointProvider brAPIEndpointProvider; - private final DeltaEntityFactory deltaEntityFactory; private final ProgramService programService; @Inject - public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, DeltaEntityFactory deltaEntityFactory, ProgramService programService) { + public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, ProgramService programService) { this.programDAO = programDAO; this.importDAO = importDAO; this.brAPIDAOUtil = brAPIDAOUtil; this.brAPIEndpointProvider = brAPIEndpointProvider; - this.deltaEntityFactory = deltaEntityFactory; this.programService = programService; } @@ -211,19 +211,8 @@ public List createBrAPILists(List brapiLi throw new ApiException("No response after creating list"); } - public DeltaListDetails getDeltaListDetailsByDbId(String listDbId, UUID programId) throws ApiException { - ListsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ListsApi.class); - ApiResponse response = api.listsListDbIdGet(listDbId); - if (Objects.isNull(response.getBody()) || Objects.isNull(response.getBody().getResult())) - { - throw new ApiException(); - } - - BrAPIListDetails details = response.getBody().getResult(); - return deltaEntityFactory.makeDeltaListDetailsBean(details); - } - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + // TODO: Switch to using the ListsApi from the BrAPI client library once the delete endpoints are merged into it var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java index 8023327d7..231978368 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java @@ -82,7 +82,7 @@ public Optional getGermplasmByDBID(UUID programId, String germpl return germplasmDAO.getGermplasmByDBID(germplasmId, programId); } - public List> processListData(List germplasm, BrAPIListDetails germplasmList, Program program){ + public List> processListData(List germplasm, List listData, Program program){ Map germplasmByName = new HashMap<>(); for (BrAPIGermplasm g: germplasm) { // Use the full, unique germplasmName with programKey and accessionNumber (GID) for 2 reasons: @@ -97,14 +97,14 @@ public List> processListData(List germplasm, // This holds the BrAPI list items or all germplasm in a program if the list is null. List orderedGermplasmNames = new ArrayList<>(); - if (germplasmList == null) { + if (listData == null) { orderedGermplasmNames = germplasm.stream().sorted((left, right) -> { Integer leftAccessionNumber = Integer.parseInt(left.getAccessionNumber()); Integer rightAccessionNumber = Integer.parseInt(right.getAccessionNumber()); return leftAccessionNumber.compareTo(rightAccessionNumber); }).map(BrAPIGermplasm::getGermplasmName).collect(Collectors.toList()); } else { - orderedGermplasmNames = germplasmList.getData(); + orderedGermplasmNames = listData; } // For export, assign entry number sequentially based on BrAPI list order. @@ -124,7 +124,7 @@ public List> processListData(List germplasm, row.put("Source", source); // Use the entry number in the list map if generated - if(germplasmList == null) { + if(listData == null) { // Not downloading a real list, use GID (https://breedinginsight.atlassian.net/browse/BI-2266). row.put("Entry No", Integer.valueOf(germplasmEntry.getAccessionNumber())); } else { @@ -254,7 +254,9 @@ public DownloadFile exportGermplasm(UUID programId, FileType fileExtension) thro return new DownloadFile(fileName, downloadFile); } - public DownloadFile exportGermplasmList(UUID programId, String listId, FileType fileExtension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException { + public DownloadFile exportGermplasmList(UUID programId, + String listId, + FileType fileExtension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException { List columns = GermplasmFileColumns.getOrderedColumns(); //Retrieve germplasm list data @@ -270,7 +272,7 @@ public DownloadFile exportGermplasmList(UUID programId, String listId, FileType String fileName = createFileName(listData, listName); StreamedFile downloadFile; //Convert list data to List> data to pass into file writer - List> processedData = processListData(germplasm, listData, program); + List> processedData = processListData(germplasm, germplasmNames, program); if (fileExtension == FileType.CSV){ downloadFile = CSVWriter.writeToDownload(columns, processedData, fileExtension); diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java index 7dd7c81c6..1ee77e310 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java @@ -12,7 +12,6 @@ import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.model.Program; -import org.breedinginsight.model.delta.DeltaListDetails; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.Utilities; @@ -91,10 +90,6 @@ public List getListSummariesByTypeAndXref( return programLists; } - public DeltaListDetails getDeltaListDetails(String listDbId, UUID programId) throws ApiException { - return listDAO.getDeltaListDetailsByDbId(listDbId, programId); - } - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { listDAO.deleteBrAPIList(listDbId, programId, hardDelete); } diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java index 8d473ce5a..f1724ef12 100644 --- a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java +++ b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java @@ -7,19 +7,16 @@ import lombok.NonNull; import org.brapi.v2.model.core.BrAPIListSummary; import org.brapi.v2.model.core.BrAPIListTypes; -import org.brapi.v2.model.core.response.BrAPIListDetails; -import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; -import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; -import org.breedinginsight.brapps.importer.services.processors.experiment.service.ObservationUnitService; - import org.brapi.v2.model.core.BrAPIStudy; import org.brapi.v2.model.core.BrAPITrial; +import org.brapi.v2.model.core.response.BrAPIListDetails; import org.brapi.v2.model.germ.BrAPIGermplasm; import org.brapi.v2.model.pheno.BrAPIObservation; import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.brapi.v2.model.pheno.BrAPIObservationVariable; +import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; +import org.breedinginsight.brapps.importer.services.processors.experiment.service.ObservationUnitService; import org.breedinginsight.model.ProgramLocation; -import org.breedinginsight.utilities.Utilities; import javax.inject.Inject; @@ -27,16 +24,10 @@ public class DeltaEntityFactory { private final ObservationUnitService observationUnitService; - private final BrAPIGermplasmService germplasmService; - private final String applicationReferenceSourceBase; @Inject - public DeltaEntityFactory(ObservationUnitService observationUnitService, - BrAPIGermplasmService germplasmService, - @Property(name = "brapi.server.reference-source") String applicationReferenceSourceBase) { + public DeltaEntityFactory(ObservationUnitService observationUnitService) { this.observationUnitService = observationUnitService; - this.germplasmService = germplasmService; - this.applicationReferenceSourceBase = applicationReferenceSourceBase; } private Experiment makeExperiment(BrAPITrial brAPIObject) { @@ -67,46 +58,6 @@ private DeltaObservationVariable makeDeltaObservationVariable(BrAPIObservationVa return new DeltaObservationVariable(brAPIObject); } - private DeltaGermplasmListSummary makeDeltaGermplasmListSummary(BrAPIListSummary brAPIObject) { - return new DeltaGermplasmListSummary(brAPIObject); - } - - private DeltaGermplasmListDetails makeDeltaGermplasmListDetails(BrAPIListDetails brAPIObject, String referenceSourceBase, BrAPIGermplasmService germplasmService) { - return new DeltaGermplasmListDetails(brAPIObject, referenceSourceBase, germplasmService); - } - - @Bean - @Prototype - public DeltaListSummary makeDeltaListSummaryBean(@NonNull BrAPIListSummary brAPIObject) { - BrAPIListTypes listType = brAPIObject.getListType(); - switch (listType) { - case GERMPLASM: return makeDeltaGermplasmListSummary(brAPIObject); - default: return null; - } - } - - @Bean - @Prototype - public DeltaListDetails makeDeltaListDetailsBean(@NonNull BrAPIListDetails brAPIObject) { - BrAPIListTypes listType = brAPIObject.getListType(); - switch (listType) { - case GERMPLASM: return makeDeltaGermplasmListDetailsBean(brAPIObject); - default: return null; - } - } - - @Bean - @Prototype - public DeltaGermplasmListSummary makeDeltaGermplasmListSummaryBean(@NonNull BrAPIListSummary brAPIObject) { - return makeDeltaGermplasmListSummary(brAPIObject); - } - - @Bean - @Prototype - public DeltaGermplasmListDetails makeDeltaGermplasmListDetailsBean(@NonNull BrAPIListDetails brAPIObject) { - return makeDeltaGermplasmListDetails(brAPIObject, applicationReferenceSourceBase, germplasmService); - } - @Bean @Prototype public Experiment makeExperimentBean(@NonNull BrAPITrial brAPIObject) { diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java deleted file mode 100644 index 1f8ea3d9c..000000000 --- a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListDetails.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.breedinginsight.model.delta; - -import io.micronaut.context.annotation.Prototype; -import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.v2.model.core.response.BrAPIListDetails; -import org.brapi.v2.model.germ.BrAPIGermplasm; -import org.breedinginsight.api.model.v1.request.query.SearchRequest; -import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; -import org.breedinginsight.brapi.v2.model.request.query.GermplasmQuery; -import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; -import org.breedinginsight.brapps.importer.model.exports.FileType; -import org.breedinginsight.model.DownloadFile; -import org.breedinginsight.services.exceptions.DoesNotExistException; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; -import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; -import org.breedinginsight.utilities.response.mappers.GermplasmQueryMapper; - -import java.io.IOException; -import java.util.List; -import java.util.UUID; - - -@Prototype -public class DeltaGermplasmListDetails extends DeltaListDetails { - - private final BrAPIGermplasmService germplasmService; - - // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. - DeltaGermplasmListDetails(BrAPIListDetails brAPIListDetails, - String referenceSourceBase, - BrAPIGermplasmService germplasmService) { - super(brAPIListDetails, referenceSourceBase); - this.germplasmService = germplasmService; - } - - @Override - public List getDataObjects() throws ApiException, UnprocessableEntityException { - UUID programId = getProgramId().orElseThrow(() -> new UnprocessableEntityException("Program Id not found for list " + getListName())); - return germplasmService.getGermplasmByList(programId, getListDbId()); - } - - @Override - public DownloadFile exportListObjects(FileType extension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException, UnprocessableEntityException { - UUID programId = getProgramId().orElseThrow(() -> new UnprocessableEntityException("Program Id not found for list " + getListName())); - return germplasmService.exportGermplasmList(programId, getListDbId(), extension); - } - - @Override - public AbstractQueryMapper getQueryMapper() { - return new GermplasmQueryMapper(); - } - - @Override - public BrapiQuery getQuery() { - return new GermplasmQuery(); - } - - @Override - public SearchRequest constructSearchRequest(BrapiQuery queryParams) { - return ((GermplasmQuery) queryParams).constructSearchRequest(); - } -} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java b/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java deleted file mode 100644 index 6ddd2518b..000000000 --- a/src/main/java/org/breedinginsight/model/delta/DeltaGermplasmListSummary.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.breedinginsight.model.delta; - -import io.micronaut.context.annotation.Prototype; -import lombok.Getter; -import lombok.Setter; -import org.brapi.v2.model.core.BrAPIListSummary; -import org.breedinginsight.brapps.importer.model.response.ImportObjectState; - - -@Prototype -public class DeltaGermplasmListSummary extends DeltaListSummary { - - // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. - DeltaGermplasmListSummary(BrAPIListSummary brAPIListSummary) { super(brAPIListSummary); } - -} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java b/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java deleted file mode 100644 index 9f2c745bf..000000000 --- a/src/main/java/org/breedinginsight/model/delta/DeltaListDetails.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.breedinginsight.model.delta; - -import io.micronaut.context.annotation.Prototype; -import lombok.Getter; -import lombok.Setter; -import lombok.Value; -import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.v2.model.BrAPIExternalReference; -import org.brapi.v2.model.core.BrAPIListTypes; -import org.brapi.v2.model.core.response.BrAPIListDetails; -import org.breedinginsight.api.model.v1.request.query.SearchRequest; -import org.breedinginsight.brapi.v1.model.request.query.BrapiQuery; -import org.breedinginsight.brapps.importer.model.exports.FileType; -import org.breedinginsight.brapps.importer.model.response.ImportObjectState; -import io.micronaut.context.annotation.Property; -import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; -import org.breedinginsight.model.DownloadFile; -import org.breedinginsight.services.exceptions.DoesNotExistException; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; -import org.breedinginsight.utilities.Utilities; -import org.breedinginsight.utilities.response.mappers.AbstractQueryMapper; - -import java.io.IOException; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; - - -@Prototype -public abstract class DeltaListDetails extends DeltaEntity { - private final String referenceSourceBase; - @Getter - @Setter - private ImportObjectState state; - - // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. - DeltaListDetails(BrAPIListDetails brAPIListDetails, String referenceSourceBase) { - super(brAPIListDetails); - this.referenceSourceBase = referenceSourceBase; - } - - public abstract List getDataObjects() throws ApiException, UnprocessableEntityException; - public abstract DownloadFile exportListObjects(FileType extension) throws IllegalArgumentException, ApiException, IOException, DoesNotExistException, UnprocessableEntityException; - public abstract AbstractQueryMapper getQueryMapper(); - - public abstract BrapiQuery getQuery(); - - public abstract SearchRequest constructSearchRequest(BrapiQuery queryParams); - - public BrAPIListTypes getListType() { - return entity.getListType(); - } - - public Optional getListId() { - return getXrefId(ExternalReferenceSource.LISTS); - } - - public Optional getProgramId() { - return getXrefId(ExternalReferenceSource.PROGRAMS); - } - - public String getListDbId() { - return entity.getListDbId(); - } - - public String getListName() { - return entity.getListName(); - } - - private Optional getXrefId(ExternalReferenceSource source) { - // Get the external reference if it exists - Optional xrefOptional = Utilities.getExternalReference(entity.getExternalReferences(), referenceSourceBase, source); - - // Parse the Deltabreed ID from the xref - return xrefOptional.map(BrAPIExternalReference::getReferenceId).map(UUID::fromString); - } -} diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java b/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java deleted file mode 100644 index 80569d077..000000000 --- a/src/main/java/org/breedinginsight/model/delta/DeltaListSummary.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.breedinginsight.model.delta; - -import io.micronaut.context.annotation.Prototype; -import lombok.Getter; -import lombok.Setter; -import org.brapi.v2.model.core.BrAPIListSummary; -import org.breedinginsight.brapps.importer.model.response.ImportObjectState; - - -@Prototype -public class DeltaListSummary extends DeltaEntity { - - @Getter - @Setter - private ImportObjectState state; - - // Note: do not use @Inject, DeltaEntity are always constructed by DeltaEntityFactory. - DeltaListSummary(BrAPIListSummary brAPIListSummary) { super(brAPIListSummary); } - -} diff --git a/src/main/java/org/breedinginsight/utilities/Utilities.java b/src/main/java/org/breedinginsight/utilities/Utilities.java index 7ed72cfef..20f3254d6 100644 --- a/src/main/java/org/breedinginsight/utilities/Utilities.java +++ b/src/main/java/org/breedinginsight/utilities/Utilities.java @@ -17,7 +17,6 @@ package org.breedinginsight.utilities; -import io.micronaut.context.annotation.Property; import org.apache.commons.lang3.StringUtils; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.BrAPIExternalReference; @@ -25,7 +24,6 @@ import org.breedinginsight.model.Program; import org.flywaydb.core.api.migration.Context; -import javax.inject.Inject; import java.lang.reflect.Field; import java.sql.ResultSet; import java.sql.Statement; From c93e8344295e2cee7619c71505c86211e63a7320 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:39:09 -0500 Subject: [PATCH 048/132] update tests --- .../org/breedinginsight/model/delta/DeltaEntityFactory.java | 5 ----- .../brapi/v2/GermplasmControllerIntegrationTest.java | 4 ++-- .../brapi/v2/ListControllerIntegrationTest.java | 5 +---- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java index f1724ef12..059f91e66 100644 --- a/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java +++ b/src/main/java/org/breedinginsight/model/delta/DeltaEntityFactory.java @@ -2,19 +2,14 @@ import io.micronaut.context.annotation.Bean; import io.micronaut.context.annotation.Factory; -import io.micronaut.context.annotation.Property; import io.micronaut.context.annotation.Prototype; import lombok.NonNull; -import org.brapi.v2.model.core.BrAPIListSummary; -import org.brapi.v2.model.core.BrAPIListTypes; import org.brapi.v2.model.core.BrAPIStudy; import org.brapi.v2.model.core.BrAPITrial; -import org.brapi.v2.model.core.response.BrAPIListDetails; import org.brapi.v2.model.germ.BrAPIGermplasm; import org.brapi.v2.model.pheno.BrAPIObservation; import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.brapi.v2.model.pheno.BrAPIObservationVariable; -import org.breedinginsight.brapi.v2.services.BrAPIGermplasmService; import org.breedinginsight.brapps.importer.services.processors.experiment.service.ObservationUnitService; import org.breedinginsight.model.ProgramLocation; diff --git a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java index 23e8f54d4..7c80482d9 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java @@ -245,7 +245,7 @@ public void getAllGermplasmByListSuccess() { String germplasmListDbId = fetchGermplasmListDbId(programId); // Build the endpoint to get germplasm by germplasm list. - String endpoint = String.format("/programs/%s/germplasm/lists/%s/records", programId, germplasmListDbId); + String endpoint = String.format("/programs/%s/brapi/v2/germplasm?list=%s", programId, germplasmListDbId); // Get germplasm by list. Flowable> call = client.exchange( @@ -258,7 +258,7 @@ public void getAllGermplasmByListSuccess() { JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject().getAsJsonObject("result"); JsonArray data = result.getAsJsonArray("data"); - assertEquals(data.size(), 3, "Wrong number of germplasm were returned"); + assertEquals(3, data.size(), "Wrong number of germplasm were returned"); } @Test diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index 9515b72a6..e29ce7e24 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -145,9 +145,7 @@ public void setup() { newExp.put(traits.get(0).getObservationVariableName(), "1"); JsonObject result = importTestUtils.uploadAndFetchWorkflow( - importTestUtils.writeExperimentDataToFile(List.of(newExp), traits), null, true, client, program, mappingId, newExperimentWorkflowId - ); - + importTestUtils.writeExperimentDataToFile(List.of(newExp), traits), null, true, client, program, mappingId, newExperimentWorkflowId); } @Test @@ -189,5 +187,4 @@ public void getAllListsSuccess() { } } } - } From a4169ff86fcfdf393358921c6f21bcd17567e8b2 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:24:18 -0500 Subject: [PATCH 049/132] add httpok helper methods to BrAPIDAOUtil --- .../brapi/v2/dao/BrAPIListDAO.java | 39 +++------------ .../utilities/BrAPIDAOUtil.java | 47 +++++++++++++++++-- .../v2/ListControllerIntegrationTest.java | 32 +++++++++++++ .../daos/BrAPIDAOUtilUnitTest.java | 10 +++- ...gwaGenotypeServiceImplIntegrationTest.java | 7 ++- 5 files changed, 96 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index 55b395ea3..11e079842 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -64,15 +64,16 @@ public class BrAPIListDAO { private ImportDAO importDAO; private final BrAPIDAOUtil brAPIDAOUtil; private final BrAPIEndpointProvider brAPIEndpointProvider; - private final ProgramService programService; @Inject - public BrAPIListDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, ProgramService programService) { + public BrAPIListDAO(ProgramDAO programDAO, + ImportDAO importDAO, + BrAPIDAOUtil brAPIDAOUtil, + BrAPIEndpointProvider brAPIEndpointProvider) { this.programDAO = programDAO; this.importDAO = importDAO; this.brAPIDAOUtil = brAPIDAOUtil; this.brAPIEndpointProvider = brAPIEndpointProvider; - this.programService = programService; } public List getListByName(List listNames, UUID programId) throws ApiException { @@ -213,7 +214,7 @@ public List createBrAPILists(List brapiLi public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { // TODO: Switch to using the ListsApi from the BrAPI client library once the delete endpoints are merged into it - var programBrAPIBaseUrl = getProgramBrAPIBaseUrl(programId); + var programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(programId); var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); HttpUrl url = requestUrl.build(); @@ -222,35 +223,7 @@ public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) .addHeader("Content-Type", "application/json") .build(); - makeCall(brapiRequest); + brAPIDAOUtil.makeCall(brapiRequest); } - private void makeCall(Request brapiRequest) throws ApiException { - OkHttpClient client = new OkHttpClient.Builder() - .readTimeout(5, TimeUnit.MINUTES) - .build(); - try { - client.newCall(brapiRequest).execute(); - } catch (IOException e) { - log.error("Error calling BrAPI Service", e); - throw new ApiException("Error calling BrAPI Service"); - } - } - - private String getProgramBrAPIBaseUrl(UUID programId) { - ProgramBrAPIEndpoints programBrAPIEndpoints; - try { - programBrAPIEndpoints = programService.getBrapiEndpoints(programId); - } catch (DoesNotExistException e) { - throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); - } - - if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { - log.error("Program: " + programId + " is missing BrAPI URL config"); - throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); - } - var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); - programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; - return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; - } } diff --git a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java index e03a67017..075d6bb2b 100644 --- a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java +++ b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java @@ -18,25 +18,33 @@ package org.breedinginsight.utilities; import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.exceptions.HttpStatusException; import io.micronaut.http.server.exceptions.InternalServerException; import io.reactivex.functions.*; import lombok.extern.slf4j.Slf4j; +import okhttp3.OkHttpClient; +import okhttp3.Request; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.brapi.client.v2.ApiResponse; import org.brapi.client.v2.model.exceptions.ApiException; -import org.brapi.client.v2.modules.germplasm.GermplasmApi; import org.brapi.v2.model.*; -import org.brapi.v2.model.germ.BrAPIGermplasm; -import org.brapi.v2.model.germ.response.BrAPIGermplasmSingleResponse; +import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapps.importer.model.ImportUpload; +import org.breedinginsight.model.ProgramBrAPIEndpoints; +import org.breedinginsight.services.ProgramService; +import org.breedinginsight.services.exceptions.DoesNotExistException; import javax.inject.Inject; import javax.inject.Singleton; +import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.TimeUnit; import static org.brapi.v2.model.BrAPIWSMIMEDataTypes.APPLICATION_JSON; @@ -48,16 +56,19 @@ public class BrAPIDAOUtil { private final Duration searchTimeout; private final int pageSize; private final int postGroupSize; + private final ProgramService programService; @Inject public BrAPIDAOUtil(@Property(name = "brapi.search.wait-time") int searchWaitTime, @Property(name = "brapi.read-timeout") Duration searchTimeout, @Property(name = "brapi.page-size") int pageSize, - @Property(name = "brapi.post-group-size") int postGroupSize) { + @Property(name = "brapi.post-group-size") int postGroupSize, + ProgramService programService) { this.searchWaitTime = searchWaitTime; this.searchTimeout = searchTimeout; this.pageSize = pageSize; this.postGroupSize = postGroupSize; + this.programService = programService; } public List search(Function, Optional>>> searchMethod, @@ -366,4 +377,32 @@ public List post(List brapiObjects, return post(brapiObjects, null, postMethod, null); } + public void makeCall(Request brapiRequest) throws ApiException { + OkHttpClient client = new OkHttpClient.Builder() + .readTimeout(5, TimeUnit.MINUTES) + .build(); + try { + client.newCall(brapiRequest).execute(); + } catch (IOException e) { + log.error("Error calling BrAPI Service", e); + throw new ApiException("Error calling BrAPI Service"); + } + } + + public String getProgramBrAPIBaseUrl(UUID programId) { + ProgramBrAPIEndpoints programBrAPIEndpoints; + try { + programBrAPIEndpoints = programService.getBrapiEndpoints(programId); + } catch (DoesNotExistException e) { + throw new HttpStatusException(HttpStatus.NOT_FOUND, "Program does not exist"); + } + + if(programBrAPIEndpoints.getCoreUrl().isEmpty()) { + log.error("Program: " + programId + " is missing BrAPI URL config"); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ""); + } + var programBrAPIBaseUrl = programBrAPIEndpoints.getCoreUrl().get(); + programBrAPIBaseUrl = programBrAPIBaseUrl.endsWith("/") ? programBrAPIBaseUrl.substring(0, programBrAPIBaseUrl.length() - 1) : programBrAPIBaseUrl; + return programBrAPIBaseUrl.endsWith(BrapiVersion.BRAPI_V2) ? programBrAPIBaseUrl : programBrAPIBaseUrl + BrapiVersion.BRAPI_V2; + } } diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index e29ce7e24..84f7385b9 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -49,6 +49,7 @@ import java.time.OffsetDateTime; import java.util.*; +import static io.micronaut.http.HttpRequest.DELETE; import static io.micronaut.http.HttpRequest.GET; import static org.junit.jupiter.api.Assertions.*; @@ -187,4 +188,35 @@ public void getAllListsSuccess() { } } } + + + @Test + @SneakyThrows + public void deleteListSuccess() { + // A GET request to the brapi/v2/lists endpoint with no query params should return all lists. + Flowable> getCall = client.exchange( + GET(String.format("/programs/%s/brapi/v2/lists", program.getId().toString())) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + + // Ensure 200 OK response for fetching lists. + HttpResponse beforeResponse = getCall.blockingFirst(); + assertEquals(HttpStatus.OK, beforeResponse.getStatus()); + + // Parse result. + JsonObject beforeResult = JsonParser.parseString(beforeResponse.body()).getAsJsonObject().getAsJsonObject("result"); + JsonArray beforeData = beforeResult.getAsJsonArray("data"); + + // A DELETE request to the brapi/v2/lists/ endpoint with no query params should delete the list. + String deleteListDbId = beforeData.get(0).getAsJsonObject().get("listDbId").getAsString(); + Flowable> deleteCall = client.exchange( + DELETE(String.format("/programs/%s/brapi/v2/lists/%s", program.getId().toString(), deleteListDbId)) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + + // Ensure 204 NO_CONTENT response for deleting a list. + HttpResponse deleteResponse = deleteCall.blockingFirst(); + assertEquals(HttpStatus.NO_CONTENT, deleteResponse.getStatus()); + + } } diff --git a/src/test/java/org/breedinginsight/daos/BrAPIDAOUtilUnitTest.java b/src/test/java/org/breedinginsight/daos/BrAPIDAOUtilUnitTest.java index 0cb51387e..427ed6d88 100644 --- a/src/test/java/org/breedinginsight/daos/BrAPIDAOUtilUnitTest.java +++ b/src/test/java/org/breedinginsight/daos/BrAPIDAOUtilUnitTest.java @@ -1,6 +1,7 @@ package org.breedinginsight.daos; import com.google.gson.JsonObject; +import io.micronaut.test.annotation.MockBean; import lombok.SneakyThrows; import org.apache.commons.lang3.tuple.Pair; import org.brapi.client.v2.ApiResponse; @@ -11,6 +12,7 @@ import org.brapi.v2.model.germ.response.BrAPIGermplasmListResponseResult; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.model.Program; +import org.breedinginsight.services.ProgramService; import org.breedinginsight.utilities.BrAPIDAOUtil; import org.breedinginsight.utilities.Utilities; import org.junit.jupiter.api.BeforeEach; @@ -21,6 +23,8 @@ import java.time.temporal.ChronoUnit; import java.util.*; +import static org.mockito.Mockito.mock; + @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class BrAPIDAOUtilUnitTest { @@ -30,6 +34,10 @@ public class BrAPIDAOUtilUnitTest { private Program testProgram; private List paginatedGermplasm; private BrAPIGermplasmSearchRequest germplasmSearch; + @MockBean(ProgramService.class) + ProgramService programService() { + return mock(ProgramService.class); + } public Integer fetchPaginatedGermplasm(int page, int pageSize) { paginatedGermplasm = new ArrayList<>(); @@ -62,7 +70,7 @@ public ApiResponse, Optional Date: Mon, 2 Dec 2024 21:02:43 -0500 Subject: [PATCH 050/132] update ListControllerIntegrationTest --- .../brapi/v2/BrAPIListController.java | 7 +++---- .../brapi/v2/dao/BrAPIListDAO.java | 7 +++++-- .../brapi/v2/services/BrAPIListService.java | 7 +++++-- .../utilities/BrAPIDAOUtil.java | 21 +++++++++++++++---- .../v2/ListControllerIntegrationTest.java | 13 ++++++++++++ 5 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 3cb73dbae..3a2672252 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -93,7 +93,7 @@ public HttpResponse>> getLists( @Delete("/lists/{listDbId}") @Produces(MediaType.APPLICATION_JSON) @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) - public HttpResponse>> deleteListById( + public HttpResponse deleteListById( @PathVariable("programId") UUID programId, @PathVariable("listDbId") String listDbId, HttpRequest request @@ -104,11 +104,10 @@ public HttpResponse>> deleteListById( hardDelete = "true".equals(paramValue); } try { - brapiListService.deleteBrAPIList(listDbId, programId, hardDelete); - return HttpResponse.status(HttpStatus.NO_CONTENT); + return brapiListService.deleteBrAPIList(listDbId, programId, hardDelete); } catch (Exception e) { log.info(e.getMessage(), e); - return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving germplasm list records"); + return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error deleting germplasm list"); } } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index 11e079842..6a1784389 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -17,6 +17,7 @@ package org.breedinginsight.brapi.v2.dao; +import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; import io.micronaut.http.exceptions.HttpStatusException; import lombok.extern.slf4j.Slf4j; @@ -37,6 +38,8 @@ import org.brapi.v2.model.core.response.BrAPIListsListResponse; import org.brapi.v2.model.core.response.BrAPIListsListResponseResult; import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; +import org.breedinginsight.api.model.v1.response.DataResponse; +import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapps.importer.daos.ImportDAO; import org.breedinginsight.brapps.importer.model.ImportUpload; @@ -212,7 +215,7 @@ public List createBrAPILists(List brapiLi throw new ApiException("No response after creating list"); } - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + public HttpResponse deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { // TODO: Switch to using the ListsApi from the BrAPI client library once the delete endpoints are merged into it var programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(programId); var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); @@ -223,7 +226,7 @@ public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) .addHeader("Content-Type", "application/json") .build(); - brAPIDAOUtil.makeCall(brapiRequest); + return brAPIDAOUtil.makeCall(brapiRequest); } } diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java index 1ee77e310..65be8f5cd 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java @@ -1,6 +1,7 @@ package org.breedinginsight.brapi.v2.services; import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpResponse; import lombok.extern.slf4j.Slf4j; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.BrAPIExternalReference; @@ -8,6 +9,8 @@ import org.brapi.v2.model.core.BrAPIListTypes; import org.brapi.v2.model.core.request.BrAPIListSearchRequest; import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; +import org.breedinginsight.api.model.v1.response.DataResponse; +import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; @@ -90,7 +93,7 @@ public List getListSummariesByTypeAndXref( return programLists; } - public void deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { - listDAO.deleteBrAPIList(listDbId, programId, hardDelete); + public HttpResponse deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { + return listDAO.deleteBrAPIList(listDbId, programId, hardDelete); } } diff --git a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java index 075d6bb2b..15d98e1c3 100644 --- a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java +++ b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java @@ -18,6 +18,7 @@ package org.breedinginsight.utilities; import io.micronaut.context.annotation.Property; +import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; import io.micronaut.http.exceptions.HttpStatusException; import io.micronaut.http.server.exceptions.InternalServerException; @@ -25,11 +26,13 @@ import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Request; +import okhttp3.Response; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.brapi.client.v2.ApiResponse; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.*; +import org.breedinginsight.api.model.v1.response.DataResponse; import org.breedinginsight.brapi.v1.controller.BrapiVersion; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.model.ProgramBrAPIEndpoints; @@ -377,15 +380,25 @@ public List post(List brapiObjects, return post(brapiObjects, null, postMethod, null); } - public void makeCall(Request brapiRequest) throws ApiException { + public HttpResponse makeCall(Request brapiRequest) { + // Create OkHttpClient with timeout OkHttpClient client = new OkHttpClient.Builder() .readTimeout(5, TimeUnit.MINUTES) .build(); - try { - client.newCall(brapiRequest).execute(); + + try (Response brapiResponse = client.newCall(brapiRequest).execute()) { + int statusCode = brapiResponse.code(); + + if (!brapiResponse.isSuccessful()) { + return HttpResponse.status(HttpStatus.valueOf(statusCode)); + } + + String responseBody = brapiResponse.body() != null ? brapiResponse.body().string() : ""; + return HttpResponse.status(HttpStatus.valueOf(statusCode), responseBody); + } catch (IOException e) { log.error("Error calling BrAPI Service", e); - throw new ApiException("Error calling BrAPI Service"); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Error calling BrAPI Service"); } } diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index 84f7385b9..8aeea6a7c 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -23,6 +23,7 @@ import io.micronaut.http.HttpStatus; import io.micronaut.http.client.RxHttpClient; import io.micronaut.http.client.annotation.Client; +import io.micronaut.http.client.exceptions.HttpClientResponseException; import io.micronaut.http.netty.cookies.NettyCookie; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import io.reactivex.Flowable; @@ -191,6 +192,7 @@ public void getAllListsSuccess() { @Test + @Disabled("Delete-list tests are temporarily disabled until the delete endpoints are merged into the develop branch of breedinginsight:brapi-java-test-server") @SneakyThrows public void deleteListSuccess() { // A GET request to the brapi/v2/lists endpoint with no query params should return all lists. @@ -218,5 +220,16 @@ public void deleteListSuccess() { HttpResponse deleteResponse = deleteCall.blockingFirst(); assertEquals(HttpStatus.NO_CONTENT, deleteResponse.getStatus()); + + // A DELETE request to the brapi/v2/lists/ endpoint with invalid dbId. + Flowable> invalidDeleteCall = client.exchange( + DELETE(String.format("/programs/%s/brapi/v2/lists/%s", program.getId().toString(), "NOT-VALID-DBID")) + .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class + ); + + // Ensure 404 NOT_FOUND response for requesting to delete a non-existant list. + HttpResponse invalidDeleteResponse = invalidDeleteCall.blockingFirst(); + assertEquals(HttpStatus.NOT_FOUND, invalidDeleteResponse.getStatus()); + } } From 7fe03bf6f10b46b859974e52c14e47ddb4394348 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:18:37 -0500 Subject: [PATCH 051/132] update TODO comment --- .../java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java index 6a1784389..e73b6836a 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIListDAO.java @@ -216,7 +216,7 @@ public List createBrAPILists(List brapiLi } public HttpResponse deleteBrAPIList(String listDbId, UUID programId, boolean hardDelete) throws ApiException { - // TODO: Switch to using the ListsApi from the BrAPI client library once the delete endpoints are merged into it + // TODO: Switch to using the ListsApi from the BrAPI client library once the delete endpoints from BI-2397 are merged. var programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(programId); var requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/lists/" + listDbId).newBuilder(); requestUrl.addQueryParameter("hardDelete", Boolean.toString(hardDelete)); From 710e677da016c2b7a8d8b365f7ffe63cfb464906 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:30:28 -0500 Subject: [PATCH 052/132] incorporate PR feedback --- .../brapi/v2/BrAPIGermplasmController.java | 2 +- .../brapi/v2/BrAPIListController.java | 5 +++++ .../v2/model/request/query/GermplasmQuery.java | 2 +- .../utilities/response/ResponseUtils.java | 1 + .../v2/ListControllerIntegrationTest.java | 18 ++++++++++++++---- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java index 94cf9c9fc..84a6a17f0 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java @@ -210,7 +210,7 @@ public HttpResponse>>> getGermplasm( } // Fetch all germplasm in the program unless a list id is supplied to return only germplasm in that collection - List germplasm = queryParams.getList() == null ? germplasmService.getGermplasm(programId) : germplasmService.getGermplasmByList(programId, queryParams.getList());; + List germplasm = queryParams.getListDbId() == null ? germplasmService.getGermplasm(programId) : germplasmService.getGermplasmByList(programId, queryParams.getListDbId()); SearchRequest searchRequest = queryParams.constructSearchRequest(); return ResponseUtils.getBrapiQueryResponse(germplasm, germplasmQueryMapper, queryParams, searchRequest); } catch (ApiException e) { diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java index 3a2672252..10528e298 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIListController.java @@ -7,7 +7,9 @@ import io.micronaut.http.annotation.*; import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; +import io.reactivex.rxjava3.core.Single; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.core.BrAPIListSummary; import org.brapi.v2.model.core.BrAPIListTypes; @@ -25,11 +27,14 @@ import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.response.ResponseUtils; import org.breedinginsight.utilities.response.mappers.ListQueryMapper; +import reactor.core.publisher.Mono; +import reactor.util.function.Tuples; import javax.inject.Inject; import javax.validation.Valid; import javax.validation.Validator; import java.util.List; +import java.util.Optional; import java.util.UUID; @Slf4j diff --git a/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java b/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java index 92752fad6..81039b7ef 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java +++ b/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java @@ -28,7 +28,7 @@ public class GermplasmQuery extends BrapiQuery { private String dateDisplayFormat; // The list id used to get a collection of germplasm - private String list; + private String listDbId; public SearchRequest constructSearchRequest() { List filters = new ArrayList<>(); diff --git a/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java b/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java index 4cee82f99..cb8791fae 100644 --- a/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java +++ b/src/main/java/org/breedinginsight/utilities/response/ResponseUtils.java @@ -18,6 +18,7 @@ package org.breedinginsight.utilities.response; import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpResponseFactory; import io.micronaut.http.HttpStatus; import io.micronaut.http.MediaType; import io.micronaut.http.exceptions.HttpStatusException; diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index 8aeea6a7c..2d421d306 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -42,6 +42,7 @@ import org.breedinginsight.model.Program; import org.breedinginsight.services.SpeciesService; import org.jooq.DSLContext; +import org.junit.Rule; import org.junit.jupiter.api.*; import javax.inject.Inject; @@ -152,6 +153,7 @@ public void setup() { @Test @SneakyThrows + @Order(1) public void getAllListsSuccess() { // A GET request to the brapi/v2/lists endpoint with no query params should return all lists. Flowable> call = client.exchange( @@ -159,6 +161,12 @@ public void getAllListsSuccess() { .cookie(new NettyCookie("phylo-token", "test-registered-user")), String.class ); + // Collect all responses + List> responses = call.toList().blockingGet(); + + // Ensure we got a response + assertFalse(responses.isEmpty(), "No response received"); + // Ensure 200 OK response. HttpResponse response = call.blockingFirst(); assertEquals(HttpStatus.OK, response.getStatus()); @@ -192,8 +200,8 @@ public void getAllListsSuccess() { @Test - @Disabled("Delete-list tests are temporarily disabled until the delete endpoints are merged into the develop branch of breedinginsight:brapi-java-test-server") @SneakyThrows + @Order(2) public void deleteListSuccess() { // A GET request to the brapi/v2/lists endpoint with no query params should return all lists. Flowable> getCall = client.exchange( @@ -228,8 +236,10 @@ public void deleteListSuccess() { ); // Ensure 404 NOT_FOUND response for requesting to delete a non-existant list. - HttpResponse invalidDeleteResponse = invalidDeleteCall.blockingFirst(); - assertEquals(HttpStatus.NOT_FOUND, invalidDeleteResponse.getStatus()); - + try { + invalidDeleteCall.blockingFirst(); + } catch(HttpClientResponseException e) { + assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); + } } } From 3deb60bf2553c60e33b749eef0f2d91fe5d07ae5 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Mon, 23 Dec 2024 17:17:03 -0500 Subject: [PATCH 053/132] [BI-2424] - refactored getObservationUnitsByDbId --- .../entity/PendingObservationUnit.java | 6 +++--- .../service/ObservationUnitService.java | 20 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java index 6bb682ee2..f7c29264c 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java @@ -95,11 +95,11 @@ public List brapiPost(List members) */ @Override public List brapiRead() throws ApiException { - // Collect deltabreed-generated exp unit ids listed in the import - Set expUnitIds = cache.getReferenceOUIds(); + // Collect deltabreed-generated obs unit ids listed in the import + Set obsUnitIds = cache.getReferenceOUIds(); // For each id fetch the observation unit from the brapi data store - return observationUnitService.getObservationUnitsByDbId(new HashSet<>(expUnitIds), importContext.getProgram()); + return observationUnitService.getObservationUnitsByDbId(new HashSet<>(obsUnitIds), importContext.getProgram()); } /** diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java index e363f9d46..720c93d25 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java @@ -49,27 +49,27 @@ public ObservationUnitService(BrAPIObservationUnitDAO brAPIObservationUnitDAO) { } /** - * Retrieves a list of BrAPI (Breeding API) observation units by their database IDs for a given set of experimental unit IDs and program. + * Retrieves a list of BrAPI (Breeding API) observation units by their database IDs for a given set of observation unit IDs and program. * - * This method queries the BrAPIObservationUnitDAO to retrieve BrAPI observation units based on the provided experimental unit IDs and program. - * If the database IDs of the retrieved BrAPI observation units do not match the provided experimental unit IDs, an IllegalStateException is thrown. + * This method queries the BrAPIObservationUnitDAO to retrieve BrAPI observation units based on the provided observation unit IDs and program. + * If the database IDs of the retrieved BrAPI observation units do not match the provided observation unit IDs, an IllegalStateException is thrown. * The exception includes information on the missing observation unit database IDs. * - * @param expUnitIds a set of experimental unit IDs for which to retrieve BrAPI observation units + * @param obsUnitIds a set of observation unit IDs for which to retrieve BrAPI observation units * @param program the program for which to retrieve BrAPI observation units - * @return a list of BrAPIObservationUnit objects corresponding to the provided experimental unit IDs + * @return a list of BrAPIObservationUnit objects corresponding to the provided observation unit IDs * @throws ApiException if an error occurs during the retrieval of observation units - * @throws IllegalStateException if the retrieved observation units do not match the provided experimental unit IDs + * @throws IllegalStateException if the retrieved observation units do not match the provided observation unit IDs */ - public List getObservationUnitsByDbId(Set expUnitIds, Program program) throws ApiException, IllegalStateException { + public List getObservationUnitsByDbId(Set obsUnitIds, Program program) throws ApiException, IllegalStateException { List brapiUnits = null; // Retrieve reference Observation Units based on IDs - brapiUnits = brAPIObservationUnitDAO.getObservationUnitsById(expUnitIds, program); + brapiUnits = brAPIObservationUnitDAO.getObservationUnitsById(obsUnitIds, program); // If no BrAPI units are found, throw an IllegalStateException with an error message - if (expUnitIds.size() != brapiUnits.size()) { - Set missingIds = new HashSet<>(expUnitIds); + if (obsUnitIds.size() != brapiUnits.size()) { + Set missingIds = new HashSet<>(obsUnitIds); // Calculate missing IDs based on retrieved BrAPI units missingIds.removeAll(brapiUnits.stream().map(BrAPIObservationUnit::getObservationUnitDbId).collect(Collectors.toSet())); From 90fcfa00a8fd43b38749c4a4fa158dbd6e33b463 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:22:20 -0500 Subject: [PATCH 054/132] Delete sample submission initial work --- .../geno/SampleSubmissionController.java | 39 +++++++++ .../brapps/importer/daos/BrAPISampleDAO.java | 85 ++++++++++++++++++- .../brapps/importer/daos/logo.svg | 0 .../model/SampleSubmission.java | 10 +++ .../services/SampleSubmissionService.java | 30 +++++++ .../utilities/BrAPIDAOUtil.java | 23 +++++ 6 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg diff --git a/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java b/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java index e99bb5e70..d859a7cbf 100644 --- a/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java @@ -281,4 +281,43 @@ public HttpResponse> checkVendorStatus(@PathVariable return HttpResponse.serverError(); } } + + /** + * Removes + * @param programId + * @param submissionId + * @return + * @throws ApiException + */ + @Delete("programs/{programId}/submissions/{submissionId}") + @Produces(MediaType.APPLICATION_JSON) + // only sys admin allowed on post & put so kept same permissions for delete + @ProgramSecured(roles = {ProgramSecuredRole.SYSTEM_ADMIN}) + public HttpResponse deleteSubmissionById(@PathVariable UUID programId, @PathVariable UUID submissionId) throws ApiException { + + // program validation + Optional program = programService.getById(programId); + if(program.isEmpty()) { + log.info(String.format("programId not found: %s", programId.toString())); + return HttpResponse.notFound(); + } + + // sample status validation + Optional submissionOpt = sampleSubmissionService.getSampleSubmission(program.get(), submissionId, false); + + if(submissionOpt.isEmpty()) { + return HttpResponse.notFound(); + } + SampleSubmission submission = submissionOpt.get(); + // if submission has status of submitted do not allow deletion + // if the user changes the status to not submitted then they can delete + if (!submission.isDeletable()) { + return HttpResponse.notAllowed(); + } + + sampleSubmissionService.deleteSampleSubmission(program.get(), submissionId); + + return HttpResponse.ok(); + } + } diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java index 1cb9a531a..8bb0993c2 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java +++ b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java @@ -17,8 +17,14 @@ package org.breedinginsight.brapps.importer.daos; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import io.micronaut.context.annotation.Property; import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.brapi.client.v2.JSON; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.modules.genotype.SamplesApi; import org.brapi.v2.model.geno.BrAPISample; @@ -33,9 +39,9 @@ import javax.inject.Inject; import javax.inject.Singleton; +import java.io.IOException; import java.util.Collections; import java.util.List; -import java.util.UUID; @Slf4j @Singleton @@ -47,6 +53,7 @@ public class BrAPISampleDAO { private final ImportDAO importDAO; private final BrAPIDAOUtil brAPIDAOUtil; private final BrAPIEndpointProvider brAPIEndpointProvider; + private final Gson gson = new JSON().getGson(); @Inject public BrAPISampleDAO(ProgramDAO programDAO, @@ -102,4 +109,80 @@ public List readSamplesBySubmissionIds(Program program, List sampleDbIds) throws ApiException { + // create batch of samples, not yet included in brapi client TODO: switch to brapi client when available + String programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(program.getId()); + String batchDbId = postSamplesBatch(programBrAPIBaseUrl, sampleDbIds); + + // delete samples specified in batch + deleteSamplesBatch(programBrAPIBaseUrl, batchDbId); + + // TODO: delete plates associated with submission, could potentially only require brapi server side change if deleting a sample cascades + } + + private String postSamplesBatch(String programBrAPIBaseUrl, List sampleDbIds) throws ApiException { + HttpUrl.Builder requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/batchDeletes").newBuilder(); + //requestUrl.addQueryParameter("hardDelete", Boolean.toString(hard)); + + BatchDeleteRequest requestBody = new BatchDeleteRequest(sampleDbIds); + String json = gson.toJson(requestBody); + RequestBody body = RequestBody.create(json, MediaType.get("application/json")); + + HttpUrl url = requestUrl.build(); + Request brapiRequest = new Request.Builder() + .url(url) + .post(body) + .addHeader("Content-Type", "application/json") + .build(); + + String jsonResponse = brAPIDAOUtil.makeCallWithResponse(brapiRequest); + JsonElement rootElement = JsonParser.parseString(jsonResponse); + JsonObject rootObject = rootElement.getAsJsonObject(); + JsonObject resultObject = rootObject.getAsJsonObject("result"); + String batchDeleteDbId = resultObject.get("batchDeleteDbId").getAsString(); + return batchDeleteDbId; + } + + private void deleteSamplesBatch(String programBrAPIBaseUrl, String batchDbId) throws ApiException { + HttpUrl.Builder requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/batchDeletes/" + batchDbId).newBuilder(); + requestUrl.addQueryParameter("hardDelete", "true"); + + HttpUrl url = requestUrl.build(); + Request brapiRequest = new Request.Builder() + .url(url) + .method("DELETE", null) + .addHeader("Content-Type", "application/json") + .build(); + + brAPIDAOUtil.makeCall(brapiRequest); + } + + /** + * TODO: temporary minimal model here until brapi client is updated with delete models + */ + public class BatchDeleteRequest { + private String batchDeleteType; + private Search search; + + public BatchDeleteRequest(List sampleDbIds) { + this.batchDeleteType = "samples"; + this.search = new Search(sampleDbIds); + } + + private class Search { + private List sampleDbIds; + + public Search(List sampleDbIds) { + this.sampleDbIds = sampleDbIds; + } + } + } + } diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg b/src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg new file mode 100644 index 000000000..e69de29bb diff --git a/src/main/java/org/breedinginsight/model/SampleSubmission.java b/src/main/java/org/breedinginsight/model/SampleSubmission.java index 6202a5a6f..020f66352 100644 --- a/src/main/java/org/breedinginsight/model/SampleSubmission.java +++ b/src/main/java/org/breedinginsight/model/SampleSubmission.java @@ -17,6 +17,7 @@ package org.breedinginsight.model; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -79,6 +80,15 @@ private void parseShipmentForms(JSONB shipmentforms) { } } + /** + * Should only be deleted when status is not submitted and has no vendor status + */ + @JsonIgnore + public boolean isDeletable() { + return (this.getSubmitted() == null || (this.getSubmitted() != null && !this.getSubmitted())) + && this.getVendorStatus() == null; + } + public enum Status { NOT_SUBMITTED("NOT SUBMITTED"), SUBMITTED("SUBMITTED"), diff --git a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java index 2c629da55..134f690dd 100644 --- a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java +++ b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java @@ -427,4 +427,34 @@ public Optional updateSubmissionStatus(Program program, UUID s return submissionOptional; } + + /** + * Deletes BrAPI plates and submission objects as well as sample submission record in bidb + * We do not currently cache plates or samples so don't need to worry about that + * @param submissionId sample submission UUID to delete + * @exception ApiException if a BrAPI call fails + */ + public void deleteSampleSubmission(Program program, UUID submissionId) throws ApiException { + + // delete BrAPI data + // delete samples + // delete plates + + // create a batch of sampleIds to delete + + // get samples with the samble submission xref + List samples = sampleDAO.readSamplesBySubmissionIds(program, List.of(submissionId.toString())); + + // extract sampleDbIds to include in batch + List sampleDbIds = samples.stream().map(BrAPISample::getSampleDbId).collect(Collectors.toList()); + + // create batch of samples, not yet included in brapi client TODO: switch to brapi client when available + sampleDAO.deleteSamples(program, sampleDbIds); + + // TODO: delete plates, not yet supported in brapi server + + // delete sample submission record from bidb + submissionDAO.deleteById(submissionId); + } + } diff --git a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java index 075d6bb2b..c3882457b 100644 --- a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java +++ b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java @@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Request; +import okhttp3.Response; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.brapi.client.v2.ApiResponse; @@ -389,6 +390,28 @@ public void makeCall(Request brapiRequest) throws ApiException { } } + /** + * TODO: replace with brapi client methods when available, will do timeout spec from config at that point + * @param brapiRequest + * @return + * @throws ApiException + */ + public String makeCallWithResponse(Request brapiRequest) throws ApiException { + OkHttpClient client = new OkHttpClient.Builder() + .readTimeout(5, TimeUnit.MINUTES) + .build(); + + // autoclose Response + try (Response response = client.newCall(brapiRequest).execute()) { + if (!response.isSuccessful()) { + throw new ApiException("Request failed with status code: " + response.code()); + } + return response.body().string(); + } catch (IOException e) { + throw new ApiException(e); + } + } + public String getProgramBrAPIBaseUrl(UUID programId) { ProgramBrAPIEndpoints programBrAPIEndpoints; try { From fe5f667810c23f1608f0ad7fc0c24171ac165439 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Fri, 10 Jan 2025 15:12:51 -0500 Subject: [PATCH 055/132] fix germplasm integration test --- .../brapi/v2/GermplasmControllerIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java index 7c80482d9..32781dd8c 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java @@ -245,7 +245,7 @@ public void getAllGermplasmByListSuccess() { String germplasmListDbId = fetchGermplasmListDbId(programId); // Build the endpoint to get germplasm by germplasm list. - String endpoint = String.format("/programs/%s/brapi/v2/germplasm?list=%s", programId, germplasmListDbId); + String endpoint = String.format("/programs/%s/brapi/v2/germplasm?listDbId=%s", programId, germplasmListDbId); // Get germplasm by list. Flowable> call = client.exchange( From bd6d39391ea0a91020d36d7f24db33993423a6f5 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Fri, 10 Jan 2025 20:57:51 +0000 Subject: [PATCH 056/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 07307c36c..99a14d407 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+895 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/8e7ec0c2ef021f8f9a5e5e6ead6f666e25a51cdb +version=v1.1.0+897 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/7fabdbfb54bb9e31e93721bae7ca556555435122 From f874c82672c32eebaa6b47e12e847bce00114cdd Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Mon, 13 Jan 2025 16:00:19 +0000 Subject: [PATCH 057/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 99a14d407..4e43777d2 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+897 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/7fabdbfb54bb9e31e93721bae7ca556555435122 +version=v1.1.0+899 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/1d19a829223e99b09324a69b51fa47c6b965de64 From 2a050531cc4cacc7f7471074312e024f9504192b Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 14 Jan 2025 12:26:48 -0500 Subject: [PATCH 058/132] [BI-2376] - cleaned up comment --- .../java/org/breedinginsight/brapi/v2/dao/BrAPICachedDAO.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPICachedDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPICachedDAO.java index fb35bd448..2d623b02b 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPICachedDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPICachedDAO.java @@ -9,7 +9,6 @@ public abstract class BrAPICachedDAO { protected ProgramCache programCache; public void repopulateCache(UUID programId) { - // TODO: test calling populate alone (without invalidate first). this.programCache.invalidate(programId); this.programCache.populate(programId); } From 6ee7e4f9e51fde2af88a3f708ce04db1080b7040 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:07:58 -0500 Subject: [PATCH 059/132] [BI-2376] - changes based on review --- .../breedinginsight/brapi/v2/services/BrAPITrialService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9aec86320..6b8a3f233 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java @@ -688,7 +688,7 @@ public int deleteExperiment(Program program, UUID experimentId, boolean hard) th // TODO: replace with a single call to a batch delete method if that becomes available. // Iterate over lists, delete each by listDbId. for (BrAPIListSummary list : lists) { - listDAO.deleteBrAPIList(list.getListDbId(), program.getId(), hard); // TODO: not yet implemented. + listDAO.deleteBrAPIList(list.getListDbId(), program.getId(), hard); } // TODO: if performance is poor, implement more precise invalidation, possibly using hierarchical cache keys. // Invalidate and repopulate cache for Trial, Study, Observation, ObservationUnit. From 3ff0eb4655668b967854ac2a795c1b8998fabd3d Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Wed, 15 Jan 2025 16:05:57 +0000 Subject: [PATCH 060/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 4e43777d2..4ef6916c8 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+899 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/1d19a829223e99b09324a69b51fa47c6b965de64 +version=v1.1.0+901 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/9c0fdb5b160215a40e5f2df57a7e922ec0036052 From b58768c7fb8cbdf8e5e4112bf25b1c53ac0f51bb Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:16:50 -0500 Subject: [PATCH 061/132] Cleanup --- src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg | 0 .../breedinginsight/brapi/v2/ListControllerIntegrationTest.java | 1 - 2 files changed, 1 deletion(-) delete mode 100644 src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg b/src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index 025179f2e..b0a55b3b1 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -240,6 +240,5 @@ public void deleteListSuccess() { } catch(HttpClientResponseException e) { assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); } - } } From 8de90d9ae50e3cc6a5e705b824c5001f2a5f2236 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:19:28 -0500 Subject: [PATCH 062/132] Removed added spacing --- .../breedinginsight/brapi/v2/ListControllerIntegrationTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index b0a55b3b1..029c16594 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -228,6 +228,7 @@ public void deleteListSuccess() { HttpResponse deleteResponse = deleteCall.blockingFirst(); assertEquals(HttpStatus.NO_CONTENT, deleteResponse.getStatus()); + // A DELETE request to the brapi/v2/lists/ endpoint with invalid dbId. Flowable> invalidDeleteCall = client.exchange( DELETE(String.format("/programs/%s/brapi/v2/lists/%s", program.getId().toString(), "NOT-VALID-DBID")) From 4ead129fdb504517449784eea502ca2b433cced2 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:28:13 -0500 Subject: [PATCH 063/132] Cleanup comments --- .../v1/controller/geno/SampleSubmissionController.java | 8 +++++--- .../brapi/v2/ListControllerIntegrationTest.java | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java b/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java index d859a7cbf..be6377870 100644 --- a/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java @@ -283,9 +283,11 @@ public HttpResponse> checkVendorStatus(@PathVariable } /** - * Removes - * @param programId - * @param submissionId + * Delete sample submission. + * Currently deletes the bidb submission record and BrAPI samples, TODO: delete BrAPI plates once supported + * in BrAPI server, see BI-2431 + * @param programId bi-api id of program + * @param submissionId bi-api id of submission * @return * @throws ApiException */ diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index 029c16594..2d421d306 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -228,7 +228,7 @@ public void deleteListSuccess() { HttpResponse deleteResponse = deleteCall.blockingFirst(); assertEquals(HttpStatus.NO_CONTENT, deleteResponse.getStatus()); - + // A DELETE request to the brapi/v2/lists/ endpoint with invalid dbId. Flowable> invalidDeleteCall = client.exchange( DELETE(String.format("/programs/%s/brapi/v2/lists/%s", program.getId().toString(), "NOT-VALID-DBID")) From 110bdaa8ce8d704b194e7f0cb445f8a959675d04 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Mon, 27 Jan 2025 20:42:04 +0000 Subject: [PATCH 064/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 4e43777d2..4ef6916c8 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+899 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/1d19a829223e99b09324a69b51fa47c6b965de64 +version=v1.1.0+901 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/9c0fdb5b160215a40e5f2df57a7e922ec0036052 From bc2b09cc5c4030c88917ddee4b0387ce8c26221b Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:17:03 -0500 Subject: [PATCH 065/132] Handle non-immediate response --- .../brapps/importer/daos/BrAPISampleDAO.java | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java index 8bb0993c2..fe4ad57c2 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java +++ b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java @@ -22,6 +22,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import io.micronaut.context.annotation.Property; +import io.micronaut.http.server.exceptions.InternalServerException; import lombok.extern.slf4j.Slf4j; import okhttp3.*; import org.brapi.client.v2.JSON; @@ -146,8 +147,35 @@ private String postSamplesBatch(String programBrAPIBaseUrl, List sampleD JsonElement rootElement = JsonParser.parseString(jsonResponse); JsonObject rootObject = rootElement.getAsJsonObject(); JsonObject resultObject = rootObject.getAsJsonObject("result"); - String batchDeleteDbId = resultObject.get("batchDeleteDbId").getAsString(); - return batchDeleteDbId; + + // check to see if immediate response or searchResultId + if(resultObject.has("batchDeleteDbId")) { + return resultObject.get("batchDeleteDbId").getAsString(); + } else if (resultObject.has("searchResultsDbId")) { + // TODO: once api stuff is in client use BrAPIDAOUtil::search to handle retries, for now just request once + // could maybe be an issue for large number of samples + return getBatchDeleteDbIdFromSearchResult(programBrAPIBaseUrl, resultObject.get("searchResultsDbId").getAsString()); + } else { + throw new InternalServerException("Expected batchDeleteDbId or searchResultsDbId but got " + resultObject); + } + + } + + private String getBatchDeleteDbIdFromSearchResult(String programBrAPIBaseUrl, String searchResultDbId) throws ApiException { + HttpUrl.Builder requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/search/batchDeletes/" + searchResultDbId).newBuilder(); + + HttpUrl url = requestUrl.build(); + Request brapiRequest = new Request.Builder() + .url(url) + .method("GET", null) + .addHeader("Content-Type", "application/json") + .build(); + + String jsonResponse = brAPIDAOUtil.makeCallWithResponse(brapiRequest); + JsonElement rootElement = JsonParser.parseString(jsonResponse); + JsonObject rootObject = rootElement.getAsJsonObject(); + JsonObject resultObject = rootObject.getAsJsonObject("result"); + return resultObject.get("batchDeleteDbId").getAsString(); } private void deleteSamplesBatch(String programBrAPIBaseUrl, String batchDbId) throws ApiException { From 4855dca493ab50c0aeae1345789cb3c916dc76c0 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Wed, 29 Jan 2025 14:26:44 -0500 Subject: [PATCH 066/132] [BI-2156-qa] - fixed ObsUnitIds not found error handling --- .../controllers/ImportController.java | 2 +- .../model/imports/BrAPIImportService.java | 3 +- .../model/imports/DomainImportService.java | 3 +- .../importer/model/workflow/Workflow.java | 5 ++- .../importer/services/FileImportService.java | 4 +- .../experiment/ExperimentUtilities.java | 2 +- .../ExperimentWorkflowNavigator.java | 41 +++++++++++++------ .../AppendOverwritePhenotypesWorkflow.java | 4 +- .../action/WorkflowReadInitialization.java | 5 ++- .../entity/ExperimentImportEntity.java | 2 +- .../entity/PendingObservationUnit.java | 2 +- .../AppendOverwriteIDValidation.java | 3 +- .../middleware/commit/BrAPICommit.java | 3 +- .../middleware/commit/BrAPIDatasetCommit.java | 2 +- .../commit/BrAPIObservationCommit.java | 2 +- .../commit/BrAPIObservationUnitCommit.java | 2 +- .../middleware/commit/BrAPIStudyCommit.java | 2 +- .../middleware/commit/BrAPITrialCommit.java | 2 +- .../middleware/commit/LocationCommit.java | 2 +- .../initialize/WorkflowInitialization.java | 5 ++- .../appendoverwrite/model/Middleware.java | 6 ++- .../service/ObservationUnitService.java | 5 ++- 22 files changed, 69 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapps/importer/controllers/ImportController.java b/src/main/java/org/breedinginsight/brapps/importer/controllers/ImportController.java index d732da0c5..c78921693 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/controllers/ImportController.java +++ b/src/main/java/org/breedinginsight/brapps/importer/controllers/ImportController.java @@ -211,7 +211,7 @@ public HttpResponse>> getSystemMappings(@Nu @Produces(MediaType.APPLICATION_JSON) @AddMetadata @Secured(SecurityRule.IS_ANONYMOUS) - public HttpResponse>> getWorkflowsForSystemMapping(@PathVariable UUID mappingId) { + public HttpResponse>> getWorkflowsForSystemMapping(@PathVariable UUID mappingId) throws UnprocessableEntityException { List workflows = null; try { diff --git a/src/main/java/org/breedinginsight/brapps/importer/model/imports/BrAPIImportService.java b/src/main/java/org/breedinginsight/brapps/importer/model/imports/BrAPIImportService.java index 3f25d99e6..606901c4f 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/model/imports/BrAPIImportService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/model/imports/BrAPIImportService.java @@ -19,13 +19,14 @@ import org.breedinginsight.brapps.importer.model.response.ImportPreviewResponse; import org.breedinginsight.brapps.importer.model.workflow.ImportWorkflow; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; import java.util.List; public interface BrAPIImportService { String getImportTypeId(); BrAPIImport getImportClass(); - List getWorkflows(); + List getWorkflows() throws UnprocessableEntityException; default String getInvalidIntegerMsg(String columnName) { return String.format("Column name \"%s\" must be integer type, but non-integer type provided.", columnName); } diff --git a/src/main/java/org/breedinginsight/brapps/importer/model/imports/DomainImportService.java b/src/main/java/org/breedinginsight/brapps/importer/model/imports/DomainImportService.java index 5f96ae904..64f84a378 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/model/imports/DomainImportService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/model/imports/DomainImportService.java @@ -22,6 +22,7 @@ import org.breedinginsight.brapps.importer.model.workflow.ImportWorkflow; import org.breedinginsight.brapps.importer.model.workflow.ImportWorkflowResult; import org.breedinginsight.brapps.importer.model.workflow.Workflow; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; import javax.inject.Singleton; import java.util.List; @@ -43,7 +44,7 @@ public String getMissingColumnMsg(String columnName) { return "Column heading does not match template or ontology"; } @Override - public List getWorkflows() { + public List getWorkflows() throws UnprocessableEntityException { return workflowNavigator.getWorkflows(); } diff --git a/src/main/java/org/breedinginsight/brapps/importer/model/workflow/Workflow.java b/src/main/java/org/breedinginsight/brapps/importer/model/workflow/Workflow.java index 7504d7ab0..f4e37648e 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/model/workflow/Workflow.java +++ b/src/main/java/org/breedinginsight/brapps/importer/model/workflow/Workflow.java @@ -19,6 +19,7 @@ import io.micronaut.core.order.Ordered; import org.breedinginsight.brapps.importer.model.imports.ImportServiceContext; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; import java.util.ArrayList; import java.util.List; @@ -38,7 +39,7 @@ public interface Workflow extends Ordered { * @param context the ImportServiceContext object containing necessary information for the workflow * @return an Optional of ImportWorkflowResult representing the result of the workflow execution */ - Optional process(ImportServiceContext context); + Optional process(ImportServiceContext context) throws UnprocessableEntityException; /** * Default method to get a list of workflows. @@ -46,7 +47,7 @@ public interface Workflow extends Ordered { * * @return a List of ImportWorkflow containing workflows */ - default List getWorkflows() { + default List getWorkflows() throws UnprocessableEntityException { // Default implementation for getWorkflows method return new ArrayList<>(); } diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/FileImportService.java b/src/main/java/org/breedinginsight/brapps/importer/services/FileImportService.java index 3c3c6a093..1b1033e04 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/FileImportService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/FileImportService.java @@ -449,7 +449,7 @@ private void processFile(String workflowId, List finalBrAPIImportLi progress.setMessage(e.getMessage()); progress.setUpdatedBy(actingUser.getId()); importDAO.update(upload); - }catch (ValidatorException e) { + } catch (ValidatorException e) { log.info("Validation errors: \n" + e); ImportProgress progress = upload.getProgress(); progress.setStatuscode((short) HttpStatus.UNPROCESSABLE_ENTITY.getCode()); @@ -563,7 +563,7 @@ public List getSystemMappingByName(String name) { * @return A list of ImportWorkflow objects representing the workflows for the specified system mapping * @throws DoesNotExistException If the system mapping with the given ID does not exist */ - public List getWorkflowsForSystemMapping(UUID mappingId) throws DoesNotExistException { + public List getWorkflowsForSystemMapping(UUID mappingId) throws DoesNotExistException, UnprocessableEntityException { // Retrieve the import mapping configuration based on the provided mapping ID ImportMapping mappingConfig = importMappingDAO.getMapping(mappingId) diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentUtilities.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentUtilities.java index ae015aab6..d63234265 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentUtilities.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentUtilities.java @@ -70,7 +70,7 @@ public class ExperimentUtilities { public static final String PREEXISTING_EXPERIMENT_TITLE = "Experiment Title already exists"; public static final String MISSING_OBS_UNIT_ID_ERROR = "Experimental entities are missing ObsUnitIDs"; public static final String UNMATCHED_COLUMN = "Ontology term(s) not found: "; - + public static final String INVALID_OBS_UNIT_ID_ERROR = "ObsUnitID(s) not found: "; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentWorkflowNavigator.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentWorkflowNavigator.java index 8de6a7d8b..c42b86696 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentWorkflowNavigator.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentWorkflowNavigator.java @@ -22,8 +22,10 @@ import org.breedinginsight.brapps.importer.model.workflow.ImportWorkflow; import org.breedinginsight.brapps.importer.model.workflow.ExperimentWorkflow; import org.breedinginsight.brapps.importer.model.workflow.ImportWorkflowResult; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; import javax.inject.Singleton; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -52,15 +54,20 @@ public ExperimentWorkflowNavigator(List workflows) { * @return An Optional containing the first non-empty ImportWorkflowResult from the executed workflows, or an empty Optional if no non-empty result is found */ @Override - public Optional process(ImportServiceContext context) { + public Optional process(ImportServiceContext context) throws UnprocessableEntityException { /** * Have each workflow in order process the context, returning the first non-empty result */ - return workflows.stream() - .map(workflow->workflow.process(context)) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst(); + Optional first = Optional.empty(); + for (ExperimentWorkflow workflow : workflows) { + Optional process = workflow.process(context); + if (process.isPresent()) { + ImportWorkflowResult importWorkflowResult = process.get(); + first = Optional.of(importWorkflowResult); + break; + } + } + return first; } /** @@ -68,13 +75,21 @@ public Optional process(ImportServiceContext context) { * * @return List of ImportWorkflow objects with workflow metadata */ - public List getWorkflows() { - List workflowSummaryList = workflows.stream() - .map(workflow -> workflow.process(null)) // Process each workflow with a null context - .filter(Optional::isPresent) // Filter out any workflows that do not return a result - .map(Optional::get) // Extract the result from Optional - .map(ImportWorkflowResult::getWorkflow) // Retrieve the workflow metadata - .collect(Collectors.toList()); // Collect the workflow metadata into a list + public List getWorkflows() throws UnprocessableEntityException { + // Process each workflow with a null context + // Filter out any workflows that do not return a result + // Extract the result from Optional + // Retrieve the workflow metadata + // Collect the workflow metadata into a list + List workflowSummaryList = new ArrayList<>(); + for (ExperimentWorkflow workflow : workflows) { + Optional process = workflow.process(null); + if (process.isPresent()) { + ImportWorkflowResult importWorkflowResult = process.get(); + ImportWorkflow importWorkflowResultWorkflow = importWorkflowResult.getWorkflow(); + workflowSummaryList.add(importWorkflowResultWorkflow); + } + } // Set the order field for each workflow based on its position in the list for (int i = 0; i < workflowSummaryList.size(); i++) { diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/AppendOverwritePhenotypesWorkflow.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/AppendOverwritePhenotypesWorkflow.java index fbbc0afbd..accd83c29 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/AppendOverwritePhenotypesWorkflow.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/AppendOverwritePhenotypesWorkflow.java @@ -30,12 +30,14 @@ import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.middleware.AppendOverwriteIDValidation; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.middleware.commit.BrAPICommit; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.middleware.initialize.WorkflowInitialization; +import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.middleware.process.AppendStatistic; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.middleware.process.ImportTableProcess; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddleware; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddlewareContext; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteWorkflowContext; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.MiddlewareException; import org.breedinginsight.brapps.importer.services.processors.experiment.model.ImportContext; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; import javax.inject.Inject; import javax.inject.Singleton; @@ -76,7 +78,7 @@ public AppendOverwritePhenotypesWorkflow(AppendOverwriteIDValidation expUnitIDVa * @return Optional containing ImportWorkflowResult with workflow metadata and import preview response if successful, else empty Optional. */ @Override - public Optional process(ImportServiceContext context) { + public Optional process(ImportServiceContext context) throws UnprocessableEntityException { // Metadata about this workflow processing the context ImportWorkflow workflow = ImportWorkflow.builder() diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/WorkflowReadInitialization.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/WorkflowReadInitialization.java index 717a78f97..5cdddaaf5 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/WorkflowReadInitialization.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/WorkflowReadInitialization.java @@ -23,6 +23,7 @@ import org.brapi.client.v2.model.exceptions.ApiException; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.factory.BrAPIState; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.factory.entity.ExperimentImportEntity; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; import org.breedinginsight.utilities.Utilities; import java.util.List; @@ -44,7 +45,7 @@ protected WorkflowReadInitialization(ExperimentImportEntity entity) { * @return an Optional containing the BrAPIState representing the completed read workflow * @throws ApiException if an error occurs during execution */ - public Optional> execute() throws ApiException { + public Optional> execute() throws ApiException, UnprocessableEntityException { try { List fetchedMembers = entity.brapiRead(); entity.initializeWorkflow(fetchedMembers); @@ -52,6 +53,8 @@ public Optional> execute() throws ApiException { } catch(ApiException e) { log.error(String.format("Error fetching %s: %s", entity.getClass().getName(), Utilities.generateApiExceptionLogMessage(e)), e); throw new ApiException(e); + } catch (UnprocessableEntityException e) { + throw e; } } diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/ExperimentImportEntity.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/ExperimentImportEntity.java index c98d9cf83..3ca5fa004 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/ExperimentImportEntity.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/ExperimentImportEntity.java @@ -43,7 +43,7 @@ public interface ExperimentImportEntity { * @return List of fetched entities * @throws ApiException if there is an issue with the API call */ - public List brapiRead() throws ApiException; + public List brapiRead() throws ApiException, UnprocessableEntityException; /** * Commit objects changed by the workflow to the BrAPI service. diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java index f7c29264c..af5d3b7e2 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java @@ -94,7 +94,7 @@ public List brapiPost(List members) * @throws ApiException if there is an issue with the API call */ @Override - public List brapiRead() throws ApiException { + public List brapiRead() throws ApiException, UnprocessableEntityException { // Collect deltabreed-generated obs unit ids listed in the import Set obsUnitIds = cache.getReferenceOUIds(); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java index 64f471b88..1068b4119 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java @@ -24,12 +24,13 @@ import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddlewareContext; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddleware; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.MiddlewareException; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; @Slf4j @Prototype public class AppendOverwriteIDValidation extends AppendOverwriteMiddleware { @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { try { context.getAppendOverwriteWorkflowContext().setReferenceOUIds(ExperimentUtilities.collateReferenceOUIds(context)); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPICommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPICommit.java index 1b6cbd7ee..48b6b3c66 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPICommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPICommit.java @@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddleware; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddlewareContext; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; import javax.inject.Inject; @@ -48,7 +49,7 @@ public BrAPICommit(BrAPIDatasetCommit brAPIDatasetCommit, } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { log.debug("starting post of experiment data to BrAPI server"); return this.middleware.process(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIDatasetCommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIDatasetCommit.java index 368673f19..a9c7378f4 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIDatasetCommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIDatasetCommit.java @@ -50,7 +50,7 @@ public BrAPIDatasetCommit(BrAPICreationFactory brAPICreationFactory, BrAPIUpdate this.brAPIUpdateFactory = brAPIUpdateFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { try { WorkflowCreation datasetCreation = brAPICreationFactory.datasetWorkflowCreationBean(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationCommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationCommit.java index 779d75493..828efe0f8 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationCommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationCommit.java @@ -51,7 +51,7 @@ public BrAPIObservationCommit(BrAPICreationFactory brAPICreationFactory, BrAPIUp this.brAPIUpdateFactory = brAPIUpdateFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { try { WorkflowCreation brAPIObservationCreation = brAPICreationFactory.observationWorkflowCreationBean(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationUnitCommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationUnitCommit.java index 4610be387..0e5c674ad 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationUnitCommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationUnitCommit.java @@ -44,7 +44,7 @@ public BrAPIObservationUnitCommit(BrAPICreationFactory brAPICreationFactory) { this.brAPICreationFactory = brAPICreationFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { try{ WorkflowCreation brAPIObservationUnitCreation = brAPICreationFactory.observationUnitWorkflowCreationBean(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIStudyCommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIStudyCommit.java index f10a0b8b6..6823a8abc 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIStudyCommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIStudyCommit.java @@ -44,7 +44,7 @@ public BrAPIStudyCommit(BrAPICreationFactory brAPICreationFactory) { this.brAPICreationFactory = brAPICreationFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { try { WorkflowCreation brAPIStudyCreation = brAPICreationFactory.studyWorkflowCreationBean(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPITrialCommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPITrialCommit.java index 3ca112b21..59de45c8b 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPITrialCommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPITrialCommit.java @@ -50,7 +50,7 @@ public BrAPITrialCommit(BrAPICreationFactory brAPICreationFactory, BrAPIUpdateFa this.brAPIUpdateFactory = brAPIUpdateFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { try { WorkflowCreation brAPITrialCreation = brAPICreationFactory.trialWorkflowCreationBean(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/LocationCommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/LocationCommit.java index 34fda1fca..d68c9183f 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/LocationCommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/LocationCommit.java @@ -44,7 +44,7 @@ public LocationCommit(BrAPICreationFactory brAPICreationFactory) { this.brAPICreationFactory = brAPICreationFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { try { WorkflowCreation locationCreation = brAPICreationFactory.locationWorkflowCreationBean(context); log.info("creating new locationss in the Deltabreed database"); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java index e65d11d15..d1e3a442a 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java @@ -31,6 +31,7 @@ import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddlewareContext; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.MiddlewareException; import org.breedinginsight.model.ProgramLocation; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; import javax.inject.Inject; @@ -50,7 +51,7 @@ public WorkflowInitialization(BrAPIReadFactory brAPIReadFactory) { this.brAPIReadFactory = brAPIReadFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { brAPIObservationUnitReadWorkflowInitialization = brAPIReadFactory.observationUnitWorkflowReadInitializationBean(context); brAPITrialReadWorkflowInitialization = brAPIReadFactory.trialWorkflowReadInitializationBean(context); brAPIStudyReadWorkflowInitialization = brAPIReadFactory.studyWorkflowReadInitializationBean(context); @@ -69,6 +70,8 @@ public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext } catch (ApiException e) { context.getAppendOverwriteWorkflowContext().setProcessError(new MiddlewareException(e)); return this.compensate(context); + } catch (UnprocessableEntityException e) { + throw e; } return processNext(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/model/Middleware.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/model/Middleware.java index 0a730a7b2..5a2fec17a 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/model/Middleware.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/model/Middleware.java @@ -17,6 +17,8 @@ package org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; + public abstract class Middleware { Middleware next; @@ -38,7 +40,7 @@ public static Middleware link(Middleware first, Middleware... chain) { /** * Subclasses will implement this local transaction. */ - public abstract T process(T context); + public abstract T process(T context) throws UnprocessableEntityException; /** * Subclasses will implement this method to handle errors and possibly undo the local transaction. */ @@ -47,7 +49,7 @@ public static Middleware link(Middleware first, Middleware... chain) { * Processes the next local transaction or ends traversing if we're at the * last local transaction of the transaction. */ - protected T processNext(T context) { + protected T processNext(T context) throws UnprocessableEntityException { if (next == null) { return context; } diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java index 720c93d25..ba93dfc71 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java @@ -28,6 +28,7 @@ import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.brapps.importer.services.processors.experiment.ExperimentUtilities; import org.breedinginsight.model.Program; +import org.breedinginsight.services.exceptions.UnprocessableEntityException; import org.breedinginsight.utilities.Utilities; import javax.inject.Inject; @@ -61,7 +62,7 @@ public ObservationUnitService(BrAPIObservationUnitDAO brAPIObservationUnitDAO) { * @throws ApiException if an error occurs during the retrieval of observation units * @throws IllegalStateException if the retrieved observation units do not match the provided observation unit IDs */ - public List getObservationUnitsByDbId(Set obsUnitIds, Program program) throws ApiException, IllegalStateException { + public List getObservationUnitsByDbId(Set obsUnitIds, Program program) throws ApiException, IllegalStateException, UnprocessableEntityException { List brapiUnits = null; // Retrieve reference Observation Units based on IDs @@ -75,7 +76,7 @@ public List getObservationUnitsByDbId(Set obsUnitI missingIds.removeAll(brapiUnits.stream().map(BrAPIObservationUnit::getObservationUnitDbId).collect(Collectors.toSet())); // Throw exception with missing IDs information - throw new IllegalStateException(ExperimentUtilities.UNMATCHED_COLUMN + String.join(COMMA_DELIMITER, missingIds)); + throw new UnprocessableEntityException(ExperimentUtilities.INVALID_OBS_UNIT_ID_ERROR + String.join(COMMA_DELIMITER, missingIds)); } return brapiUnits; From e52481f645a2d1de0fee0ed8476b3efc617a24e8 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Wed, 29 Jan 2025 14:27:57 -0500 Subject: [PATCH 067/132] [BI-2156-qa] - fixed edge case NPE fixed case when file contains valid ObsUnitIds but no phenotypic data columns --- .../middleware/process/ImportTableProcess.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/process/ImportTableProcess.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/process/ImportTableProcess.java index 7ee29e51f..914633769 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/process/ImportTableProcess.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/process/ImportTableProcess.java @@ -386,6 +386,11 @@ public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext // Add the pending observation map to the context for use in processing the import context.getAppendOverwriteWorkflowContext().setPendingObservationByHash(pendingObservationByHash); + // Make sure the workflow statistic is not null. + if (context.getAppendOverwriteWorkflowContext().getStatistic() == null) { + context.getAppendOverwriteWorkflowContext().setStatistic(statistic); + } + return processNext(context); } catch (DoesNotExistException | ApiException | UnprocessableEntityException | ValidatorException | IllegalStateException e) { context.getAppendOverwriteWorkflowContext().setProcessError(new MiddlewareException(e)); From 4f0b94122a52c80ec2b9872e1bdb8fd7f12f4ff7 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Fri, 31 Jan 2025 17:57:37 -0500 Subject: [PATCH 068/132] [BI-2156-qa] - made changes based on review --- .../brapps/importer/controllers/ImportController.java | 2 +- .../brapps/importer/model/imports/BrAPIImportService.java | 2 +- .../brapps/importer/model/imports/DomainImportService.java | 2 +- .../brapps/importer/model/workflow/Workflow.java | 4 ++-- .../brapps/importer/services/FileImportService.java | 2 +- .../processors/experiment/ExperimentWorkflowNavigator.java | 4 ++-- .../appendoverwrite/AppendOverwritePhenotypesWorkflow.java | 2 +- .../middleware/AppendOverwriteIDValidation.java | 2 +- .../appendoverwrite/middleware/commit/BrAPICommit.java | 2 +- .../middleware/commit/BrAPIDatasetCommit.java | 2 +- .../middleware/commit/BrAPIObservationCommit.java | 2 +- .../middleware/commit/BrAPIObservationUnitCommit.java | 2 +- .../appendoverwrite/middleware/commit/BrAPIStudyCommit.java | 2 +- .../appendoverwrite/middleware/commit/BrAPITrialCommit.java | 2 +- .../appendoverwrite/middleware/commit/LocationCommit.java | 2 +- .../middleware/initialize/WorkflowInitialization.java | 6 ++---- .../experiment/appendoverwrite/model/Middleware.java | 4 ++-- 17 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapps/importer/controllers/ImportController.java b/src/main/java/org/breedinginsight/brapps/importer/controllers/ImportController.java index c78921693..d732da0c5 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/controllers/ImportController.java +++ b/src/main/java/org/breedinginsight/brapps/importer/controllers/ImportController.java @@ -211,7 +211,7 @@ public HttpResponse>> getSystemMappings(@Nu @Produces(MediaType.APPLICATION_JSON) @AddMetadata @Secured(SecurityRule.IS_ANONYMOUS) - public HttpResponse>> getWorkflowsForSystemMapping(@PathVariable UUID mappingId) throws UnprocessableEntityException { + public HttpResponse>> getWorkflowsForSystemMapping(@PathVariable UUID mappingId) { List workflows = null; try { diff --git a/src/main/java/org/breedinginsight/brapps/importer/model/imports/BrAPIImportService.java b/src/main/java/org/breedinginsight/brapps/importer/model/imports/BrAPIImportService.java index 606901c4f..4b9ed339d 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/model/imports/BrAPIImportService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/model/imports/BrAPIImportService.java @@ -26,7 +26,7 @@ public interface BrAPIImportService { String getImportTypeId(); BrAPIImport getImportClass(); - List getWorkflows() throws UnprocessableEntityException; + List getWorkflows(); default String getInvalidIntegerMsg(String columnName) { return String.format("Column name \"%s\" must be integer type, but non-integer type provided.", columnName); } diff --git a/src/main/java/org/breedinginsight/brapps/importer/model/imports/DomainImportService.java b/src/main/java/org/breedinginsight/brapps/importer/model/imports/DomainImportService.java index 64f84a378..dcaa3e613 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/model/imports/DomainImportService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/model/imports/DomainImportService.java @@ -44,7 +44,7 @@ public String getMissingColumnMsg(String columnName) { return "Column heading does not match template or ontology"; } @Override - public List getWorkflows() throws UnprocessableEntityException { + public List getWorkflows() { return workflowNavigator.getWorkflows(); } diff --git a/src/main/java/org/breedinginsight/brapps/importer/model/workflow/Workflow.java b/src/main/java/org/breedinginsight/brapps/importer/model/workflow/Workflow.java index f4e37648e..9c5c2e8d5 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/model/workflow/Workflow.java +++ b/src/main/java/org/breedinginsight/brapps/importer/model/workflow/Workflow.java @@ -39,7 +39,7 @@ public interface Workflow extends Ordered { * @param context the ImportServiceContext object containing necessary information for the workflow * @return an Optional of ImportWorkflowResult representing the result of the workflow execution */ - Optional process(ImportServiceContext context) throws UnprocessableEntityException; + Optional process(ImportServiceContext context); /** * Default method to get a list of workflows. @@ -47,7 +47,7 @@ public interface Workflow extends Ordered { * * @return a List of ImportWorkflow containing workflows */ - default List getWorkflows() throws UnprocessableEntityException { + default List getWorkflows() { // Default implementation for getWorkflows method return new ArrayList<>(); } diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/FileImportService.java b/src/main/java/org/breedinginsight/brapps/importer/services/FileImportService.java index 1b1033e04..039c0b58c 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/FileImportService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/FileImportService.java @@ -563,7 +563,7 @@ public List getSystemMappingByName(String name) { * @return A list of ImportWorkflow objects representing the workflows for the specified system mapping * @throws DoesNotExistException If the system mapping with the given ID does not exist */ - public List getWorkflowsForSystemMapping(UUID mappingId) throws DoesNotExistException, UnprocessableEntityException { + public List getWorkflowsForSystemMapping(UUID mappingId) throws DoesNotExistException { // Retrieve the import mapping configuration based on the provided mapping ID ImportMapping mappingConfig = importMappingDAO.getMapping(mappingId) diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentWorkflowNavigator.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentWorkflowNavigator.java index c42b86696..7b294c534 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentWorkflowNavigator.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentWorkflowNavigator.java @@ -54,7 +54,7 @@ public ExperimentWorkflowNavigator(List workflows) { * @return An Optional containing the first non-empty ImportWorkflowResult from the executed workflows, or an empty Optional if no non-empty result is found */ @Override - public Optional process(ImportServiceContext context) throws UnprocessableEntityException { + public Optional process(ImportServiceContext context) { /** * Have each workflow in order process the context, returning the first non-empty result */ @@ -75,7 +75,7 @@ public Optional process(ImportServiceContext context) thro * * @return List of ImportWorkflow objects with workflow metadata */ - public List getWorkflows() throws UnprocessableEntityException { + public List getWorkflows() { // Process each workflow with a null context // Filter out any workflows that do not return a result // Extract the result from Optional diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/AppendOverwritePhenotypesWorkflow.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/AppendOverwritePhenotypesWorkflow.java index accd83c29..c3a121518 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/AppendOverwritePhenotypesWorkflow.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/AppendOverwritePhenotypesWorkflow.java @@ -78,7 +78,7 @@ public AppendOverwritePhenotypesWorkflow(AppendOverwriteIDValidation expUnitIDVa * @return Optional containing ImportWorkflowResult with workflow metadata and import preview response if successful, else empty Optional. */ @Override - public Optional process(ImportServiceContext context) throws UnprocessableEntityException { + public Optional process(ImportServiceContext context) { // Metadata about this workflow processing the context ImportWorkflow workflow = ImportWorkflow.builder() diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java index 1068b4119..a7b664520 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java @@ -30,7 +30,7 @@ @Prototype public class AppendOverwriteIDValidation extends AppendOverwriteMiddleware { @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { try { context.getAppendOverwriteWorkflowContext().setReferenceOUIds(ExperimentUtilities.collateReferenceOUIds(context)); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPICommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPICommit.java index 48b6b3c66..8f96a6ae8 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPICommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPICommit.java @@ -49,7 +49,7 @@ public BrAPICommit(BrAPIDatasetCommit brAPIDatasetCommit, } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { log.debug("starting post of experiment data to BrAPI server"); return this.middleware.process(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIDatasetCommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIDatasetCommit.java index a9c7378f4..368673f19 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIDatasetCommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIDatasetCommit.java @@ -50,7 +50,7 @@ public BrAPIDatasetCommit(BrAPICreationFactory brAPICreationFactory, BrAPIUpdate this.brAPIUpdateFactory = brAPIUpdateFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { try { WorkflowCreation datasetCreation = brAPICreationFactory.datasetWorkflowCreationBean(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationCommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationCommit.java index 828efe0f8..779d75493 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationCommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationCommit.java @@ -51,7 +51,7 @@ public BrAPIObservationCommit(BrAPICreationFactory brAPICreationFactory, BrAPIUp this.brAPIUpdateFactory = brAPIUpdateFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { try { WorkflowCreation brAPIObservationCreation = brAPICreationFactory.observationWorkflowCreationBean(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationUnitCommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationUnitCommit.java index 0e5c674ad..4610be387 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationUnitCommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIObservationUnitCommit.java @@ -44,7 +44,7 @@ public BrAPIObservationUnitCommit(BrAPICreationFactory brAPICreationFactory) { this.brAPICreationFactory = brAPICreationFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { try{ WorkflowCreation brAPIObservationUnitCreation = brAPICreationFactory.observationUnitWorkflowCreationBean(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIStudyCommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIStudyCommit.java index 6823a8abc..f10a0b8b6 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIStudyCommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPIStudyCommit.java @@ -44,7 +44,7 @@ public BrAPIStudyCommit(BrAPICreationFactory brAPICreationFactory) { this.brAPICreationFactory = brAPICreationFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { try { WorkflowCreation brAPIStudyCreation = brAPICreationFactory.studyWorkflowCreationBean(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPITrialCommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPITrialCommit.java index 59de45c8b..3ca112b21 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPITrialCommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPITrialCommit.java @@ -50,7 +50,7 @@ public BrAPITrialCommit(BrAPICreationFactory brAPICreationFactory, BrAPIUpdateFa this.brAPIUpdateFactory = brAPIUpdateFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { try { WorkflowCreation brAPITrialCreation = brAPICreationFactory.trialWorkflowCreationBean(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/LocationCommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/LocationCommit.java index d68c9183f..34fda1fca 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/LocationCommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/LocationCommit.java @@ -44,7 +44,7 @@ public LocationCommit(BrAPICreationFactory brAPICreationFactory) { this.brAPICreationFactory = brAPICreationFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { try { WorkflowCreation locationCreation = brAPICreationFactory.locationWorkflowCreationBean(context); log.info("creating new locationss in the Deltabreed database"); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java index d1e3a442a..909c9db4a 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java @@ -51,7 +51,7 @@ public WorkflowInitialization(BrAPIReadFactory brAPIReadFactory) { this.brAPIReadFactory = brAPIReadFactory; } @Override - public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) throws UnprocessableEntityException { + public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { brAPIObservationUnitReadWorkflowInitialization = brAPIReadFactory.observationUnitWorkflowReadInitializationBean(context); brAPITrialReadWorkflowInitialization = brAPIReadFactory.trialWorkflowReadInitializationBean(context); brAPIStudyReadWorkflowInitialization = brAPIReadFactory.studyWorkflowReadInitializationBean(context); @@ -67,11 +67,9 @@ public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext locationReadWorkflowInitialization.execute(); brAPIDatasetReadWorkflowInitialization.execute(); brAPIGermplasmReadWorkflowInitialization.execute(); - } catch (ApiException e) { + } catch (ApiException | UnprocessableEntityException e) { context.getAppendOverwriteWorkflowContext().setProcessError(new MiddlewareException(e)); return this.compensate(context); - } catch (UnprocessableEntityException e) { - throw e; } return processNext(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/model/Middleware.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/model/Middleware.java index 5a2fec17a..fd9b4d5b7 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/model/Middleware.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/model/Middleware.java @@ -40,7 +40,7 @@ public static Middleware link(Middleware first, Middleware... chain) { /** * Subclasses will implement this local transaction. */ - public abstract T process(T context) throws UnprocessableEntityException; + public abstract T process(T context); /** * Subclasses will implement this method to handle errors and possibly undo the local transaction. */ @@ -49,7 +49,7 @@ public static Middleware link(Middleware first, Middleware... chain) { * Processes the next local transaction or ends traversing if we're at the * last local transaction of the transaction. */ - protected T processNext(T context) throws UnprocessableEntityException { + protected T processNext(T context) { if (next == null) { return context; } From d23c84c4aafe038ba3380fad7398f5152a46bed6 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Fri, 31 Jan 2025 18:04:15 -0500 Subject: [PATCH 069/132] [BI-2156-qa] - cleaned up imports --- .../model/imports/BrAPIImportService.java | 1 - .../model/imports/DomainImportService.java | 1 - .../importer/model/workflow/Workflow.java | 1 - .../ExperimentWorkflowNavigator.java | 37 ++++++------------- .../AppendOverwritePhenotypesWorkflow.java | 2 - .../AppendOverwriteIDValidation.java | 1 - .../middleware/commit/BrAPICommit.java | 1 - .../appendoverwrite/model/Middleware.java | 2 - .../service/ObservationUnitService.java | 1 - 9 files changed, 11 insertions(+), 36 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapps/importer/model/imports/BrAPIImportService.java b/src/main/java/org/breedinginsight/brapps/importer/model/imports/BrAPIImportService.java index 4b9ed339d..3f25d99e6 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/model/imports/BrAPIImportService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/model/imports/BrAPIImportService.java @@ -19,7 +19,6 @@ import org.breedinginsight.brapps.importer.model.response.ImportPreviewResponse; import org.breedinginsight.brapps.importer.model.workflow.ImportWorkflow; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; import java.util.List; diff --git a/src/main/java/org/breedinginsight/brapps/importer/model/imports/DomainImportService.java b/src/main/java/org/breedinginsight/brapps/importer/model/imports/DomainImportService.java index dcaa3e613..5f96ae904 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/model/imports/DomainImportService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/model/imports/DomainImportService.java @@ -22,7 +22,6 @@ import org.breedinginsight.brapps.importer.model.workflow.ImportWorkflow; import org.breedinginsight.brapps.importer.model.workflow.ImportWorkflowResult; import org.breedinginsight.brapps.importer.model.workflow.Workflow; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; import javax.inject.Singleton; import java.util.List; diff --git a/src/main/java/org/breedinginsight/brapps/importer/model/workflow/Workflow.java b/src/main/java/org/breedinginsight/brapps/importer/model/workflow/Workflow.java index 9c5c2e8d5..7504d7ab0 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/model/workflow/Workflow.java +++ b/src/main/java/org/breedinginsight/brapps/importer/model/workflow/Workflow.java @@ -19,7 +19,6 @@ import io.micronaut.core.order.Ordered; import org.breedinginsight.brapps.importer.model.imports.ImportServiceContext; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentWorkflowNavigator.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentWorkflowNavigator.java index 7b294c534..8de6a7d8b 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentWorkflowNavigator.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentWorkflowNavigator.java @@ -22,10 +22,8 @@ import org.breedinginsight.brapps.importer.model.workflow.ImportWorkflow; import org.breedinginsight.brapps.importer.model.workflow.ExperimentWorkflow; import org.breedinginsight.brapps.importer.model.workflow.ImportWorkflowResult; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; import javax.inject.Singleton; -import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -58,16 +56,11 @@ public Optional process(ImportServiceContext context) { /** * Have each workflow in order process the context, returning the first non-empty result */ - Optional first = Optional.empty(); - for (ExperimentWorkflow workflow : workflows) { - Optional process = workflow.process(context); - if (process.isPresent()) { - ImportWorkflowResult importWorkflowResult = process.get(); - first = Optional.of(importWorkflowResult); - break; - } - } - return first; + return workflows.stream() + .map(workflow->workflow.process(context)) + .filter(Optional::isPresent) + .map(Optional::get) + .findFirst(); } /** @@ -76,20 +69,12 @@ public Optional process(ImportServiceContext context) { * @return List of ImportWorkflow objects with workflow metadata */ public List getWorkflows() { - // Process each workflow with a null context - // Filter out any workflows that do not return a result - // Extract the result from Optional - // Retrieve the workflow metadata - // Collect the workflow metadata into a list - List workflowSummaryList = new ArrayList<>(); - for (ExperimentWorkflow workflow : workflows) { - Optional process = workflow.process(null); - if (process.isPresent()) { - ImportWorkflowResult importWorkflowResult = process.get(); - ImportWorkflow importWorkflowResultWorkflow = importWorkflowResult.getWorkflow(); - workflowSummaryList.add(importWorkflowResultWorkflow); - } - } + List workflowSummaryList = workflows.stream() + .map(workflow -> workflow.process(null)) // Process each workflow with a null context + .filter(Optional::isPresent) // Filter out any workflows that do not return a result + .map(Optional::get) // Extract the result from Optional + .map(ImportWorkflowResult::getWorkflow) // Retrieve the workflow metadata + .collect(Collectors.toList()); // Collect the workflow metadata into a list // Set the order field for each workflow based on its position in the list for (int i = 0; i < workflowSummaryList.size(); i++) { diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/AppendOverwritePhenotypesWorkflow.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/AppendOverwritePhenotypesWorkflow.java index c3a121518..fbbc0afbd 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/AppendOverwritePhenotypesWorkflow.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/AppendOverwritePhenotypesWorkflow.java @@ -30,14 +30,12 @@ import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.middleware.AppendOverwriteIDValidation; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.middleware.commit.BrAPICommit; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.middleware.initialize.WorkflowInitialization; -import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.middleware.process.AppendStatistic; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.middleware.process.ImportTableProcess; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddleware; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddlewareContext; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteWorkflowContext; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.MiddlewareException; import org.breedinginsight.brapps.importer.services.processors.experiment.model.ImportContext; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java index a7b664520..64f471b88 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java @@ -24,7 +24,6 @@ import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddlewareContext; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddleware; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.MiddlewareException; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; @Slf4j @Prototype diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPICommit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPICommit.java index 8f96a6ae8..1b6cbd7ee 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPICommit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/commit/BrAPICommit.java @@ -21,7 +21,6 @@ import lombok.extern.slf4j.Slf4j; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddleware; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddlewareContext; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; import javax.inject.Inject; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/model/Middleware.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/model/Middleware.java index fd9b4d5b7..0a730a7b2 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/model/Middleware.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/model/Middleware.java @@ -17,8 +17,6 @@ package org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; - public abstract class Middleware { Middleware next; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java index ba93dfc71..10f6b4c7d 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java @@ -22,7 +22,6 @@ import org.brapi.v2.model.BrAPIExternalReference; import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.breedinginsight.brapi.v2.dao.BrAPIObservationUnitDAO; -import org.breedinginsight.brapps.importer.model.imports.experimentObservation.ExperimentObservation; import org.breedinginsight.brapps.importer.model.response.ImportObjectState; import org.breedinginsight.brapps.importer.model.response.PendingImportObject; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; From 67421212396716bfdc9abb015b27ff4306ecda79 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Mon, 3 Feb 2025 19:44:47 -0500 Subject: [PATCH 070/132] [BI-2156-qa] - made error tabular --- .../experiment/ExperimentUtilities.java | 3 +-- .../experiment/MissingValuesException.java | 12 ++++++++++ .../factory/action/BrAPIAction.java | 3 ++- .../action/WorkflowReadInitialization.java | 6 ++--- .../entity/ExperimentImportEntity.java | 3 ++- .../entity/PendingObservationUnit.java | 24 ++++++++++++++++--- .../initialize/WorkflowInitialization.java | 4 ++-- .../service/ObservationUnitService.java | 8 +++---- 8 files changed, 45 insertions(+), 18 deletions(-) create mode 100644 src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/MissingValuesException.java diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentUtilities.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentUtilities.java index d63234265..8c3160e5d 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentUtilities.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentUtilities.java @@ -70,8 +70,7 @@ public class ExperimentUtilities { public static final String PREEXISTING_EXPERIMENT_TITLE = "Experiment Title already exists"; public static final String MISSING_OBS_UNIT_ID_ERROR = "Experimental entities are missing ObsUnitIDs"; public static final String UNMATCHED_COLUMN = "Ontology term(s) not found: "; - public static final String INVALID_OBS_UNIT_ID_ERROR = "ObsUnitID(s) not found: "; - + public static final String INVALID_OBS_UNIT_ID_ERROR = "Invalid ObsUnitID"; Gson gson; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/MissingValuesException.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/MissingValuesException.java new file mode 100644 index 000000000..61e7fc299 --- /dev/null +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/MissingValuesException.java @@ -0,0 +1,12 @@ +package org.breedinginsight.brapps.importer.services.processors.experiment; + +import lombok.Getter; + +import java.util.Set; + +@Getter +public class MissingValuesException extends Exception { + private Set missingIds; + + public MissingValuesException(Set missingIds) { this.missingIds = missingIds; } +} diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/BrAPIAction.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/BrAPIAction.java index 830de5828..436ad2938 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/BrAPIAction.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/BrAPIAction.java @@ -23,6 +23,7 @@ import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.services.exceptions.MissingRequiredInfoException; import org.breedinginsight.services.exceptions.UnprocessableEntityException; +import org.breedinginsight.services.exceptions.ValidatorException; import java.util.Optional; @@ -42,7 +43,7 @@ public interface BrAPIAction { * @return An Optional containing the relevant BrAPI state after executing the action. * @throws ApiException if an error occurs during the execution of the action. */ - Optional> execute() throws ApiException, MissingRequiredInfoException, UnprocessableEntityException, DoesNotExistException; + Optional> execute() throws ApiException, MissingRequiredInfoException, UnprocessableEntityException, DoesNotExistException, ValidatorException; /** * Get the BrAPI entity being acted on based on the provided ExpUnitMiddlewareContext. diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/WorkflowReadInitialization.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/WorkflowReadInitialization.java index 5cdddaaf5..4e1f62142 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/WorkflowReadInitialization.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/WorkflowReadInitialization.java @@ -23,7 +23,7 @@ import org.brapi.client.v2.model.exceptions.ApiException; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.factory.BrAPIState; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.factory.entity.ExperimentImportEntity; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; +import org.breedinginsight.services.exceptions.ValidatorException; import org.breedinginsight.utilities.Utilities; import java.util.List; @@ -45,7 +45,7 @@ protected WorkflowReadInitialization(ExperimentImportEntity entity) { * @return an Optional containing the BrAPIState representing the completed read workflow * @throws ApiException if an error occurs during execution */ - public Optional> execute() throws ApiException, UnprocessableEntityException { + public Optional> execute() throws ApiException, ValidatorException { try { List fetchedMembers = entity.brapiRead(); entity.initializeWorkflow(fetchedMembers); @@ -53,8 +53,6 @@ public Optional> execute() throws ApiException, UnprocessableEntit } catch(ApiException e) { log.error(String.format("Error fetching %s: %s", entity.getClass().getName(), Utilities.generateApiExceptionLogMessage(e)), e); throw new ApiException(e); - } catch (UnprocessableEntityException e) { - throw e; } } diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/ExperimentImportEntity.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/ExperimentImportEntity.java index 3ca5fa004..3bb33d78f 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/ExperimentImportEntity.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/ExperimentImportEntity.java @@ -22,6 +22,7 @@ import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.services.exceptions.MissingRequiredInfoException; import org.breedinginsight.services.exceptions.UnprocessableEntityException; +import org.breedinginsight.services.exceptions.ValidatorException; import java.util.List; @@ -43,7 +44,7 @@ public interface ExperimentImportEntity { * @return List of fetched entities * @throws ApiException if there is an issue with the API call */ - public List brapiRead() throws ApiException, UnprocessableEntityException; + public List brapiRead() throws ApiException, ValidatorException; /** * Commit objects changed by the workflow to the BrAPI service. diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java index af5d3b7e2..f4e458520 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java @@ -21,11 +21,14 @@ import org.apache.commons.lang3.StringUtils; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.pheno.BrAPIObservationUnit; +import org.breedinginsight.api.model.v1.response.ValidationErrors; import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; import org.breedinginsight.brapi.v2.dao.BrAPIObservationUnitDAO; +import org.breedinginsight.brapps.importer.model.imports.experimentObservation.ExperimentObservation; import org.breedinginsight.brapps.importer.model.response.ImportObjectState; import org.breedinginsight.brapps.importer.model.response.PendingImportObject; import org.breedinginsight.brapps.importer.services.processors.experiment.ExperimentUtilities; +import org.breedinginsight.brapps.importer.services.processors.experiment.MissingValuesException; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteWorkflowContext; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddlewareContext; import org.breedinginsight.brapps.importer.services.processors.experiment.model.ImportContext; @@ -33,6 +36,7 @@ import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.services.exceptions.MissingRequiredInfoException; import org.breedinginsight.services.exceptions.UnprocessableEntityException; +import org.breedinginsight.services.exceptions.ValidatorException; import org.breedinginsight.utilities.Utilities; import java.util.*; @@ -94,12 +98,26 @@ public List brapiPost(List members) * @throws ApiException if there is an issue with the API call */ @Override - public List brapiRead() throws ApiException, UnprocessableEntityException { + public List brapiRead() throws ApiException, ValidatorException { // Collect deltabreed-generated obs unit ids listed in the import Set obsUnitIds = cache.getReferenceOUIds(); - // For each id fetch the observation unit from the brapi data store - return observationUnitService.getObservationUnitsByDbId(new HashSet<>(obsUnitIds), importContext.getProgram()); + try { + // For each id fetch the observation unit from the brapi data store + return observationUnitService.getObservationUnitsByDbId(new HashSet<>(obsUnitIds), importContext.getProgram()); + } + catch (MissingValuesException e) { + ValidationErrors validationErrors = new ValidationErrors(); + + // Build a detailed tabular error. + for (int rowNum = 0; rowNum < importContext.getImportRows().size(); rowNum++) { + String rowObsUnitId = ((ExperimentObservation)importContext.getImportRows().get(rowNum)).getObsUnitID(); + if (e.getMissingIds().contains(rowObsUnitId)) { + ExperimentUtilities.addRowError(ExperimentObservation.Columns.OBS_UNIT_ID, ExperimentUtilities.INVALID_OBS_UNIT_ID_ERROR, validationErrors, rowNum); + } + } + throw new ValidatorException(validationErrors); + } } /** diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java index 909c9db4a..40e0ea301 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java @@ -31,7 +31,7 @@ import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddlewareContext; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.MiddlewareException; import org.breedinginsight.model.ProgramLocation; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; +import org.breedinginsight.services.exceptions.ValidatorException; import javax.inject.Inject; @@ -67,7 +67,7 @@ public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext locationReadWorkflowInitialization.execute(); brAPIDatasetReadWorkflowInitialization.execute(); brAPIGermplasmReadWorkflowInitialization.execute(); - } catch (ApiException | UnprocessableEntityException e) { + } catch (ApiException | ValidatorException e) { context.getAppendOverwriteWorkflowContext().setProcessError(new MiddlewareException(e)); return this.compensate(context); } diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java index 10f6b4c7d..f34808f7f 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java @@ -26,8 +26,8 @@ import org.breedinginsight.brapps.importer.model.response.PendingImportObject; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.brapps.importer.services.processors.experiment.ExperimentUtilities; +import org.breedinginsight.brapps.importer.services.processors.experiment.MissingValuesException; import org.breedinginsight.model.Program; -import org.breedinginsight.services.exceptions.UnprocessableEntityException; import org.breedinginsight.utilities.Utilities; import javax.inject.Inject; @@ -35,8 +35,6 @@ import java.util.*; import java.util.stream.Collectors; -import static org.breedinginsight.brapps.importer.services.processors.experiment.model.ExpImportProcessConstants.COMMA_DELIMITER; - @Singleton public class ObservationUnitService { private final BrAPIObservationUnitDAO brAPIObservationUnitDAO; @@ -61,7 +59,7 @@ public ObservationUnitService(BrAPIObservationUnitDAO brAPIObservationUnitDAO) { * @throws ApiException if an error occurs during the retrieval of observation units * @throws IllegalStateException if the retrieved observation units do not match the provided observation unit IDs */ - public List getObservationUnitsByDbId(Set obsUnitIds, Program program) throws ApiException, IllegalStateException, UnprocessableEntityException { + public List getObservationUnitsByDbId(Set obsUnitIds, Program program) throws ApiException, IllegalStateException, MissingValuesException { List brapiUnits = null; // Retrieve reference Observation Units based on IDs @@ -75,7 +73,7 @@ public List getObservationUnitsByDbId(Set obsUnitI missingIds.removeAll(brapiUnits.stream().map(BrAPIObservationUnit::getObservationUnitDbId).collect(Collectors.toSet())); // Throw exception with missing IDs information - throw new UnprocessableEntityException(ExperimentUtilities.INVALID_OBS_UNIT_ID_ERROR + String.join(COMMA_DELIMITER, missingIds)); + throw new MissingValuesException(missingIds); } return brapiUnits; From a35eb5fd464f47fbc607754cd3a0dfd71754bf7b Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Tue, 4 Feb 2025 15:39:21 -0500 Subject: [PATCH 071/132] Added plate deletion --- .../brapps/importer/daos/BrAPISampleDAO.java | 62 ++++++++++++++++--- .../services/SampleSubmissionService.java | 11 ++-- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java index fe4ad57c2..13f2cc723 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java +++ b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java @@ -123,20 +123,45 @@ public void deleteSamples(Program program, List sampleDbIds) throws ApiE String batchDbId = postSamplesBatch(programBrAPIBaseUrl, sampleDbIds); // delete samples specified in batch - deleteSamplesBatch(programBrAPIBaseUrl, batchDbId); + deleteBatch(programBrAPIBaseUrl, batchDbId); + } + + /** + * Deletes all plates specified in the brapi server + * @param program + * @param plateDbIds + * @throws ApiException + */ + public void deletePlates(Program program, List plateDbIds) throws ApiException { + // create batch of samples, not yet included in brapi client TODO: switch to brapi client when available + String programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(program.getId()); + String batchDbId = postPlatesBatch(programBrAPIBaseUrl, plateDbIds); - // TODO: delete plates associated with submission, could potentially only require brapi server side change if deleting a sample cascades + // delete plates specified in batch + deleteBatch(programBrAPIBaseUrl, batchDbId); } + private String postSamplesBatch(String programBrAPIBaseUrl, List sampleDbIds) throws ApiException { HttpUrl.Builder requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/batchDeletes").newBuilder(); - //requestUrl.addQueryParameter("hardDelete", Boolean.toString(hard)); - - BatchDeleteRequest requestBody = new BatchDeleteRequest(sampleDbIds); + SampleBatchDeleteRequest requestBody = new SampleBatchDeleteRequest(sampleDbIds); String json = gson.toJson(requestBody); RequestBody body = RequestBody.create(json, MediaType.get("application/json")); + HttpUrl url = requestUrl.build(); + return postBatch(url, body, programBrAPIBaseUrl); + } + private String postPlatesBatch(String programBrAPIBaseUrl, List plateDbIds) throws ApiException { + HttpUrl.Builder requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/batchDeletes").newBuilder(); + PlateBatchDeleteRequest requestBody = new PlateBatchDeleteRequest(plateDbIds); + String json = gson.toJson(requestBody); + RequestBody body = RequestBody.create(json, MediaType.get("application/json")); HttpUrl url = requestUrl.build(); + return postBatch(url, body, programBrAPIBaseUrl); + } + + private String postBatch(HttpUrl url, RequestBody body, String programBrAPIBaseUrl) throws ApiException { + Request brapiRequest = new Request.Builder() .url(url) .post(body) @@ -153,12 +178,11 @@ private String postSamplesBatch(String programBrAPIBaseUrl, List sampleD return resultObject.get("batchDeleteDbId").getAsString(); } else if (resultObject.has("searchResultsDbId")) { // TODO: once api stuff is in client use BrAPIDAOUtil::search to handle retries, for now just request once - // could maybe be an issue for large number of samples + // could be an issue for large number of samples return getBatchDeleteDbIdFromSearchResult(programBrAPIBaseUrl, resultObject.get("searchResultsDbId").getAsString()); } else { throw new InternalServerException("Expected batchDeleteDbId or searchResultsDbId but got " + resultObject); } - } private String getBatchDeleteDbIdFromSearchResult(String programBrAPIBaseUrl, String searchResultDbId) throws ApiException { @@ -178,7 +202,7 @@ private String getBatchDeleteDbIdFromSearchResult(String programBrAPIBaseUrl, St return resultObject.get("batchDeleteDbId").getAsString(); } - private void deleteSamplesBatch(String programBrAPIBaseUrl, String batchDbId) throws ApiException { + private void deleteBatch(String programBrAPIBaseUrl, String batchDbId) throws ApiException { HttpUrl.Builder requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/batchDeletes/" + batchDbId).newBuilder(); requestUrl.addQueryParameter("hardDelete", "true"); @@ -195,11 +219,11 @@ private void deleteSamplesBatch(String programBrAPIBaseUrl, String batchDbId) th /** * TODO: temporary minimal model here until brapi client is updated with delete models */ - public class BatchDeleteRequest { + public class SampleBatchDeleteRequest { private String batchDeleteType; private Search search; - public BatchDeleteRequest(List sampleDbIds) { + public SampleBatchDeleteRequest(List sampleDbIds) { this.batchDeleteType = "samples"; this.search = new Search(sampleDbIds); } @@ -213,4 +237,22 @@ public Search(List sampleDbIds) { } } + public class PlateBatchDeleteRequest { + private String batchDeleteType; + private Search search; + + public PlateBatchDeleteRequest(List plateDbIds) { + this.batchDeleteType = "plates"; + this.search = new Search(plateDbIds); + } + + private class Search { + private List plateDbIds; + + public Search(List plateDbIds) { + this.plateDbIds = plateDbIds; + } + } + } + } diff --git a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java index 134f690dd..e0ccc49fd 100644 --- a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java +++ b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java @@ -440,18 +440,19 @@ public void deleteSampleSubmission(Program program, UUID submissionId) throws Ap // delete samples // delete plates - // create a batch of sampleIds to delete + // create a batch of sampleIds and plateIds to delete - // get samples with the samble submission xref + // get samples with the sample submission xref List samples = sampleDAO.readSamplesBySubmissionIds(program, List.of(submissionId.toString())); - // extract sampleDbIds to include in batch + // extract sampleDbIds and plateDbIds to include in batches List sampleDbIds = samples.stream().map(BrAPISample::getSampleDbId).collect(Collectors.toList()); + List platesDbIds = samples.stream().map(BrAPISample::getPlateDbId).collect(Collectors.toList()); // create batch of samples, not yet included in brapi client TODO: switch to brapi client when available + // TODO: uncomment when brapi server is working sampleDAO.deleteSamples(program, sampleDbIds); - - // TODO: delete plates, not yet supported in brapi server + sampleDAO.deletePlates(program, platesDbIds); // delete sample submission record from bidb submissionDAO.deleteById(submissionId); From 6b6728f0d22390ffedc3734a57ef7175bc6509dc Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:21:18 -0500 Subject: [PATCH 072/132] [BI-2047] - added getByObservationVariableDbId --- .../org/breedinginsight/services/TraitService.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/org/breedinginsight/services/TraitService.java b/src/main/java/org/breedinginsight/services/TraitService.java index 8394acd28..2bd8eb4b3 100644 --- a/src/main/java/org/breedinginsight/services/TraitService.java +++ b/src/main/java/org/breedinginsight/services/TraitService.java @@ -491,4 +491,15 @@ 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 Trait getByObservationVariableDbId(UUID programId, String observationVariableDbId) throws DoesNotExistException { + if (!programService.exists(programId)) { + throw new DoesNotExistException("Program does not exist"); + } + + return traitDAO.getTraitsFullByProgramId(programId).stream() + .filter(t -> t.getObservationVariableDbId().equals(observationVariableDbId)) + .findFirst().orElseThrow(() -> new DoesNotExistException("Trait not found for observationVariableDbId: " + observationVariableDbId)); + + } } From 4d73e2d319283bc267786e7a614eee3df930e697 Mon Sep 17 00:00:00 2001 From: dmeidlin <14339308+dmeidlin@users.noreply.github.com> Date: Fri, 7 Feb 2025 14:50:22 -0500 Subject: [PATCH 073/132] refactor append workflow obs unit validation and create UndefinedDataset class --- .../experiment/ExperimentUtilities.java | 101 ++++++++++++++---- .../experiment/MissingValuesException.java | 12 --- .../factory/action/BrAPIAction.java | 3 +- .../action/WorkflowReadInitialization.java | 3 +- .../factory/data/ProcessedDataFactory.java | 10 ++ .../factory/data/UndefinedDataset.java | 26 +++++ .../entity/ExperimentImportEntity.java | 3 +- .../entity/PendingObservationUnit.java | 25 +---- .../AppendOverwriteIDValidation.java | 53 ++++++++- .../initialize/WorkflowInitialization.java | 9 +- .../process/ImportTableProcess.java | 62 +++++++---- .../model/EntityNotFoundException.java | 11 ++ .../model/ExpImportProcessConstants.java | 10 +- .../service/ObservationUnitService.java | 15 ++- 14 files changed, 251 insertions(+), 92 deletions(-) delete mode 100644 src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/MissingValuesException.java create mode 100644 src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/data/UndefinedDataset.java create mode 100644 src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/EntityNotFoundException.java diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentUtilities.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentUtilities.java index 8c3160e5d..4c79478e8 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentUtilities.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/ExperimentUtilities.java @@ -44,6 +44,7 @@ import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddlewareContext; import org.breedinginsight.brapps.importer.services.processors.experiment.create.model.PendingData; import org.breedinginsight.brapps.importer.services.processors.experiment.create.model.ProcessedPhenotypeData; +import org.breedinginsight.brapps.importer.services.processors.experiment.model.EntityNotFoundException; import org.breedinginsight.brapps.importer.services.processors.experiment.model.ExpImportProcessConstants; import org.breedinginsight.model.Program; import org.breedinginsight.model.Scale; @@ -293,44 +294,104 @@ public static void addYearToStudyAdditionalInfo(Program program, BrAPIStudy stud } /** - * This method is responsible for collating unique ObsUnit IDs from the provided context data. + * Collates unique Observation Unit IDs from the import context. * - * @param context the AppendOverwriteMiddlewareContext containing the import rows to process - * @return a Set of unique ObsUnit IDs collated from the import rows - * @throws IllegalStateException if any ObsUnit ID is repeated in the import rows - * @throws HttpStatusException if there is a mix of ObsUnit IDs for some but not all rows + * This method iterates through all import rows in the given context and + * extracts unique Observation Unit IDs (ObsUnit IDs) that are not null or blank. + * + * @param context The AppendOverwriteMiddlewareContext containing the import data. + * @return A Set of String containing all unique, non-null, non-blank Observation Unit IDs. + * + * @implNote The method performs the following steps: + * 1. Initializes an empty HashSet to store unique ObsUnit IDs. + * 2. Iterates through each import row in the context. + * 3. For each row, checks if the ObsUnit ID is not null and not blank. + * 4. If valid, adds the ObsUnit ID to the set. + * 5. Returns the set of unique ObsUnit IDs. */ - public static Set collateReferenceOUIds(AppendOverwriteMiddlewareContext context) throws HttpStatusException, IllegalStateException { + public static Set collateUniqueOUIds(AppendOverwriteMiddlewareContext context) { // Initialize variables to track the presence of ObsUnit IDs Set referenceOUIds = new HashSet<>(); - boolean hasNoReferenceUnitIds = true; - boolean hasAllReferenceUnitIds = true; + + // Iterate through the import rows to process ObsUnit IDs + for (int rowNum = 0; rowNum < context.getImportContext().getImportRows().size(); rowNum++) { + ExperimentObservation importRow = (ExperimentObservation) context.getImportContext().getImportRows().get(rowNum); + if (importRow.getObsUnitID() != null && !importRow.getObsUnitID().isBlank()) { + referenceOUIds.add(importRow.getObsUnitID()); + } + } + return referenceOUIds; + } + + /** + * Validates Observation Unit ID values in the import context. + * + * This method checks each import row for the validity of its Observation Unit ID (ObsUnitID). + * It performs the following validations: + * 1. Checks if the ObsUnitID is null or blank. + * 2. Checks if the ObsUnitID is a duplicate within the import data. + * + * @param context The AppendOverwriteMiddlewareContext containing import data and validation error storage. + * @throws HttpStatusException If there's an HTTP-related error during the validation process. + * @throws IllegalStateException If the system encounters an unexpected state during validation. + * + * @implNote The method performs the following steps: + * 1. Retrieves the ValidationErrors object from the context. + * 2. Initializes a HashSet to track unique ObsUnitIDs. + * 3. Iterates through each import row in the context. + * 4. For each row: + * - If ObsUnitID is null or blank, adds a "missing ObsUnitID" error. + * - If ObsUnitID is already in the set (duplicate), adds a "duplicate ObsUnitID" error. + * - Otherwise, adds the ObsUnitID to the set of unique IDs. + * 5. Errors are added using the addRowError method, specifying the OBS_UNIT_ID column and appropriate error messages. + */ + public static void validateReferenceOUIdValues(AppendOverwriteMiddlewareContext context) throws HttpStatusException, IllegalStateException { + ValidationErrors validationErrors = context.getAppendOverwriteWorkflowContext().getValidationErrors(); + Set referenceOUIds = new HashSet<>(); // Iterate through the import rows to process ObsUnit IDs for (int rowNum = 0; rowNum < context.getImportContext().getImportRows().size(); rowNum++) { ExperimentObservation importRow = (ExperimentObservation) context.getImportContext().getImportRows().get(rowNum); - // Check if ObsUnitID is blank if (importRow.getObsUnitID() == null || importRow.getObsUnitID().isBlank()) { - // Set flag to indicate missing ObsUnit ID for current row - hasAllReferenceUnitIds = false; + // Check if ObsUnitID is blank + addRowError(ExperimentObservation.Columns.OBS_UNIT_ID, ExpImportProcessConstants.ErrMessage.MISSING_OBS_UNIT_ID.getValue(), validationErrors, rowNum); } else if (referenceOUIds.contains(importRow.getObsUnitID())) { - // Throw exception if ObsUnitID is repeated - throw new IllegalStateException("ObsUnitId is repeated: " + importRow.getObsUnitID()); + // Check if ObsUnitID is repeated + addRowError(ExperimentObservation.Columns.OBS_UNIT_ID, ExpImportProcessConstants.ErrMessage.DUPLICATE_OBS_UNIT_ID.getValue(), validationErrors, rowNum); } else { // Add ObsUnitID to referenceOUIds referenceOUIds.add(importRow.getObsUnitID()); - // Set flag to indicate presence of ObsUnit ID - hasNoReferenceUnitIds = false; } } + } - if (!hasNoReferenceUnitIds && !hasAllReferenceUnitIds) { - // Throw exception if there is a mix of ObsUnit IDs for some but not all rows - throw new HttpStatusException(HttpStatus.UNPROCESSABLE_ENTITY, ExpImportProcessConstants.ErrMessage.MISSING_OBS_UNIT_ID_ERROR.getValue()); - } + /** + * Adds validation errors for observation units that were not found in the database. + * + * This method processes an EntityNotFoundException and adds corresponding validation errors + * to the context for each import row where the Observation Unit ID was not found. + * + * @param e The EntityNotFoundException containing information about missing Observation Unit IDs. + * @param context The AppendOverwriteMiddlewareContext containing import data and validation error storage. + * + * @implNote The method performs the following steps: + * 1. Retrieves the ValidationErrors object from the context. + * 2. Iterates through each import row in the context. + * 3. For each row, checks if its Observation Unit ID is in the set of missing entity IDs from the exception. + * 4. If a match is found, adds a validation error for that row, indicating an invalid Observation Unit ID. + * 5. The error is added using the addRowError method, specifying the OBS_UNIT_ID column and using a predefined error message. + */ + public static void addValidationErrorsForObsUnitsNotFound(EntityNotFoundException e, AppendOverwriteMiddlewareContext context) { + ValidationErrors validationErrors = context.getAppendOverwriteWorkflowContext().getValidationErrors(); + List errors = new ArrayList<>(); - return referenceOUIds; + for (int rowNum = 0; rowNum < context.getImportContext().getImportRows().size(); rowNum++) { + String rowObsUnitId = ((ExperimentObservation)context.getImportContext().getImportRows().get(rowNum)).getObsUnitID(); + if (e.getMissingEntityIds().contains(rowObsUnitId)) { + addRowError(ExperimentObservation.Columns.OBS_UNIT_ID, ExperimentUtilities.INVALID_OBS_UNIT_ID_ERROR, validationErrors, rowNum); + } + } } /** diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/MissingValuesException.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/MissingValuesException.java deleted file mode 100644 index 61e7fc299..000000000 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/MissingValuesException.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.breedinginsight.brapps.importer.services.processors.experiment; - -import lombok.Getter; - -import java.util.Set; - -@Getter -public class MissingValuesException extends Exception { - private Set missingIds; - - public MissingValuesException(Set missingIds) { this.missingIds = missingIds; } -} diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/BrAPIAction.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/BrAPIAction.java index 436ad2938..2702f9c7a 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/BrAPIAction.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/BrAPIAction.java @@ -20,6 +20,7 @@ import org.brapi.client.v2.model.exceptions.ApiException; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.factory.BrAPIState; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.factory.entity.ExperimentImportEntity; +import org.breedinginsight.brapps.importer.services.processors.experiment.model.EntityNotFoundException; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.services.exceptions.MissingRequiredInfoException; import org.breedinginsight.services.exceptions.UnprocessableEntityException; @@ -43,7 +44,7 @@ public interface BrAPIAction { * @return An Optional containing the relevant BrAPI state after executing the action. * @throws ApiException if an error occurs during the execution of the action. */ - Optional> execute() throws ApiException, MissingRequiredInfoException, UnprocessableEntityException, DoesNotExistException, ValidatorException; + Optional> execute() throws ApiException, MissingRequiredInfoException, UnprocessableEntityException, DoesNotExistException, EntityNotFoundException; /** * Get the BrAPI entity being acted on based on the provided ExpUnitMiddlewareContext. diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/WorkflowReadInitialization.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/WorkflowReadInitialization.java index 4e1f62142..198c9c1ba 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/WorkflowReadInitialization.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/action/WorkflowReadInitialization.java @@ -23,6 +23,7 @@ import org.brapi.client.v2.model.exceptions.ApiException; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.factory.BrAPIState; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.factory.entity.ExperimentImportEntity; +import org.breedinginsight.brapps.importer.services.processors.experiment.model.EntityNotFoundException; import org.breedinginsight.services.exceptions.ValidatorException; import org.breedinginsight.utilities.Utilities; @@ -45,7 +46,7 @@ protected WorkflowReadInitialization(ExperimentImportEntity entity) { * @return an Optional containing the BrAPIState representing the completed read workflow * @throws ApiException if an error occurs during execution */ - public Optional> execute() throws ApiException, ValidatorException { + public Optional> execute() throws ApiException, EntityNotFoundException { try { List fetchedMembers = entity.brapiRead(); entity.initializeWorkflow(fetchedMembers); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/data/ProcessedDataFactory.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/data/ProcessedDataFactory.java index 84f8bcf75..6775e6d03 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/data/ProcessedDataFactory.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/data/ProcessedDataFactory.java @@ -112,6 +112,10 @@ public static EmptyData emptyData(String brapiReferenceSource, return new EmptyData(brapiReferenceSource, isCommit, germplasmName, study, phenoColumnName, trialId, studyId, unitId, studyYear, observationUnit, user, program, studyService, observationService); } + public static UndefinedDataset undefinedDataset() { + return new UndefinedDataset(); + } + @Bean @Prototype public InitialData initialDataBean(String brapiReferenceSource, @@ -173,5 +177,11 @@ public EmptyData emptyDataBean(String brapiReferenceSource, Program program) { return emptyData(brapiReferenceSource, isCommit, germplasmName, study, phenoColumnName, trialId, studyId, unitId, studyYear, observationUnit, user, program, studyService, observationService); } + + @Bean + @Prototype + public UndefinedDataset undefinedDatasetBean() { + return undefinedDataset(); + } } diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/data/UndefinedDataset.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/data/UndefinedDataset.java new file mode 100644 index 000000000..85c739a60 --- /dev/null +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/data/UndefinedDataset.java @@ -0,0 +1,26 @@ +package org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.factory.data; + +import org.brapi.v2.model.pheno.BrAPIObservation; +import org.breedinginsight.api.model.v1.response.ValidationError; +import org.breedinginsight.brapps.importer.model.response.PendingImportObject; +import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.middleware.process.AppendStatistic; + +import java.util.List; +import java.util.Optional; + +public class UndefinedDataset extends VisitedObservationData { + @Override + public Optional> getValidationErrors() { + return Optional.empty(); + } + + @Override + public PendingImportObject constructPendingObservation() { + return null; + } + + @Override + public void updateTally(AppendStatistic statistic) { + + } +} diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/ExperimentImportEntity.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/ExperimentImportEntity.java index 3bb33d78f..e4b2a4aa2 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/ExperimentImportEntity.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/ExperimentImportEntity.java @@ -19,6 +19,7 @@ import org.brapi.client.v2.model.exceptions.ApiException; import org.breedinginsight.brapps.importer.model.response.ImportObjectState; +import org.breedinginsight.brapps.importer.services.processors.experiment.model.EntityNotFoundException; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.services.exceptions.MissingRequiredInfoException; import org.breedinginsight.services.exceptions.UnprocessableEntityException; @@ -44,7 +45,7 @@ public interface ExperimentImportEntity { * @return List of fetched entities * @throws ApiException if there is an issue with the API call */ - public List brapiRead() throws ApiException, ValidatorException; + public List brapiRead() throws ApiException, EntityNotFoundException; /** * Commit objects changed by the workflow to the BrAPI service. diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java index f4e458520..f9ec45a2b 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/entity/PendingObservationUnit.java @@ -21,14 +21,12 @@ import org.apache.commons.lang3.StringUtils; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.v2.model.pheno.BrAPIObservationUnit; -import org.breedinginsight.api.model.v1.response.ValidationErrors; import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; import org.breedinginsight.brapi.v2.dao.BrAPIObservationUnitDAO; -import org.breedinginsight.brapps.importer.model.imports.experimentObservation.ExperimentObservation; import org.breedinginsight.brapps.importer.model.response.ImportObjectState; import org.breedinginsight.brapps.importer.model.response.PendingImportObject; import org.breedinginsight.brapps.importer.services.processors.experiment.ExperimentUtilities; -import org.breedinginsight.brapps.importer.services.processors.experiment.MissingValuesException; +import org.breedinginsight.brapps.importer.services.processors.experiment.model.EntityNotFoundException; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteWorkflowContext; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddlewareContext; import org.breedinginsight.brapps.importer.services.processors.experiment.model.ImportContext; @@ -36,7 +34,6 @@ import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.services.exceptions.MissingRequiredInfoException; import org.breedinginsight.services.exceptions.UnprocessableEntityException; -import org.breedinginsight.services.exceptions.ValidatorException; import org.breedinginsight.utilities.Utilities; import java.util.*; @@ -98,26 +95,12 @@ public List brapiPost(List members) * @throws ApiException if there is an issue with the API call */ @Override - public List brapiRead() throws ApiException, ValidatorException { + public List brapiRead() throws ApiException, EntityNotFoundException { // Collect deltabreed-generated obs unit ids listed in the import Set obsUnitIds = cache.getReferenceOUIds(); - try { - // For each id fetch the observation unit from the brapi data store - return observationUnitService.getObservationUnitsByDbId(new HashSet<>(obsUnitIds), importContext.getProgram()); - } - catch (MissingValuesException e) { - ValidationErrors validationErrors = new ValidationErrors(); - - // Build a detailed tabular error. - for (int rowNum = 0; rowNum < importContext.getImportRows().size(); rowNum++) { - String rowObsUnitId = ((ExperimentObservation)importContext.getImportRows().get(rowNum)).getObsUnitID(); - if (e.getMissingIds().contains(rowObsUnitId)) { - ExperimentUtilities.addRowError(ExperimentObservation.Columns.OBS_UNIT_ID, ExperimentUtilities.INVALID_OBS_UNIT_ID_ERROR, validationErrors, rowNum); - } - } - throw new ValidatorException(validationErrors); - } + // For each id fetch the observation unit from the brapi data store + return observationUnitService.getObservationUnitsById(new HashSet<>(obsUnitIds), importContext.getProgram()); } /** diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java index 64f471b88..a2edbdf65 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/AppendOverwriteIDValidation.java @@ -18,25 +18,70 @@ package org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.middleware; import io.micronaut.context.annotation.Prototype; -import io.micronaut.http.exceptions.HttpStatusException; import lombok.extern.slf4j.Slf4j; +import org.brapi.client.v2.model.exceptions.ApiException; +import org.brapi.v2.model.pheno.BrAPIObservationUnit; +import org.breedinginsight.api.model.v1.response.ValidationErrors; import org.breedinginsight.brapps.importer.services.processors.experiment.ExperimentUtilities; +import org.breedinginsight.brapps.importer.services.processors.experiment.model.EntityNotFoundException; +import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.factory.action.BrAPIReadFactory; +import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.factory.action.WorkflowReadInitialization; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddlewareContext; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddleware; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.MiddlewareException; +import org.breedinginsight.services.exceptions.ValidatorException; + +import javax.inject.Inject; +import java.util.Optional; +import java.util.Set; @Slf4j @Prototype public class AppendOverwriteIDValidation extends AppendOverwriteMiddleware { + WorkflowReadInitialization brAPIObservationUnitReadWorkflowInitialization; + BrAPIReadFactory brAPIReadFactory; + + @Inject + public AppendOverwriteIDValidation(BrAPIReadFactory brAPIReadFactory) { + this.brAPIReadFactory = brAPIReadFactory; + } @Override public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { + brAPIObservationUnitReadWorkflowInitialization = brAPIReadFactory.observationUnitWorkflowReadInitializationBean(context); + // Initialize the validation error collection + Optional.ofNullable(context.getAppendOverwriteWorkflowContext().getValidationErrors()).orElseGet(() -> { + context.getAppendOverwriteWorkflowContext().setValidationErrors(new ValidationErrors()); + return new ValidationErrors(); + }); + ValidationErrors validationErrors = context.getAppendOverwriteWorkflowContext().getValidationErrors(); + ExperimentUtilities.validateReferenceOUIdValues(context); // Check for missing or duplicate OU ids + Set uniqueOUIds = ExperimentUtilities.collateUniqueOUIds(context); + context.getAppendOverwriteWorkflowContext().setReferenceOUIds(uniqueOUIds); try { - context.getAppendOverwriteWorkflowContext().setReferenceOUIds(ExperimentUtilities.collateReferenceOUIds(context)); - } catch (HttpStatusException | IllegalStateException e) { + brAPIObservationUnitReadWorkflowInitialization.execute(); // Fetch the obs units from the BrAPi service + if (validationErrors.hasErrors()) { + throw new ValidatorException(validationErrors); + } + return processNext(context); + } catch (EntityNotFoundException e) { + /** + * Return an error response with a list of rows where the unique OU id was not found in the BrAPI service in + * addition to rows where there are missing or duplicate OU ids + */ + ExperimentUtilities.addValidationErrorsForObsUnitsNotFound(e, context); + context.getAppendOverwriteWorkflowContext().setProcessError(new MiddlewareException(new ValidatorException(validationErrors))); + return this.compensate(context); + } catch (ApiException | ValidatorException e) { + /** + * If OUs were fetched for all unique reference ids but some of the reference ids failed validation, + * return an error response and a list of rows with duplicate or missing ids + * + * Return an error response if there was a problem connecting to the BrAPI service + */ context.getAppendOverwriteWorkflowContext().setProcessError(new MiddlewareException(e)); return this.compensate(context); } - return processNext(context); + } } diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java index 40e0ea301..2aa889fc2 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/initialize/WorkflowInitialization.java @@ -30,6 +30,7 @@ import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddleware; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.AppendOverwriteMiddlewareContext; import org.breedinginsight.brapps.importer.services.processors.experiment.appendoverwrite.model.MiddlewareException; +import org.breedinginsight.brapps.importer.services.processors.experiment.model.EntityNotFoundException; import org.breedinginsight.model.ProgramLocation; import org.breedinginsight.services.exceptions.ValidatorException; @@ -38,7 +39,6 @@ @Slf4j @Prototype public class WorkflowInitialization extends AppendOverwriteMiddleware { - WorkflowReadInitialization brAPIObservationUnitReadWorkflowInitialization; WorkflowReadInitialization brAPITrialReadWorkflowInitialization; WorkflowReadInitialization brAPIStudyReadWorkflowInitialization; WorkflowReadInitialization locationReadWorkflowInitialization; @@ -52,7 +52,6 @@ public WorkflowInitialization(BrAPIReadFactory brAPIReadFactory) { } @Override public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext context) { - brAPIObservationUnitReadWorkflowInitialization = brAPIReadFactory.observationUnitWorkflowReadInitializationBean(context); brAPITrialReadWorkflowInitialization = brAPIReadFactory.trialWorkflowReadInitializationBean(context); brAPIStudyReadWorkflowInitialization = brAPIReadFactory.studyWorkflowReadInitializationBean(context); locationReadWorkflowInitialization = brAPIReadFactory.locationWorkflowReadInitializationBean(context); @@ -61,15 +60,17 @@ public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext log.debug("reading required BrAPI data from BrAPI service"); try { - brAPIObservationUnitReadWorkflowInitialization.execute(); brAPITrialReadWorkflowInitialization.execute(); brAPIStudyReadWorkflowInitialization.execute(); locationReadWorkflowInitialization.execute(); brAPIDatasetReadWorkflowInitialization.execute(); brAPIGermplasmReadWorkflowInitialization.execute(); - } catch (ApiException | ValidatorException e) { + } catch (ApiException e) { context.getAppendOverwriteWorkflowContext().setProcessError(new MiddlewareException(e)); return this.compensate(context); + } catch (EntityNotFoundException e) { + // TODO: handle edge cases of missing brapi entities as needed + return this.compensate(context); } return processNext(context); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/process/ImportTableProcess.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/process/ImportTableProcess.java index 914633769..770576b67 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/process/ImportTableProcess.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/middleware/process/ImportTableProcess.java @@ -225,6 +225,7 @@ public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext for (int i = 0; i < context.getImportContext().getImportRows().size(); i++) { Integer rowNum = i; ExperimentObservation row = (ExperimentObservation) context.getImportContext().getImportRows().get(rowNum); + VisitedObservationData processedData = null; // Construct the pending import for the row Optional.ofNullable(context.getImportContext().getMappedBrAPIImport()).orElseGet(() -> { @@ -233,18 +234,28 @@ public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext }); PendingImport mappedImportRow = context.getImportContext().getMappedBrAPIImport().getOrDefault(rowNum, new PendingImport()); String unitId = row.getObsUnitID(); + String studyName = context.getAppendOverwriteWorkflowContext().getPendingStudyByOUId().get(unitId).getBrAPIObject().getStudyName(); mappedImportRow.setTrial(context.getAppendOverwriteWorkflowContext().getPendingTrialByOUId().get(unitId)); mappedImportRow.setLocation(context.getAppendOverwriteWorkflowContext().getPendingLocationByOUId().get(unitId)); mappedImportRow.setStudy(context.getAppendOverwriteWorkflowContext().getPendingStudyByOUId().get(unitId)); mappedImportRow.setObservationUnit(context.getAppendOverwriteWorkflowContext().getPendingObsUnitByOUId().get(unitId)); mappedImportRow.setGermplasm(context.getAppendOverwriteWorkflowContext().getPendingGermplasmByOUId().get(unitId)); + /** + * Handle the edge case where a user imports with the append/overwrite workflow for an experiment + * without a dataset defined (i.e. no observation variables headers) and the import does not + * actually have new data to append + */ + if (phenotypeCols.isEmpty()) { + processedData = processedDataFactory.undefinedDatasetBean(); + updatePreviewStatistics(processedData, context, studyName, unitId); + } + // Assemble the pending observation data for all phenotypes for (Column column : phenotypeCols) { String cellData = column.getString(rowNum); // Generate hash for looking up prior observation data - String studyName = context.getAppendOverwriteWorkflowContext().getPendingStudyByOUId().get(unitId).getBrAPIObject().getStudyName(); String unitName = context.getAppendOverwriteWorkflowContext().getPendingObsUnitByOUId().get(unitId).getBrAPIObject().getObservationUnitName(); String phenoColumnName = column.name(); String observationHash = observationService.getObservationHash(unitName, phenoColumnName, studyName); @@ -266,8 +277,6 @@ public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext } - VisitedObservationData processedData = null; - // Is there prior observation data for this unit + var? if (observationByObsHash.containsKey(observationHash)) { @@ -275,9 +284,7 @@ public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext BrAPIObservation observation = gson.fromJson(gson.toJson(observationByObsHash.get(observationHash)), BrAPIObservation.class); // Is there a change to the prior data? - if ( - isChanged(cellData, observation, cell.timestamp) - ) { + if (isChanged(cellData, observation, cell.timestamp)) { // Is prior data protected? /** @@ -356,13 +363,7 @@ public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext processedData.getValidationErrors().ifPresent(errList -> errList.forEach(e -> validationErrors.addError(rowNum + 2, e))); // +2 to account for header row and excel file 1-based row index // Update import preview statistics and set in the context - processedData.updateTally(statistic); - statistic.addEnvironmentName(studyName); - // TODO: change null values to actual data - // TODO: change signature to take two args, studyName and unitName - statistic.addObservationUnitId(null); - statistic.addGid(context.getAppendOverwriteWorkflowContext().getPendingGermplasmByOUId().get(unitId).getBrAPIObject().getAccessionNumber()); - context.getAppendOverwriteWorkflowContext().setStatistic(statistic); + updatePreviewStatistics(processedData, context, studyName, unitId); // Construct a pending observation Optional> pendingProcessedData = Optional.ofNullable(processedData.constructPendingObservation()); @@ -386,11 +387,6 @@ public AppendOverwriteMiddlewareContext process(AppendOverwriteMiddlewareContext // Add the pending observation map to the context for use in processing the import context.getAppendOverwriteWorkflowContext().setPendingObservationByHash(pendingObservationByHash); - // Make sure the workflow statistic is not null. - if (context.getAppendOverwriteWorkflowContext().getStatistic() == null) { - context.getAppendOverwriteWorkflowContext().setStatistic(statistic); - } - return processNext(context); } catch (DoesNotExistException | ApiException | UnprocessableEntityException | ValidatorException | IllegalStateException e) { context.getAppendOverwriteWorkflowContext().setProcessError(new MiddlewareException(e)); @@ -407,4 +403,34 @@ private boolean isChanged(String cellData, BrAPIObservation observation, String } return !observationService.parseDateTime(newTimestamp).equals(observation.getObservationTimeStamp()); } + + /** + * Updates the preview statistics for processed observation data. + * + * This method updates various statistical metrics related to the processed + * observation data and stores them in the provided context. + * + * @param processedData The VisitedObservationData object containing the processed observation data. + * @param context The AppendOverwriteMiddlewareContext object where the updated statistics will be stored. + * @param studyName The name of the study associated with the observation data. + * @param unitId The identifier of the observation unit. + * + * @implNote This method performs the following operations: + * 1. Updates the tally in the processedData object. + * 2. Adds the study name to the statistics. + * 3. Adds the observation unit ID to the statistics. + * 4. Adds the germplasm ID (GID) to the statistics, retrieved from the pending germplasm data in the context. + * 5. Sets the updated statistics in the context. + */ + private void updatePreviewStatistics(VisitedObservationData processedData, + AppendOverwriteMiddlewareContext context, + String studyName, + String unitId) { + // Update import preview statistics and set in the context + processedData.updateTally(statistic); + statistic.addEnvironmentName(studyName); + statistic.addObservationUnitId(unitId); + statistic.addGid(context.getAppendOverwriteWorkflowContext().getPendingGermplasmByOUId().get(unitId).getBrAPIObject().getAccessionNumber()); + context.getAppendOverwriteWorkflowContext().setStatistic(statistic); + } } diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/EntityNotFoundException.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/EntityNotFoundException.java new file mode 100644 index 000000000..3872e73db --- /dev/null +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/EntityNotFoundException.java @@ -0,0 +1,11 @@ +package org.breedinginsight.brapps.importer.services.processors.experiment.model; + +import lombok.Getter; + +import java.util.Set; +@Getter +public class EntityNotFoundException extends Throwable { + private Set missingEntityIds; + + public EntityNotFoundException(Set missingEntityIds) { this.missingEntityIds = missingEntityIds; } +} diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/ExpImportProcessConstants.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/ExpImportProcessConstants.java index b4f7caf90..fa400d133 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/ExpImportProcessConstants.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/ExpImportProcessConstants.java @@ -2,11 +2,8 @@ import com.fasterxml.jackson.annotation.JsonValue; import io.micronaut.context.annotation.Property; -import io.micronaut.context.annotation.Value; import lombok.extern.slf4j.Slf4j; -import javax.annotation.PostConstruct; - @Slf4j public class ExpImportProcessConstants { @@ -18,8 +15,11 @@ public class ExpImportProcessConstants { public enum ErrMessage { MULTIPLE_EXP_TITLES("File contains more than one Experiment Title"), - MISSING_OBS_UNIT_ID_ERROR("Required field is blank"), - PREEXISTING_EXPERIMENT_TITLE("Experiment Title already exists"); + MISSING_OBS_UNIT_ID("Invalid ObsUnitID"), + PREEXISTING_EXPERIMENT_TITLE("Experiment Title already exists"), + UNMATCHED_COLUMN("Ontology term(s) not found: "), + OBS_UNIT_NOT_FOUND("Invalid ObsUnitID"), + DUPLICATE_OBS_UNIT_ID("ObsUnitId is repeated"); private String value; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java index f34808f7f..d66507558 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/ObservationUnitService.java @@ -26,7 +26,7 @@ import org.breedinginsight.brapps.importer.model.response.PendingImportObject; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.brapps.importer.services.processors.experiment.ExperimentUtilities; -import org.breedinginsight.brapps.importer.services.processors.experiment.MissingValuesException; +import org.breedinginsight.brapps.importer.services.processors.experiment.model.EntityNotFoundException; import org.breedinginsight.model.Program; import org.breedinginsight.utilities.Utilities; @@ -59,21 +59,26 @@ public ObservationUnitService(BrAPIObservationUnitDAO brAPIObservationUnitDAO) { * @throws ApiException if an error occurs during the retrieval of observation units * @throws IllegalStateException if the retrieved observation units do not match the provided observation unit IDs */ - public List getObservationUnitsByDbId(Set obsUnitIds, Program program) throws ApiException, IllegalStateException, MissingValuesException { + public List getObservationUnitsById(Set obsUnitIds, Program program) throws ApiException, IllegalStateException, EntityNotFoundException { List brapiUnits = null; // Retrieve reference Observation Units based on IDs brapiUnits = brAPIObservationUnitDAO.getObservationUnitsById(obsUnitIds, program); - // If no BrAPI units are found, throw an IllegalStateException with an error message + // If no BrAPI units are found, throw an EntityNotFoundException with an error message if (obsUnitIds.size() != brapiUnits.size()) { Set missingIds = new HashSet<>(obsUnitIds); // Calculate missing IDs based on retrieved BrAPI units - missingIds.removeAll(brapiUnits.stream().map(BrAPIObservationUnit::getObservationUnitDbId).collect(Collectors.toSet())); + //missingIds.removeAll(brapiUnits.stream().map(BrAPIObservationUnit::getObservationUnitDbId).collect(Collectors.toSet())); + missingIds.removeAll(brapiUnits.stream() + .map(unit -> Utilities.getExternalReference(unit.getExternalReferences(), BRAPI_REFERENCE_SOURCE, ExternalReferenceSource.OBSERVATION_UNITS)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(BrAPIExternalReference::getReferenceId).collect(Collectors.toSet())); // Throw exception with missing IDs information - throw new MissingValuesException(missingIds); + throw new EntityNotFoundException(missingIds); } return brapiUnits; From 530967651d0d4c52be047fd6956025905948b4c8 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Mon, 10 Feb 2025 15:06:54 -0500 Subject: [PATCH 074/132] Add program admin permission --- .../api/v1/controller/geno/SampleSubmissionController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java b/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java index be6377870..539391c73 100644 --- a/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java @@ -293,8 +293,8 @@ public HttpResponse> checkVendorStatus(@PathVariable */ @Delete("programs/{programId}/submissions/{submissionId}") @Produces(MediaType.APPLICATION_JSON) - // only sys admin allowed on post & put so kept same permissions for delete - @ProgramSecured(roles = {ProgramSecuredRole.SYSTEM_ADMIN}) + // sys admin and program admin roles to match file import permissions + @ProgramSecured(roles = {ProgramSecuredRole.SYSTEM_ADMIN, ProgramSecuredRole.PROGRAM_ADMIN}) public HttpResponse deleteSubmissionById(@PathVariable UUID programId, @PathVariable UUID submissionId) throws ApiException { // program validation From 10c6adf645fc16551e452746c68232ce24736fb8 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Tue, 11 Feb 2025 14:58:06 -0500 Subject: [PATCH 075/132] Cleaned up comments --- .../v1/controller/geno/SampleSubmissionController.java | 5 +---- .../brapps/importer/daos/BrAPISampleDAO.java | 4 ++-- .../services/SampleSubmissionService.java | 9 +-------- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java b/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java index 539391c73..6c0e79bc3 100644 --- a/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java @@ -284,8 +284,7 @@ public HttpResponse> checkVendorStatus(@PathVariable /** * Delete sample submission. - * Currently deletes the bidb submission record and BrAPI samples, TODO: delete BrAPI plates once supported - * in BrAPI server, see BI-2431 + * Deletes the bidb submission record and BrAPI samples & plates * @param programId bi-api id of program * @param submissionId bi-api id of submission * @return @@ -311,8 +310,6 @@ public HttpResponse deleteSubmissionById(@PathVariable UUID programId, @PathVari return HttpResponse.notFound(); } SampleSubmission submission = submissionOpt.get(); - // if submission has status of submitted do not allow deletion - // if the user changes the status to not submitted then they can delete if (!submission.isDeletable()) { return HttpResponse.notAllowed(); } diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java index 13f2cc723..372bb63be 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java +++ b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java @@ -133,7 +133,7 @@ public void deleteSamples(Program program, List sampleDbIds) throws ApiE * @throws ApiException */ public void deletePlates(Program program, List plateDbIds) throws ApiException { - // create batch of samples, not yet included in brapi client TODO: switch to brapi client when available + // create batch of plates, not yet included in brapi client TODO: switch to brapi client when available String programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(program.getId()); String batchDbId = postPlatesBatch(programBrAPIBaseUrl, plateDbIds); @@ -178,7 +178,7 @@ private String postBatch(HttpUrl url, RequestBody body, String programBrAPIBaseU return resultObject.get("batchDeleteDbId").getAsString(); } else if (resultObject.has("searchResultsDbId")) { // TODO: once api stuff is in client use BrAPIDAOUtil::search to handle retries, for now just request once - // could be an issue for large number of samples + // brapi server only returns immediate response for batchDeletes so this case won't happen return getBatchDeleteDbIdFromSearchResult(programBrAPIBaseUrl, resultObject.get("searchResultsDbId").getAsString()); } else { throw new InternalServerException("Expected batchDeleteDbId or searchResultsDbId but got " + resultObject); diff --git a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java index e0ccc49fd..c7f4acef1 100644 --- a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java +++ b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java @@ -435,13 +435,7 @@ public Optional updateSubmissionStatus(Program program, UUID s * @exception ApiException if a BrAPI call fails */ public void deleteSampleSubmission(Program program, UUID submissionId) throws ApiException { - - // delete BrAPI data - // delete samples - // delete plates - // create a batch of sampleIds and plateIds to delete - // get samples with the sample submission xref List samples = sampleDAO.readSamplesBySubmissionIds(program, List.of(submissionId.toString())); @@ -449,8 +443,7 @@ public void deleteSampleSubmission(Program program, UUID submissionId) throws Ap List sampleDbIds = samples.stream().map(BrAPISample::getSampleDbId).collect(Collectors.toList()); List platesDbIds = samples.stream().map(BrAPISample::getPlateDbId).collect(Collectors.toList()); - // create batch of samples, not yet included in brapi client TODO: switch to brapi client when available - // TODO: uncomment when brapi server is working + // delete samples and plates BrAPI objects in brapi server sampleDAO.deleteSamples(program, sampleDbIds); sampleDAO.deletePlates(program, platesDbIds); From f44a4f9610d8b934a9068552667c67553bf42f97 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 11 Feb 2025 15:41:44 -0500 Subject: [PATCH 076/132] [BI-2047] - added observations test --- ...ObservationsControllerIntegrationTest.java | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java index b9ba07d43..8c3f058bb 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java @@ -63,6 +63,7 @@ import java.util.*; import static io.micronaut.http.HttpRequest.*; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; @MicronautTest @@ -71,8 +72,10 @@ public class BrAPIObservationsControllerIntegrationTest extends BrAPITest { private Program program; - private String experimentId; + private String experimentId; private List envIds = new ArrayList<>(); + // Use hardcoded values for more deterministic test runs and easier assertions. + private List values = List.of(0.125F, 12.415F); private final List> rows = new ArrayList<>(); private final List columns = ExperimentFileColumns.getOrderedColumns(); private List traits; @@ -170,15 +173,11 @@ void setup() throws Exception { Map row2 = makeExpImportRow("Env2"); // Add test observation data - for (Trait trait : traits) { - Random random = new Random(); - + for (int i = 0; i < traits.size(); i++) { // 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); + int valueIndex = i % values.size(); // Prevent overflow. + row1.put(traits.get(i).getObservationVariableName(), values.get(valueIndex)); } rows.add(row1); @@ -273,7 +272,7 @@ public void testGetObsTableOK() { } @Test - @Disabled("Disabled until fetching of observations is implemented") + @Disabled("Disabled until fetching of observations is implemented in BI-2506.") public void testGetObsListByExpId() { Flowable> call = client.exchange( GET(String.format("/programs/%s/brapi/v2/observations?trialDbId=%s", program.getId(), experimentId)) @@ -286,7 +285,7 @@ public void testGetObsListByExpId() { } @Test - @Disabled("Disabled until fetching of observations is implemented") + @Disabled("Disabled until fetching of observations is implemented in BI-2506.") public void testGetOUById() { Flowable> call = client.exchange( GET(String.format("/programs/%s/brapi/v2/observations?trialDbId=%s", program.getId(), experimentId)) @@ -311,6 +310,31 @@ public void testGetOUById() { assertEquals(HttpStatus.OK, ouResponse.getStatus()); } + @Test + public void testGetObsByStudyDbId() { + // Make a GET request to the /observations endpoint with the studyDbId query parameter. + Flowable> call = client.exchange( + GET(String.format("/programs/%s/brapi/v2/observations?studyDbId=%s", program.getId(), envIds.get(0))) + .bearerAuth("test-registered-user"), + String.class + ); + + // Check for 200 OK response. + HttpResponse response = call.blockingFirst(); + assertEquals(HttpStatus.OK, response.getStatus()); + + // Check that two observations were returned. + JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); + JsonArray observations = responseObj.getAsJsonObject("result").getAsJsonArray("data"); + assertEquals(2, observations.size()); + + // Check the observation values, keep in mind the order of results is not guaranteed. + Float value1 = observations.get(0).getAsJsonObject().get("value").getAsFloat(); + Float value2 = observations.get(1).getAsJsonObject().get("value").getAsFloat(); + assertTrue(values.contains(value1), "Observation with value " + value1 + " not found."); + assertTrue(values.contains(value2), "Observation with value " + value2 + " not found."); + } + private File writeDataToFile(List> data, List traits) throws IOException { File file = File.createTempFile("test", ".csv"); From cfea75b728cc4b86d8ac5941eceac772465db806 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 11 Feb 2025 15:42:25 -0500 Subject: [PATCH 077/132] [BI-2047] - implemented BrAPIObservationsController::observationsGet --- .../brapi/v2/BrAPIObservationsController.java | 77 ++++++++++++++++++- .../brapi/v2/dao/BrAPIObservationDAO.java | 28 ++++++- 2 files changed, 100 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java index cbb476910..f1d5d3841 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java @@ -28,13 +28,17 @@ import org.brapi.client.v2.ApiResponse; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.modules.phenotype.ObservationsApi; +import org.brapi.v2.model.BrAPIIndexPagination; +import org.brapi.v2.model.BrAPIMetadata; +import org.brapi.v2.model.BrAPIStatus; import org.brapi.v2.model.BrAPIWSMIMEDataTypes; import org.brapi.v2.model.core.BrAPIStudy; import org.brapi.v2.model.pheno.BrAPIObservation; -import org.brapi.v2.model.pheno.response.BrAPIObservationTableResponse; +import org.brapi.v2.model.pheno.response.*; import org.breedinginsight.api.auth.ProgramSecured; import org.breedinginsight.api.auth.ProgramSecuredRoleGroup; import org.breedinginsight.brapi.v1.controller.BrapiVersion; +import org.breedinginsight.brapi.v2.dao.BrAPIObservationDAO; import org.breedinginsight.brapi.v2.dao.BrAPIStudyDAO; import org.breedinginsight.brapi.v2.model.request.query.ObservationQuery; import org.breedinginsight.daos.ProgramDAO; @@ -47,6 +51,8 @@ import javax.annotation.Nullable; import javax.inject.Inject; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; @Slf4j @Controller("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2) @@ -57,13 +63,20 @@ public class BrAPIObservationsController { private final ProgramDAO programDAO; private final BrAPIStudyDAO brAPIStudyDAO; private final BrAPIEndpointProvider brAPIEndpointProvider; + private final BrAPIObservationDAO observationDAO; @Inject - public BrAPIObservationsController(ProgramService programService, ProgramDAO programDAO, ProgramDAO programDAO1, BrAPIStudyDAO brAPIStudyDAO, BrAPIEndpointProvider brAPIEndpointProvider) { + public BrAPIObservationsController(ProgramService programService, + ProgramDAO programDAO, + ProgramDAO programDAO1, + BrAPIStudyDAO brAPIStudyDAO, + BrAPIEndpointProvider brAPIEndpointProvider, + BrAPIObservationDAO brAPIObservationDAO) { this.programService = programService; this.programDAO = programDAO1; this.brAPIStudyDAO = brAPIStudyDAO; this.brAPIEndpointProvider = brAPIEndpointProvider; + this.observationDAO = brAPIObservationDAO; } @Get("/observations") @@ -93,8 +106,64 @@ public HttpResponse observationsGet(@PathVariable("programId") UUID programId, @Nullable @QueryValue("externalReferenceSource") String externalReferenceSource, @Nullable @QueryValue("page") Integer page, @Nullable @QueryValue("pageSize") Integer pageSize) { - //TODO - return HttpResponse.notFound(); + log.debug("observationsGet: fetching observations by filters"); + Optional program = programService.getById(programId); + if(program.isEmpty()) { + log.warn("Program id: " + programId + " not found"); + return HttpResponse.notFound(); + } + + try { + + // TODO: BI-2506 - implement support for all query parameters. + List unsupportedParams = Stream.of( + observationDbId, + observationUnitDbId, + observationVariableDbId, + locationDbId, + seasonDbId, + observationTimeStampRangeStart, + observationTimeStampRangeEnd, + observationUnitLevelName, + observationUnitLevelOrder, + observationUnitLevelCode, + observationUnitLevelRelationshipName, + observationUnitLevelRelationshipOrder, + observationUnitLevelRelationshipCode, + observationUnitLevelRelationshipDbId, + commonCropName, + programDbId, + trialDbId, + germplasmDbId, + externalReferenceID, + externalReferenceId, + externalReferenceSource + ).filter(Objects::nonNull).collect(Collectors.toList()); + + if (!unsupportedParams.isEmpty()) { + return HttpResponse.status(HttpStatus.NOT_IMPLEMENTED).body( + new BrAPIObservationListResponse().metadata(new BrAPIMetadata().status(List.of(new BrAPIStatus().messageType(BrAPIStatus.MessageTypeEnum.ERROR) + .message("Unsupported query parameter. Only studyDbId, page, and pageSize are supported.")))) + ); + } + + // Handle studyDbId and pagination query params for Field Book integration. + List observations = observationDAO.getObservationsByStudyIds(List.of(studyDbId), program.get()); + + return HttpResponse.ok(new BrAPIObservationListResponse().metadata(new BrAPIMetadata().pagination(new BrAPIIndexPagination().currentPage(0) + .totalPages(1) + .pageSize(observations.size()) + .totalCount(observations.size()))) + .result(new BrAPIObservationListResponseResult().data(observations))); + } catch (ApiException e) { + log.error(Utilities.generateApiExceptionLogMessage(e), e); + return HttpResponse.serverError(new BrAPIObservationListResponse().metadata(new BrAPIMetadata().status(List.of(new BrAPIStatus().messageType(BrAPIStatus.MessageTypeEnum.ERROR) + .message("Error fetching observations"))))); + } catch (Exception e) { + log.error("Error fetching Observations", e); + return HttpResponse.serverError(new BrAPIObservationListResponse().metadata(new BrAPIMetadata().status(List.of(new BrAPIStatus().messageType(BrAPIStatus.MessageTypeEnum.ERROR) + .message("Error fetching observations"))))); + } } @Get("/observations/{observationDbId}") diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java index fdfbd18a1..df6b37865 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java @@ -38,7 +38,10 @@ import org.breedinginsight.daos.ProgramDAO; import org.breedinginsight.daos.cache.ProgramCacheProvider; import org.breedinginsight.model.Program; +import org.breedinginsight.model.Trait; +import org.breedinginsight.services.TraitService; import org.breedinginsight.services.brapi.BrAPIEndpointProvider; +import org.breedinginsight.services.exceptions.DoesNotExistException; import org.breedinginsight.utilities.BrAPIDAOUtil; import org.breedinginsight.utilities.Utilities; import org.jetbrains.annotations.NotNull; @@ -62,6 +65,7 @@ public class BrAPIObservationDAO extends BrAPICachedDAO { private final BrAPIEndpointProvider brAPIEndpointProvider; private final String referenceSource; private boolean runScheduledTasks; + private final TraitService traitService; @Inject public BrAPIObservationDAO(ProgramDAO programDAO, @@ -71,7 +75,7 @@ public BrAPIObservationDAO(ProgramDAO programDAO, BrAPIEndpointProvider brAPIEndpointProvider, @Property(name = "brapi.server.reference-source") String referenceSource, @Property(name = "micronaut.bi.api.run-scheduled-tasks") boolean runScheduledTasks, - ProgramCacheProvider programCacheProvider) { + ProgramCacheProvider programCacheProvider, TraitService traitService) { this.programDAO = programDAO; this.importDAO = importDAO; this.observationUnitDAO = observationUnitDAO; @@ -79,6 +83,7 @@ public BrAPIObservationDAO(ProgramDAO programDAO, this.brAPIEndpointProvider = brAPIEndpointProvider; this.referenceSource = referenceSource; this.runScheduledTasks = runScheduledTasks; + this.traitService = traitService; this.programCache = programCacheProvider.getProgramCache(this::fetchProgramObservations, BrAPIObservation.class); } @@ -242,6 +247,27 @@ public List getObservationsByObservationUnitsAndStudies(Collec .collect(Collectors.toList()); } + public List getObservationsByStudyIds(Collection studyDbIds, Program program) throws ApiException { + if(studyDbIds.isEmpty()) { + return Collections.emptyList(); + } + String xrefSource = Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.STUDIES); + // Lookup studyDbId + return getProgramObservations(program.getId()).values().stream() + .filter(o -> { + Optional xref = Utilities.getExternalReference(o.getExternalReferences(), xrefSource); + return xref.filter(brAPIExternalReference -> studyDbIds.contains(brAPIExternalReference.getReferenceId())).isPresent(); + }).peek(o -> { + try { + Trait trait = traitService.getByObservationVariableDbId(program.getId(), o.getObservationVariableDbId()); + o.setObservationVariableDbId(trait.getId().toString()); + } catch (DoesNotExistException e) { + throw new RuntimeException(e); + } + + }).collect(Collectors.toList()); + } + @NotNull private ApiResponse, Optional>> searchObservationsSearchResultsDbIdGet(UUID programId, String searchResultsDbId, Integer page, Integer pageSize) throws ApiException { ObservationsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ObservationsApi.class); From 62776129d24dd2a9ab403c2dbdcc29c335738398 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 11 Feb 2025 15:49:51 -0500 Subject: [PATCH 078/132] [BI-2156-qa] - added notice to new files --- .../factory/data/UndefinedDataset.java | 17 +++++++++++++++++ .../model/EntityNotFoundException.java | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/data/UndefinedDataset.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/data/UndefinedDataset.java index 85c739a60..d30d917ec 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/data/UndefinedDataset.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/appendoverwrite/factory/data/UndefinedDataset.java @@ -1,3 +1,20 @@ +/* + * 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.brapps.importer.services.processors.experiment.appendoverwrite.factory.data; import org.brapi.v2.model.pheno.BrAPIObservation; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/EntityNotFoundException.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/EntityNotFoundException.java index 3872e73db..c6a915833 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/EntityNotFoundException.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/EntityNotFoundException.java @@ -1,3 +1,20 @@ +/* + * 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.brapps.importer.services.processors.experiment.model; import lombok.Getter; From 3883e85eac8f623c99091ef9e38a3a6f4429f5c5 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Wed, 12 Feb 2025 13:34:44 -0500 Subject: [PATCH 079/132] [BI-2156-qa] - added notice --- .../model/ExpImportProcessConstants.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/ExpImportProcessConstants.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/ExpImportProcessConstants.java index fa400d133..2c0ae2e53 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/ExpImportProcessConstants.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/model/ExpImportProcessConstants.java @@ -1,3 +1,20 @@ +/* + * 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.brapps.importer.services.processors.experiment.model; import com.fasterxml.jackson.annotation.JsonValue; From c83d037d20b698a5f5cfe837bbe9902d83ee6779 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Wed, 12 Feb 2025 15:06:59 -0500 Subject: [PATCH 080/132] [BI-2047] - improved efficiency --- .../brapi/v2/dao/BrAPIObservationDAO.java | 19 +++++++++---------- .../services/TraitService.java | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java index df6b37865..ab096a0ea 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java @@ -247,25 +247,24 @@ public List getObservationsByObservationUnitsAndStudies(Collec .collect(Collectors.toList()); } - public List getObservationsByStudyIds(Collection studyDbIds, Program program) throws ApiException { + public List getObservationsByStudyIds(Collection studyDbIds, Program program) throws ApiException, DoesNotExistException { if(studyDbIds.isEmpty()) { return Collections.emptyList(); } String xrefSource = Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.STUDIES); + // Get all observations for the program. + Collection observations = getProgramObservations(program.getId()).values(); + // Build a hashmap of traits for fast lookup. The key is ObservationVariableDbId, the value is the Trait Id. + HashMap traitIdsByObservationVariableDbId = traitService.getIdsByObservationVariableDbIds(program.getId(), observations.stream().map(BrAPIObservation::getObservationVariableDbId).collect(Collectors.toList())); + // Lookup studyDbId return getProgramObservations(program.getId()).values().stream() .filter(o -> { Optional xref = Utilities.getExternalReference(o.getExternalReferences(), xrefSource); return xref.filter(brAPIExternalReference -> studyDbIds.contains(brAPIExternalReference.getReferenceId())).isPresent(); - }).peek(o -> { - try { - Trait trait = traitService.getByObservationVariableDbId(program.getId(), o.getObservationVariableDbId()); - o.setObservationVariableDbId(trait.getId().toString()); - } catch (DoesNotExistException e) { - throw new RuntimeException(e); - } - - }).collect(Collectors.toList()); + }) + .peek(o -> o.setObservationVariableDbId(traitIdsByObservationVariableDbId.get(o.getObservationVariableDbId()))) + .collect(Collectors.toList()); } @NotNull diff --git a/src/main/java/org/breedinginsight/services/TraitService.java b/src/main/java/org/breedinginsight/services/TraitService.java index 2bd8eb4b3..cdedac3ba 100644 --- a/src/main/java/org/breedinginsight/services/TraitService.java +++ b/src/main/java/org/breedinginsight/services/TraitService.java @@ -502,4 +502,18 @@ public Trait getByObservationVariableDbId(UUID programId, String observationVari .findFirst().orElseThrow(() -> new DoesNotExistException("Trait not found for observationVariableDbId: " + observationVariableDbId)); } + + public HashMap getIdsByObservationVariableDbIds(UUID programId, List observationVariableDbIds) throws DoesNotExistException { + if (!programService.exists(programId)) { + throw new DoesNotExistException("Program does not exist"); + } + return traitDAO.getTraitsFullByProgramId(programId).stream() + .filter(t -> observationVariableDbIds.contains(t.getObservationVariableDbId())) + .collect(Collectors.toMap( + Trait::getObservationVariableDbId, + (t) -> t.getId().toString(), + (existing, replacement) -> existing, + HashMap::new + )); + } } From c9de0444ac3f921226976b50ddcfbe4e40c5945c Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Thu, 13 Feb 2025 10:57:22 -0500 Subject: [PATCH 081/132] Update src/main/java/org/breedinginsight/services/SampleSubmissionService.java Co-authored-by: mlm483 <128052931+mlm483@users.noreply.github.com> --- .../org/breedinginsight/services/SampleSubmissionService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java index c7f4acef1..3309dd0dc 100644 --- a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java +++ b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java @@ -440,8 +440,8 @@ public void deleteSampleSubmission(Program program, UUID submissionId) throws Ap List samples = sampleDAO.readSamplesBySubmissionIds(program, List.of(submissionId.toString())); // extract sampleDbIds and plateDbIds to include in batches - List sampleDbIds = samples.stream().map(BrAPISample::getSampleDbId).collect(Collectors.toList()); - List platesDbIds = samples.stream().map(BrAPISample::getPlateDbId).collect(Collectors.toList()); + List sampleDbIds = samples.stream().map(BrAPISample::getSampleDbId).distinct().collect(Collectors.toList()); + List platesDbIds = samples.stream().map(BrAPISample::getPlateDbId).distinct().collect(Collectors.toList()); // delete samples and plates BrAPI objects in brapi server sampleDAO.deleteSamples(program, sampleDbIds); From 21ce4be20a75dfcad8c02381f9242b69d1504da4 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Thu, 13 Feb 2025 11:10:48 -0500 Subject: [PATCH 082/132] Added return type to docs --- .../api/v1/controller/geno/SampleSubmissionController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java b/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java index 6c0e79bc3..e01ab2c08 100644 --- a/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java +++ b/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java @@ -287,7 +287,7 @@ public HttpResponse> checkVendorStatus(@PathVariable * Deletes the bidb submission record and BrAPI samples & plates * @param programId bi-api id of program * @param submissionId bi-api id of submission - * @return + * @return HttpResponse * @throws ApiException */ @Delete("programs/{programId}/submissions/{submissionId}") From e57120ceec8556340469d8cb50feffe2f16d81c2 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Thu, 13 Feb 2025 19:35:43 +0000 Subject: [PATCH 083/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 4ef6916c8..2996916c7 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+901 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/9c0fdb5b160215a40e5f2df57a7e922ec0036052 +version=v1.1.0+904 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/eab67a016af348480698361440314fecd593ae20 From e3be5b61313d85c9e243f633b0173da2e585103c Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Fri, 14 Feb 2025 18:23:53 -0500 Subject: [PATCH 084/132] [BI-2047] - implemented pagination --- .../brapi/v2/BrAPIObservationsController.java | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java index f1d5d3841..71a7d9eea 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java @@ -147,14 +147,43 @@ public HttpResponse observationsGet(@PathVariable("programId") UUID programId, ); } - // Handle studyDbId and pagination query params for Field Book integration. - List observations = observationDAO.getObservationsByStudyIds(List.of(studyDbId), program.get()); + // Get a filtered list of observations. + List observations = observationDAO.getObservationsByFilters(program.get(), studyDbId); - return HttpResponse.ok(new BrAPIObservationListResponse().metadata(new BrAPIMetadata().pagination(new BrAPIIndexPagination().currentPage(0) - .totalPages(1) + // Handle pagination query params. + int totalCount = observations.size(); // Total number of records in the unpaged super set. + int actualPage = page != null ? page : 0; // Zero-indexed page. + int requestedPageSize = pageSize != null ? Math.min(pageSize, totalCount) : totalCount; // The lesser of pageSize and totalCount. + int totalPages = totalCount / requestedPageSize + ((totalCount % requestedPageSize == 0) ? 0 : 1); // Integer division and round up. + log.info("(Pagination) totalCount: " + totalCount + " actualPage (0-indexed): " + actualPage + " requestedPageSize: " + requestedPageSize + " totalPages: " + totalPages); + + // Determine validity of pagination query parameters. + boolean pageSizeValid = pageSize != null && pageSize > 0 && pageSize <= totalCount; + boolean pageValid = page != null && page >= 0 && page < totalPages; + + // Only paginate if valid pagination values were sent. + if (pageSizeValid && pageValid) { + int start = actualPage * requestedPageSize; + // Account for last page, which may have fewer than requestedPageSize items, or exactly requestedPageSize items. + int end = (actualPage == (totalPages - 1) && totalCount % requestedPageSize != 0) ? (start + (totalCount % requestedPageSize)) : Math.min(((actualPage + 1) * requestedPageSize), totalCount); + log.info("(Pagination) start " + start + " end " + end); + // Sort observations so that paging is consistent and coherent. + observations.sort(Comparator.comparing(BrAPIObservation::getObservationDbId)); + // Paginate response. + observations = observations.subList(start, end); + } else if (pageSize != null || page != null) { + // If one or more of the pagination query parameters are not null, both must be present and valid. + String errorMessage = "Invalid query parameters: page, pageSize"; + return HttpResponse.badRequest(new BrAPIObservationListResponse().metadata(new BrAPIMetadata().status(List.of(new BrAPIStatus().messageType(BrAPIStatus.MessageTypeEnum.ERROR) + .message(errorMessage))))); + } + + return HttpResponse.ok(new BrAPIObservationListResponse().metadata(new BrAPIMetadata().pagination(new BrAPIIndexPagination().currentPage(actualPage) + .totalPages(totalPages) .pageSize(observations.size()) - .totalCount(observations.size()))) + .totalCount(totalCount))) .result(new BrAPIObservationListResponseResult().data(observations))); + } catch (ApiException e) { log.error(Utilities.generateApiExceptionLogMessage(e), e); return HttpResponse.serverError(new BrAPIObservationListResponse().metadata(new BrAPIMetadata().status(List.of(new BrAPIStatus().messageType(BrAPIStatus.MessageTypeEnum.ERROR) From 123e94fe6ce82d79cef07414324cc056ce7bfb94 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Fri, 14 Feb 2025 18:29:36 -0500 Subject: [PATCH 085/132] [BI-2047] - translated DbIds --- .../brapi/v2/BrAPIObservationsController.java | 2 +- .../brapi/v2/dao/BrAPIObservationDAO.java | 30 +++++++++++++------ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java index 71a7d9eea..fe96f523f 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java @@ -183,7 +183,7 @@ public HttpResponse observationsGet(@PathVariable("programId") UUID programId, .pageSize(observations.size()) .totalCount(totalCount))) .result(new BrAPIObservationListResponseResult().data(observations))); - + } catch (ApiException e) { log.error(Utilities.generateApiExceptionLogMessage(e), e); return HttpResponse.serverError(new BrAPIObservationListResponse().metadata(new BrAPIMetadata().status(List.of(new BrAPIStatus().messageType(BrAPIStatus.MessageTypeEnum.ERROR) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java index ab096a0ea..82f168dc1 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java @@ -247,11 +247,13 @@ public List getObservationsByObservationUnitsAndStudies(Collec .collect(Collectors.toList()); } - public List getObservationsByStudyIds(Collection studyDbIds, Program program) throws ApiException, DoesNotExistException { - if(studyDbIds.isEmpty()) { - return Collections.emptyList(); - } - String xrefSource = Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.STUDIES); + // TODO: implement other filters. + public List getObservationsByFilters(Program program, String studyDbId) throws ApiException, DoesNotExistException { + + String studySource = Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.STUDIES); + String observationUnitSource = Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.OBSERVATION_UNITS); + String observationSource = Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.OBSERVATIONS); + // Get all observations for the program. Collection observations = getProgramObservations(program.getId()).values(); // Build a hashmap of traits for fast lookup. The key is ObservationVariableDbId, the value is the Trait Id. @@ -260,11 +262,21 @@ public List getObservationsByStudyIds(Collection study // Lookup studyDbId return getProgramObservations(program.getId()).values().stream() .filter(o -> { - Optional xref = Utilities.getExternalReference(o.getExternalReferences(), xrefSource); - return xref.filter(brAPIExternalReference -> studyDbIds.contains(brAPIExternalReference.getReferenceId())).isPresent(); + // Short circuit if filter is null. + if (studyDbId == null) return true; + Optional xref = Utilities.getExternalReference(o.getExternalReferences(), studySource); + return xref.filter(brAPIExternalReference -> studyDbId.equals(brAPIExternalReference.getReferenceId())).isPresent(); }) - .peek(o -> o.setObservationVariableDbId(traitIdsByObservationVariableDbId.get(o.getObservationVariableDbId()))) - .collect(Collectors.toList()); + .peek(o -> { + // TODO: avoid NPEs! + // Translate ObservationVariableDbId. + o.setObservationVariableDbId(traitIdsByObservationVariableDbId.get(o.getObservationVariableDbId())); + // Translate ObservationUnitDbId. + o.setObservationUnitDbId(Utilities.getExternalReference(o.getExternalReferences(), observationUnitSource).get().getReferenceId()); + // Translate ObservationId. + o.setObservationDbId(Utilities.getExternalReference(o.getExternalReferences(), observationSource).get().getReferenceId()); + // TODO: do we need to translate germplasmDbId? + }).collect(Collectors.toList()); } @NotNull From 5a0b69e9f66c31f1531c7878979ae1d5a43d12e4 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Mon, 17 Feb 2025 12:46:14 -0500 Subject: [PATCH 086/132] [BI-2047] - handled division by zero edge case --- .../brapi/v2/BrAPIObservationsController.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java index fe96f523f..5f8a4b39c 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java @@ -150,11 +150,14 @@ public HttpResponse observationsGet(@PathVariable("programId") UUID programId, // Get a filtered list of observations. List observations = observationDAO.getObservationsByFilters(program.get(), studyDbId); - // Handle pagination query params. - int totalCount = observations.size(); // Total number of records in the unpaged super set. - int actualPage = page != null ? page : 0; // Zero-indexed page. - int requestedPageSize = pageSize != null ? Math.min(pageSize, totalCount) : totalCount; // The lesser of pageSize and totalCount. - int totalPages = totalCount / requestedPageSize + ((totalCount % requestedPageSize == 0) ? 0 : 1); // Integer division and round up. + // Total number of records in the unpaged super set. + int totalCount = observations.size(); + // Zero-indexed page, default to zero. + int actualPage = page != null ? page : 0; + // The least of pageSize and totalCount, unless pageSize is null or zero, in which case use totalCount. + int requestedPageSize = (pageSize != null && pageSize > 0) ? Math.min(pageSize, totalCount) : totalCount; + // Integer division and round up. + int totalPages = totalCount / requestedPageSize + ((totalCount % requestedPageSize == 0) ? 0 : 1); log.info("(Pagination) totalCount: " + totalCount + " actualPage (0-indexed): " + actualPage + " requestedPageSize: " + requestedPageSize + " totalPages: " + totalPages); // Determine validity of pagination query parameters. From d0ab4c3668f4b1577375c9b7df4dfe8b951e04fd Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Mon, 17 Feb 2025 12:58:58 -0500 Subject: [PATCH 087/132] [BI-2047] - added pagination test --- .../brapi/v2/BrAPIObservationsController.java | 2 +- ...ObservationsControllerIntegrationTest.java | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java index 5f8a4b39c..4c7127528 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java @@ -161,7 +161,7 @@ public HttpResponse observationsGet(@PathVariable("programId") UUID programId, log.info("(Pagination) totalCount: " + totalCount + " actualPage (0-indexed): " + actualPage + " requestedPageSize: " + requestedPageSize + " totalPages: " + totalPages); // Determine validity of pagination query parameters. - boolean pageSizeValid = pageSize != null && pageSize > 0 && pageSize <= totalCount; + boolean pageSizeValid = pageSize != null && pageSize > 0; boolean pageValid = page != null && page >= 0 && page < totalPages; // Only paginate if valid pagination values were sent. diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java index 8c3f058bb..5ed4ac064 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java @@ -178,6 +178,7 @@ void setup() throws Exception { // A float is returned from the backend instead of double. there is a separate card to fix this. int valueIndex = i % values.size(); // Prevent overflow. row1.put(traits.get(i).getObservationVariableName(), values.get(valueIndex)); + row2.put(traits.get(i).getObservationVariableName(), values.get(valueIndex)); } rows.add(row1); @@ -335,6 +336,71 @@ public void testGetObsByStudyDbId() { assertTrue(values.contains(value2), "Observation with value " + value2 + " not found."); } + @Test + public void testGetObsPagination() { + + // Check no pagination. + checkPagination(null, null, HttpStatus.OK, 4, 4, 1); + // Check valid pagination, including last page edge case. + checkPagination(0, 1, HttpStatus.OK, 1, 4, 4); + checkPagination(1, 2, HttpStatus.OK, 2, 4, 2); + checkPagination(0, 3, HttpStatus.OK, 3, 4, 2); + checkPagination(1, 3, HttpStatus.OK, 1, 4, 2); + checkPagination(0, 100, HttpStatus.OK, 4, 4, 1); + // Check invalid pagination. + checkPagination(2, 2, HttpStatus.BAD_REQUEST, null, null, null); + checkPagination(0, 0, HttpStatus.BAD_REQUEST, null, null, null); + checkPagination(1, 0, HttpStatus.BAD_REQUEST, null, null, null); + checkPagination(10, 100, HttpStatus.BAD_REQUEST, null, null, null); + + } + + private void checkPagination(Integer page, Integer pageSize, HttpStatus expectedStatus, Integer expectedSize, Integer expectedTotalCount, Integer expectedTotalPages) { + // Build request URL. + String requestURL = String.format("/programs/%s/brapi/v2/observations", program.getId()); + if (page != null) { + requestURL = requestURL + "?page=" + page; + } else { + page = 0; + } + if (pageSize != null) { + requestURL = requestURL + "&pageSize=" + pageSize; + } + + // Make a GET request to the /observations endpoint with the supplied pagination parameters. + Flowable> call = client.exchange( + GET(requestURL).bearerAuth("test-registered-user"), + String.class + ); + + // Check for expected response. + try { + HttpResponse response = call.blockingFirst(); + assertEquals(expectedStatus, response.getStatus()); + + // If call.blockingFirst() doesn't throw, expect a 200 OK. + assertEquals(HttpStatus.OK, response.getStatus()); + + // Parse and check body and metadata. + JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); + // Get metadata. + JsonObject pagination = responseObj.getAsJsonObject("metadata").getAsJsonObject("pagination"); + // TODO: Per BrAPI docs, pageSize (metadata) should always be the + assertEquals(expectedSize, pagination.get("pageSize").getAsInt()); // TODO: check... BJTS does something different. + assertEquals(page, pagination.get("currentPage").getAsInt()); + assertEquals(expectedTotalPages, pagination.get("totalPages").getAsInt()); + assertEquals(expectedTotalCount, pagination.get("totalCount").getAsInt()); + // Get observations. + JsonArray observations = responseObj.getAsJsonObject("result").getAsJsonArray("data"); + assertEquals(expectedSize, observations.size()); + + } catch (HttpClientResponseException e) { + // call.blockingFirst() will throw in the case of a non-200 code. + assertEquals(expectedStatus, e.getStatus()); + } + + } + private File writeDataToFile(List> data, List traits) throws IOException { File file = File.createTempFile("test", ".csv"); From 618d8a7358a1ec4708bb1c145e23f78e885509a2 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Mon, 17 Feb 2025 13:10:51 -0500 Subject: [PATCH 088/132] [BI-2047] - updated comments --- .../brapi/v2/BrAPIObservationsControllerIntegrationTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java index 5ed4ac064..12269df3e 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java @@ -385,8 +385,7 @@ private void checkPagination(Integer page, Integer pageSize, HttpStatus expected JsonObject responseObj = gson.fromJson(response.body(), JsonObject.class); // Get metadata. JsonObject pagination = responseObj.getAsJsonObject("metadata").getAsJsonObject("pagination"); - // TODO: Per BrAPI docs, pageSize (metadata) should always be the - assertEquals(expectedSize, pagination.get("pageSize").getAsInt()); // TODO: check... BJTS does something different. + assertEquals(expectedSize, pagination.get("pageSize").getAsInt()); assertEquals(page, pagination.get("currentPage").getAsInt()); assertEquals(expectedTotalPages, pagination.get("totalPages").getAsInt()); assertEquals(expectedTotalCount, pagination.get("totalCount").getAsInt()); From 9ea73d45767489a2f50ce3b5e350e5bd316e90f9 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Mon, 17 Feb 2025 14:12:06 -0500 Subject: [PATCH 089/132] [BI-2047] - improved exception handling --- .../brapi/v2/dao/BrAPIObservationDAO.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java index 82f168dc1..ced857c9d 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java @@ -247,7 +247,7 @@ public List getObservationsByObservationUnitsAndStudies(Collec .collect(Collectors.toList()); } - // TODO: implement other filters. + // TODO: implement other filters in BI-2506. public List getObservationsByFilters(Program program, String studyDbId) throws ApiException, DoesNotExistException { String studySource = Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.STUDIES); @@ -268,14 +268,15 @@ public List getObservationsByFilters(Program program, String s return xref.filter(brAPIExternalReference -> studyDbId.equals(brAPIExternalReference.getReferenceId())).isPresent(); }) .peek(o -> { - // TODO: avoid NPEs! // Translate ObservationVariableDbId. o.setObservationVariableDbId(traitIdsByObservationVariableDbId.get(o.getObservationVariableDbId())); // Translate ObservationUnitDbId. - o.setObservationUnitDbId(Utilities.getExternalReference(o.getExternalReferences(), observationUnitSource).get().getReferenceId()); + o.setObservationUnitDbId(Utilities.getExternalReference(o.getExternalReferences(), observationUnitSource) + .orElseThrow(() -> new RuntimeException("observationUnit xref not found on observation")).getReferenceId()); // Translate ObservationId. - o.setObservationDbId(Utilities.getExternalReference(o.getExternalReferences(), observationSource).get().getReferenceId()); - // TODO: do we need to translate germplasmDbId? + o.setObservationDbId(Utilities.getExternalReference(o.getExternalReferences(), observationSource) + .orElseThrow(() -> new RuntimeException("observation xref not found on observation")).getReferenceId()); + // TODO: consider translating germplasmDbId in BI-2506. }).collect(Collectors.toList()); } From 3279b5befecc68545e102ff8b1902ca8797ee141 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Mon, 17 Feb 2025 17:43:35 -0500 Subject: [PATCH 090/132] [BI-2047] - reused observations list --- .../org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java index ced857c9d..f68024184 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java @@ -260,7 +260,7 @@ public List getObservationsByFilters(Program program, String s HashMap traitIdsByObservationVariableDbId = traitService.getIdsByObservationVariableDbIds(program.getId(), observations.stream().map(BrAPIObservation::getObservationVariableDbId).collect(Collectors.toList())); // Lookup studyDbId - return getProgramObservations(program.getId()).values().stream() + return observations.stream() .filter(o -> { // Short circuit if filter is null. if (studyDbId == null) return true; From edb94f5234e8eb7a1c7b4f25096fec283cdaba39 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 18 Feb 2025 10:28:27 -0500 Subject: [PATCH 091/132] [BI-2047] - added correct defaults --- .../brapi/v2/BrAPIObservationsController.java | 26 +++++++++++-------- .../brapi/v2/dao/BrAPIObservationDAO.java | 2 +- ...ObservationsControllerIntegrationTest.java | 12 ++++++--- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java index 4c7127528..4611fd34f 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java @@ -150,38 +150,42 @@ public HttpResponse observationsGet(@PathVariable("programId") UUID programId, // Get a filtered list of observations. List observations = observationDAO.getObservationsByFilters(program.get(), studyDbId); + // If page is not provided, set it to the default value 0. + if (page == null) page = 0; + // If pageSize is not provided, set it to the default value 1000. + if (pageSize == null) pageSize = 1000; + // Total number of records in the unpaged super set. int totalCount = observations.size(); - // Zero-indexed page, default to zero. - int actualPage = page != null ? page : 0; - // The least of pageSize and totalCount, unless pageSize is null or zero, in which case use totalCount. - int requestedPageSize = (pageSize != null && pageSize > 0) ? Math.min(pageSize, totalCount) : totalCount; + // The least of pageSize and totalCount, unless pageSize is zero, in which case use totalCount. + int requestedPageSize = pageSize > 0 ? Math.min(pageSize, totalCount) : totalCount; // Integer division and round up. int totalPages = totalCount / requestedPageSize + ((totalCount % requestedPageSize == 0) ? 0 : 1); - log.info("(Pagination) totalCount: " + totalCount + " actualPage (0-indexed): " + actualPage + " requestedPageSize: " + requestedPageSize + " totalPages: " + totalPages); + log.info("(Pagination) totalCount: " + totalCount + " page (0-indexed): " + page + " requestedPageSize: " + requestedPageSize + " totalPages: " + totalPages); // Determine validity of pagination query parameters. - boolean pageSizeValid = pageSize != null && pageSize > 0; - boolean pageValid = page != null && page >= 0 && page < totalPages; + boolean pageSizeValid = pageSize > 0; + boolean pageValid = page >= 0 && page < totalPages; // Only paginate if valid pagination values were sent. if (pageSizeValid && pageValid) { - int start = actualPage * requestedPageSize; + int start = page * requestedPageSize; // Account for last page, which may have fewer than requestedPageSize items, or exactly requestedPageSize items. - int end = (actualPage == (totalPages - 1) && totalCount % requestedPageSize != 0) ? (start + (totalCount % requestedPageSize)) : Math.min(((actualPage + 1) * requestedPageSize), totalCount); + int end = (page == (totalPages - 1) && totalCount % requestedPageSize != 0) ? (start + (totalCount % requestedPageSize)) : Math.min(((page + 1) * requestedPageSize), totalCount); log.info("(Pagination) start " + start + " end " + end); // Sort observations so that paging is consistent and coherent. observations.sort(Comparator.comparing(BrAPIObservation::getObservationDbId)); // Paginate response. observations = observations.subList(start, end); - } else if (pageSize != null || page != null) { + } else { // If one or more of the pagination query parameters are not null, both must be present and valid. String errorMessage = "Invalid query parameters: page, pageSize"; return HttpResponse.badRequest(new BrAPIObservationListResponse().metadata(new BrAPIMetadata().status(List.of(new BrAPIStatus().messageType(BrAPIStatus.MessageTypeEnum.ERROR) .message(errorMessage))))); } - return HttpResponse.ok(new BrAPIObservationListResponse().metadata(new BrAPIMetadata().pagination(new BrAPIIndexPagination().currentPage(actualPage) + return HttpResponse.ok(new BrAPIObservationListResponse().metadata(new BrAPIMetadata().pagination(new BrAPIIndexPagination() + .currentPage(page) .totalPages(totalPages) .pageSize(observations.size()) .totalCount(totalCount))) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java index f68024184..e9d2f788f 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java @@ -259,7 +259,7 @@ public List getObservationsByFilters(Program program, String s // Build a hashmap of traits for fast lookup. The key is ObservationVariableDbId, the value is the Trait Id. HashMap traitIdsByObservationVariableDbId = traitService.getIdsByObservationVariableDbIds(program.getId(), observations.stream().map(BrAPIObservation::getObservationVariableDbId).collect(Collectors.toList())); - // Lookup studyDbId + // Lookup studyDbId. return observations.stream() .filter(o -> { // Short circuit if filter is null. diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java index 12269df3e..27cf6defd 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java @@ -341,6 +341,9 @@ public void testGetObsPagination() { // Check no pagination. checkPagination(null, null, HttpStatus.OK, 4, 4, 1); + // Check page and pageSize defaults. + checkPagination(null, 2, HttpStatus.OK, 2, 4, 2); + checkPagination(0, null, HttpStatus.OK, 4, 4, 1); // Check valid pagination, including last page edge case. checkPagination(0, 1, HttpStatus.OK, 1, 4, 4); checkPagination(1, 2, HttpStatus.OK, 2, 4, 2); @@ -360,10 +363,10 @@ private void checkPagination(Integer page, Integer pageSize, HttpStatus expected String requestURL = String.format("/programs/%s/brapi/v2/observations", program.getId()); if (page != null) { requestURL = requestURL + "?page=" + page; - } else { - page = 0; } - if (pageSize != null) { + if (pageSize != null && page == null) { + requestURL = requestURL + "?pageSize=" + pageSize; + } else if (pageSize != null) { requestURL = requestURL + "&pageSize=" + pageSize; } @@ -386,7 +389,8 @@ private void checkPagination(Integer page, Integer pageSize, HttpStatus expected // Get metadata. JsonObject pagination = responseObj.getAsJsonObject("metadata").getAsJsonObject("pagination"); assertEquals(expectedSize, pagination.get("pageSize").getAsInt()); - assertEquals(page, pagination.get("currentPage").getAsInt()); + int expectedPage = page == null ? 0 : page; + assertEquals(expectedPage, pagination.get("currentPage").getAsInt()); assertEquals(expectedTotalPages, pagination.get("totalPages").getAsInt()); assertEquals(expectedTotalCount, pagination.get("totalCount").getAsInt()); // Get observations. From c988d88475794ea38ac27b7ecc6f3d5959075509 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 18 Feb 2025 11:21:15 -0500 Subject: [PATCH 092/132] [BI-2047] - removed comment --- .../breedinginsight/brapi/v2/BrAPIObservationsController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java index 4611fd34f..27ac4e34c 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationsController.java @@ -178,7 +178,6 @@ public HttpResponse observationsGet(@PathVariable("programId") UUID programId, // Paginate response. observations = observations.subList(start, end); } else { - // If one or more of the pagination query parameters are not null, both must be present and valid. String errorMessage = "Invalid query parameters: page, pageSize"; return HttpResponse.badRequest(new BrAPIObservationListResponse().metadata(new BrAPIMetadata().status(List.of(new BrAPIStatus().messageType(BrAPIStatus.MessageTypeEnum.ERROR) .message(errorMessage))))); From 8d41a91fb9cd1309e7370b9e01163e6ce27c9d08 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 18 Feb 2025 13:59:44 -0500 Subject: [PATCH 093/132] [BI-2047] - added studyDbId translation --- .../org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java index e9d2f788f..d0217748a 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java @@ -276,6 +276,9 @@ public List getObservationsByFilters(Program program, String s // Translate ObservationId. o.setObservationDbId(Utilities.getExternalReference(o.getExternalReferences(), observationSource) .orElseThrow(() -> new RuntimeException("observation xref not found on observation")).getReferenceId()); + // Translate StudyDbId. + o.setStudyDbId(Utilities.getExternalReference(o.getExternalReferences(), studySource) + .orElseThrow(() -> new RuntimeException("study xref not found on observation")).getReferenceId()); // TODO: consider translating germplasmDbId in BI-2506. }).collect(Collectors.toList()); } From a3e6fc5e2bf6fc8c8519d95292f3011c030df015 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Tue, 18 Feb 2025 20:29:52 +0000 Subject: [PATCH 094/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 2996916c7..de0d9bccb 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+904 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/eab67a016af348480698361440314fecd593ae20 +version=v1.1.0+906 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/487aedbada7fba9b337842f889c909201905cb95 From d4677ab614f34a8ce2f9b512bf9ea41a319ea7de Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Wed, 19 Feb 2025 18:23:48 +0000 Subject: [PATCH 095/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index de0d9bccb..ac58c0482 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+906 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/487aedbada7fba9b337842f889c909201905cb95 +version=v1.1.0+908 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/686568c26174a44c8299969ab43f2ebeea9640ec From 10e74d04cdbfc89400cccebf5b10b9a4bb97dbf3 Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Mon, 17 Feb 2025 09:18:12 -0500 Subject: [PATCH 096/132] [BI-2450] made the delay for caching, config driven. If no nnew values are added to the .env file, the current behaviour is preserved. --- .env.template | 10 +++++++++- .../brapi/v2/dao/BrAPIGermplasmDAO.java | 2 +- .../brapi/v2/dao/BrAPIObservationDAO.java | 2 +- .../brapi/v2/dao/BrAPIObservationUnitDAO.java | 2 +- .../breedinginsight/brapi/v2/dao/BrAPIStudyDAO.java | 2 +- .../brapi/v2/dao/impl/BrAPITrialDAOImpl.java | 2 +- .../org/breedinginsight/daos/impl/TraitDAOImpl.java | 2 +- src/main/resources/application.yml | 8 ++++++++ 8 files changed, 23 insertions(+), 7 deletions(-) diff --git a/.env.template b/.env.template index 66a62fb5d..f3afe2725 100644 --- a/.env.template +++ b/.env.template @@ -61,4 +61,12 @@ AWS_GENO_BUCKET= AWS_S3_ENDPOINT= BRAPI_VENDOR_SUBMISSION_ENABLED=false #can a submission be sent to a vendor via BrAPI -BRAPI_VENDOR_CHECK_FREQUENCY=1d #how often to check for vendor updates for sample submissions \ No newline at end of file +BRAPI_VENDOR_CHECK_FREQUENCY=1d #how often to check for vendor updates for sample submissions + +#The initial caching of each type of object needs to be staggered by the prescribed number of seconds +germplasm_start_delay=5s +study_start_delay=10s +trial_start_delay=15s +trait_start_delay=20s +observation_start_delay=25s +observation_unit_start_delay=30s \ No newline at end of file diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java index 2e9371be4..75226babf 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java @@ -78,7 +78,7 @@ public BrAPIGermplasmDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUti this.brAPIEndpointProvider = brAPIEndpointProvider; } - @Scheduled(initialDelay = "2s") + @Scheduled(initialDelay = "${startup.delay.germplasm}") public void setup() { if(!runScheduledTasks) { return; diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java index d0217748a..10048564c 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationDAO.java @@ -87,7 +87,7 @@ public BrAPIObservationDAO(ProgramDAO programDAO, this.programCache = programCacheProvider.getProgramCache(this::fetchProgramObservations, BrAPIObservation.class); } - @Scheduled(initialDelay = "3s") + @Scheduled(initialDelay = "${startup.delay.observation}") public void setup() { if(!runScheduledTasks) { return; 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 9ef12b371..9749bf093 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java @@ -95,7 +95,7 @@ public BrAPIObservationUnitDAO(ProgramDAO programDAO, this.programCache = programCacheProvider.getProgramCache(this::fetchProgramObservationUnits, BrAPIObservationUnit.class); } - @Scheduled(initialDelay = "3s") + @Scheduled(initialDelay = "${startup.delay.observation_unit}") public void setup() { if(!runScheduledTasks) { return; diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIStudyDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIStudyDAO.java index 160e8df62..217d10216 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIStudyDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIStudyDAO.java @@ -65,7 +65,7 @@ public BrAPIStudyDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil br this.programCache = programCacheProvider.getProgramCache(this::fetchProgramStudy, BrAPIStudy.class); } - @Scheduled(initialDelay = "2s") + @Scheduled(initialDelay = "${startup.delay.study}") public void setup() { if(!runScheduledTasks) { return; diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java index d703f97e4..b0c771f6d 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/impl/BrAPITrialDAOImpl.java @@ -81,7 +81,7 @@ public BrAPITrialDAOImpl(ProgramCacheProvider programCacheProvider, } - @Scheduled(initialDelay = "2s") + @Scheduled(initialDelay = "${startup.delay.trial}") public void setup() { if(!runScheduledTasks) { return; diff --git a/src/main/java/org/breedinginsight/daos/impl/TraitDAOImpl.java b/src/main/java/org/breedinginsight/daos/impl/TraitDAOImpl.java index 983c739c2..618d1239d 100644 --- a/src/main/java/org/breedinginsight/daos/impl/TraitDAOImpl.java +++ b/src/main/java/org/breedinginsight/daos/impl/TraitDAOImpl.java @@ -109,7 +109,7 @@ public TraitDAOImpl(Configuration config, this.runScheduledTasks = runScheduledTasks; } - @Scheduled(initialDelay = "2s") + @Scheduled(initialDelay = "${startup.delay.trait}") public void setup() { if(!runScheduledTasks) { return; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 85987ef9d..51ae38f67 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -205,3 +205,11 @@ aws: buckets: genotype: bucket: ${AWS_GENO_BUCKET} +startup: + delay: + germplasm: ${germplasm_start_delay:2s} + study: ${study_start_delay:2s} + trial: ${trial_start_delay:2s} + trait: ${trait_start_delay:2s} + observation: ${observation_start_delay:3s} + observation_unit: ${observation_unit_start_delay:3s} From f83b8418eba42ff5752df8e55821b8cd9caae643 Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Mon, 24 Feb 2025 08:55:06 -0500 Subject: [PATCH 097/132] [BI-2450] made .env variables uppercase --- .env.template | 12 ++++++------ src/main/resources/application.yml | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.env.template b/.env.template index f3afe2725..abe859391 100644 --- a/.env.template +++ b/.env.template @@ -64,9 +64,9 @@ BRAPI_VENDOR_SUBMISSION_ENABLED=false #can a submission be sent to a vendor via BRAPI_VENDOR_CHECK_FREQUENCY=1d #how often to check for vendor updates for sample submissions #The initial caching of each type of object needs to be staggered by the prescribed number of seconds -germplasm_start_delay=5s -study_start_delay=10s -trial_start_delay=15s -trait_start_delay=20s -observation_start_delay=25s -observation_unit_start_delay=30s \ No newline at end of file +GERMPLASM_START_DELAY=5s +STUDY_START_DELAY=10s +TRIAL_START_DELAY=15s +TRAIT_START_DELAY=20s +OBSERVATION_START_DELAY=25s +OBSERVATION_UNIT_START_DELAY=30s \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 51ae38f67..3a98c3fb6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -207,9 +207,9 @@ aws: bucket: ${AWS_GENO_BUCKET} startup: delay: - germplasm: ${germplasm_start_delay:2s} - study: ${study_start_delay:2s} - trial: ${trial_start_delay:2s} - trait: ${trait_start_delay:2s} - observation: ${observation_start_delay:3s} - observation_unit: ${observation_unit_start_delay:3s} + germplasm: ${GERMPLASM_START_DELAY:2s} + study: ${STUDY_START_DELAY:2s} + trial: ${TRIAL_START_DELAY:2s} + trait: ${TRAIT_START_DELAY:2s} + observation: ${OBSERVATION_START_DELAY:3s} + observation_unit: ${OBSERVATION_UNIT_START_DELAY:3s} From be4d0a19a53f4d6f6d2f829f1c9c926eeb05ef56 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Wed, 26 Feb 2025 13:49:33 +0000 Subject: [PATCH 098/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index ac58c0482..371abdcdb 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+908 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/686568c26174a44c8299969ab43f2ebeea9640ec +version=v1.1.0+910 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/03326c69aa983cda878b26047df9df49ca39c8aa From 5e6af039da3636f2e18329f035f3d72d5ba559c3 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Mon, 3 Mar 2025 14:52:31 +0000 Subject: [PATCH 099/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 371abdcdb..418ac1a2c 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+910 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/03326c69aa983cda878b26047df9df49ca39c8aa +version=v1.1.0+912 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/be4d0a19a53f4d6f6d2f829f1c9c926eeb05ef56 From 69c82eeacfc82295ec71514d1788077e6bccc1cb Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Mon, 24 Mar 2025 09:58:16 -0400 Subject: [PATCH 100/132] [BI-2585] fixed type-o in migrations --- src/main/resources/brapi/sql/R__create_indexes.sql | 2 +- src/test/resources/sql/brapi/mount/R__create_indexes.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/brapi/sql/R__create_indexes.sql b/src/main/resources/brapi/sql/R__create_indexes.sql index 59f3c32af..3b73ec3b6 100644 --- a/src/main/resources/brapi/sql/R__create_indexes.sql +++ b/src/main/resources/brapi/sql/R__create_indexes.sql @@ -15,7 +15,7 @@ -- Indexes to improve read performance of Germplasm operations. CREATE INDEX CONCURRENTLY IF NOT EXISTS "pedigree_edge_this_node_id" ON pedigree_edge (this_node_id); -CREATE INDEX CONCURRENTLY IF NOT EXISTS "pedigree_edge_connected_node_id" ON pedigree_edge (connceted_node_id); +CREATE INDEX CONCURRENTLY IF NOT EXISTS "pedigree_edge_connected_node_id" ON pedigree_edge (connected_node_id); CREATE INDEX CONCURRENTLY IF NOT EXISTS "pedigree_edge_edge_type" ON pedigree_edge (edge_type); CREATE INDEX CONCURRENTLY IF NOT EXISTS "program_external_references_program_entity_id" ON program_external_references (program_entity_id); CREATE INDEX CONCURRENTLY IF NOT EXISTS "external_reference_composite" ON external_reference (external_reference_source, external_reference_id); diff --git a/src/test/resources/sql/brapi/mount/R__create_indexes.sql b/src/test/resources/sql/brapi/mount/R__create_indexes.sql index 59f3c32af..3b73ec3b6 100644 --- a/src/test/resources/sql/brapi/mount/R__create_indexes.sql +++ b/src/test/resources/sql/brapi/mount/R__create_indexes.sql @@ -15,7 +15,7 @@ -- Indexes to improve read performance of Germplasm operations. CREATE INDEX CONCURRENTLY IF NOT EXISTS "pedigree_edge_this_node_id" ON pedigree_edge (this_node_id); -CREATE INDEX CONCURRENTLY IF NOT EXISTS "pedigree_edge_connected_node_id" ON pedigree_edge (connceted_node_id); +CREATE INDEX CONCURRENTLY IF NOT EXISTS "pedigree_edge_connected_node_id" ON pedigree_edge (connected_node_id); CREATE INDEX CONCURRENTLY IF NOT EXISTS "pedigree_edge_edge_type" ON pedigree_edge (edge_type); CREATE INDEX CONCURRENTLY IF NOT EXISTS "program_external_references_program_entity_id" ON program_external_references (program_entity_id); CREATE INDEX CONCURRENTLY IF NOT EXISTS "external_reference_composite" ON external_reference (external_reference_source, external_reference_id); From 1996f2a96771960acfe0731d71a82b55a6b5675b Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Mon, 24 Mar 2025 16:43:55 +0000 Subject: [PATCH 101/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 418ac1a2c..d5662ba70 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+912 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/be4d0a19a53f4d6f6d2f829f1c9c926eeb05ef56 +version=v1.1.0+914 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/8c30a392b3f13a8b1da9916e4e7857d82fe1514c From 79ee1c689046b245267b165360c8ecf7b5104049 Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Fri, 21 Feb 2025 09:55:36 -0500 Subject: [PATCH 102/132] [BI-2187] call GermplasmProcessor::createPostOrder for commit and NOT commit --- .../services/processors/GermplasmProcessor.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 0d514ee72..10d8a41de 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 @@ -326,10 +326,9 @@ public Map process(ImportUpload upload, List
[ - ]) - if (commit) { - createPostOrder(); - } + // for commit: Construct a dependency tree for POSTing order. Dependents on unique germplasm name, ( [ - ]) + // for !commit: Validate for circular pedigree dependencies. + createPostOrder(); // Construct our response object return getStatisticsMap(importRows); @@ -556,6 +555,9 @@ private void validatePedigree(Germplasm germplasm, Integer rowNumber, Validation } } + /* + This will set the postOrder and validate for circular pedigree dependencies. + */ private void createPostOrder() { // Construct a dependency tree for POSTing order Set created = existingGermplasm.stream().map(BrAPIGermplasm::getGermplasmName).collect(Collectors.toSet()); From 05f25b5712a85d89eb6ece96c2a16d62f9e2690e Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Wed, 26 Mar 2025 11:35:46 -0400 Subject: [PATCH 103/132] [BI-2187] Fixed bug, caused by the gemplasm Pedigree string being different for commit=true vs commit=false --- .../services/processors/GermplasmProcessor.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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 10d8a41de..81d9bef93 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 @@ -328,7 +328,7 @@ public Map process(ImportUpload upload, List
[ - ]) // for !commit: Validate for circular pedigree dependencies. - createPostOrder(); + createPostOrder(commit); // Construct our response object return getStatisticsMap(importRows); @@ -558,9 +558,15 @@ private void validatePedigree(Germplasm germplasm, Integer rowNumber, Validation /* This will set the postOrder and validate for circular pedigree dependencies. */ - private void createPostOrder() { + private void createPostOrder(boolean commit) { + Set created = null; // Construct a dependency tree for POSTing order - Set created = existingGermplasm.stream().map(BrAPIGermplasm::getGermplasmName).collect(Collectors.toSet()); + if(commit){ + created = existingGermplasm.stream().map(BrAPIGermplasm::getGermplasmName).collect(Collectors.toSet()); + } + else { + created = existingGermplasm.stream().map(BrAPIGermplasm::getDefaultDisplayName).collect(Collectors.toSet()); + } //todo this gets messy @@ -571,7 +577,7 @@ private void createPostOrder() { for (BrAPIGermplasm germplasm : newGermplasmList) { // If we've already planned this germplasm, skip - if (created.contains(germplasm.getGermplasmName())) { + if ( (commit && created.contains(germplasm.getGermplasmName())) || (!commit && created.contains(germplasm.getDefaultDisplayName())) ) { continue; } From 14acf7ae29a3f290d344aa96e6449e6bfc41713a Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Wed, 26 Mar 2025 18:18:22 +0000 Subject: [PATCH 104/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index d5662ba70..08cf3f1e2 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+914 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/8c30a392b3f13a8b1da9916e4e7857d82fe1514c +version=v1.1.0+916 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/8be0c65555e65c5e70ea7e20a272662e9366f41f From 3c4d3e31d2743ded0f5391e8ec7890a1e854b7af Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:04:52 -0400 Subject: [PATCH 105/132] Baseline tests --- .../processors/GermplasmProcessor.java | 29 ++- .../importer/GermplasmFileImportTest.java | 190 +++++++++++++++++- 2 files changed, 205 insertions(+), 14 deletions(-) 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 81d9bef93..09be22389 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 @@ -115,6 +115,17 @@ public GermplasmProcessor(BrAPIGermplasmService brAPIGermplasmService, DSLContex public void getExistingBrapiData(List importRows, Program program) throws ApiException { + // BI-2573 - sort by entry no here so ordering is consistent everywhere in processor + importRows.sort((left, right) -> { + if (left.getGermplasm().getEntryNo() == null || right.getGermplasm().getEntryNo() == null) { + return 0; + } else { + Integer leftEntryNo = Integer.parseInt(left.getGermplasm().getEntryNo()); + Integer rightEntryNo = Integer.parseInt(right.getGermplasm().getEntryNo()); + return leftEntryNo.compareTo(rightEntryNo); + } + }); + // Get all of our objects specified in the data file by their unique attributes Map germplasmAccessionNumbers = new HashMap<>(); for (int i = 0; i < importRows.size(); i++) { @@ -265,16 +276,7 @@ public Map process(ImportUpload upload, List
entryNumberCounts = new HashMap<>(); List userProvidedEntryNumbers = new ArrayList<>(); ValidationErrors validationErrors = new ValidationErrors(); - // Sort importRows by entry number (if present). - importRows.sort((left, right) -> { - if (left.getGermplasm().getEntryNo() == null || right.getGermplasm().getEntryNo() == null) { - return 0; - } else { - Integer leftEntryNo = Integer.parseInt(left.getGermplasm().getEntryNo()); - Integer rightEntryNo = Integer.parseInt(right.getGermplasm().getEntryNo()); - return leftEntryNo.compareTo(rightEntryNo); - } - }); + for (int i = 0; i < importRows.size(); i++) { log.debug("processing germplasm row: " + (i+1)); BrAPIImport brapiImport = importRows.get(i); @@ -283,6 +285,7 @@ public Map process(ImportUpload upload, List
importRows, Map germplasmNames = new ArrayList<>(); + + for (int i = 0; i < previewRows.size(); i++) { + JsonObject germplasm = previewRows.get(i).getAsJsonObject().getAsJsonObject("germplasm").getAsJsonObject("brAPIObject"); + germplasmNames.add(germplasm.get("germplasmName").getAsString()); + checkBasicResponse(germplasm, fileData, i); + + if (!commit) { + // preview checks + checkEntryNoPreviewFields(fileData, previewRows, i, motherName, fatherName); + } else { + // commit checks + checkEntryNoCommitFields(fileData, previewRows, i); + } + } + + if (commit) { + // Check the germplasm list + // TODO: check germplasm list order + checkGermplasmList(Germplasm.constructGermplasmListName(listName, validProgram), listDescription, germplasmNames); + } + } + + /** + * Check fields relevant to preview for descending entry no tests + * @param fileData + * @param previewRows + */ + private void checkEntryNoPreviewFields(Table fileData, JsonArray previewRows, int i, String motherName, String fatherName) { + JsonObject germplasm = previewRows.get(i).getAsJsonObject().getAsJsonObject("germplasm").getAsJsonObject("brAPIObject"); + + // Check preview specific items + // Germplasm name (display name) + assertEquals(fileData.getString(i, "Germplasm Name"), germplasm.get("germplasmName").getAsString()); + JsonObject additionalInfo = germplasm.getAsJsonObject("additionalInfo"); + + // check entry number assignment + assertEquals(fileData.getString(i, "Entry No"), additionalInfo.get("importEntryNumber").getAsString(), "Wrong entry number"); + // check pedigree entry number assignment + String fileFemaleEntryNo = fileData.getString(i, "Female Parent Entry No"); + if (isNotBlank(fileFemaleEntryNo)) { + assertEquals(fileFemaleEntryNo, additionalInfo.get("femaleParentEntryNo").getAsString(), "Wrong female parent entry number"); + } + String fileMaleEntryNo = fileData.getString(i, "Male Parent Entry No"); + if (isNotBlank(fileMaleEntryNo)) { + assertEquals(fileMaleEntryNo, additionalInfo.get("maleParentEntryNo").getAsString(), "Wrong male parent entry number"); + } + + // check preview pedigree values + // only care about entry nos for this test case, not using GIDs + if (isNotBlank(fileFemaleEntryNo) && isNotBlank(fileMaleEntryNo)) { + String pedigree = germplasm.get("pedigree").getAsString(); + String[] pedigreeParts = pedigree.split("/"); + String mother = pedigreeParts[0]; + String father = pedigreeParts[1]; + assertEquals(motherName, mother, "Wrong mother"); + assertEquals(fatherName, father, "Wrong father"); + } + } + + private void checkEntryNoCommitFields(Table fileData, JsonArray previewRows, int i) { + JsonObject germplasm = previewRows.get(i).getAsJsonObject().getAsJsonObject("germplasm").getAsJsonObject("brAPIObject"); + // Check commit specific items + // Germplasm name (display name) + String expectedGermplasmName = String.format("%s [%s-%s]", fileData.getString(i, "Germplasm Name"), validProgram.getKey(), germplasm.get("accessionNumber").getAsString()); + assertEquals(expectedGermplasmName, germplasm.get("germplasmName").getAsString()); + // Created Date + JsonObject additionalInfo = germplasm.getAsJsonObject("additionalInfo"); + assertTrue(additionalInfo.has(BrAPIAdditionalInfoFields.CREATED_DATE), "createdDate is missing"); + // Accession Number + assertTrue(germplasm.has("accessionNumber"), "accessionNumber missing"); + // TODO: check that gids are assigned in entry no order + /* + if (i > 0) { + int lastEntryNo = previewRows.get(i-1). + } + */ + + // TODO: pedigree + /* + // Pedigree (germplasm names) + String pedigree = germplasm.get("pedigree").getAsString(); + String mother = !pedigree.isBlank() ? pedigree.split("/")[0] : null; + String father = !pedigree.isBlank() && pedigree.split("/").length > 1 ? pedigree.split("/")[1] : null; + String regexMatcher = "^(.*\\b) \\[([A-Z]{2,6})-(\\d+)\\]$"; + assertTrue(mother.matches(String.format(regexMatcher, femaleParents.get(i))), "Wrong mother"); + if (!maleParents.get(i).isBlank()) { + assertTrue(father.matches(String.format(regexMatcher, maleParents.get(i))), "Wrong father"); + } else { + assertNull(father, "Wrong father"); + } + */ + + // External Reference germplasm + JsonArray externalReferences = germplasm.getAsJsonArray("externalReferences"); + boolean referenceFound = false; + for (JsonElement reference: externalReferences) { + String referenceSource = reference.getAsJsonObject().get("referenceSource").getAsString(); + if (referenceSource.equals(BRAPI_REFERENCE_SOURCE)) { + referenceFound = true; + break; + } + } + assertTrue(referenceFound, "Germplasm UUID reference not found"); + + // TODO: add synonyms? + /* + // Synonyms + String[] splitGermplasmName = germplasm.get("germplasmName").getAsString().split(" "); + String scope = splitGermplasmName[splitGermplasmName.length - 1]; + JsonArray synonyms = germplasm.getAsJsonArray("synonyms"); + for (JsonElement synonym: synonyms) { + String synonymName = synonym.getAsJsonObject().get("synonym").getAsString(); + assertNotNull(synonymName); + assertTrue(synonymName.contains(scope), "Germplasm synonym was not properly scoped"); + } + */ + } + private JsonObject importGermplasm(String pathname, String listName, String listDescription, Boolean commit) throws InterruptedException { File file = new File(pathname); From 55065ff5e0af6a9e184aa3ebd8944d5de6e76aa9 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:14:26 -0400 Subject: [PATCH 106/132] Added test csv import files --- .../brapps/importer/GermplasmFileImportTest.java | 16 ++-------------- .../files/germplasm_import/entry_no_asc.csv | 4 ++++ .../files/germplasm_import/entry_no_desc.csv | 4 ++++ 3 files changed, 10 insertions(+), 14 deletions(-) create mode 100644 src/test/resources/files/germplasm_import/entry_no_asc.csv create mode 100644 src/test/resources/files/germplasm_import/entry_no_desc.csv diff --git a/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java b/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java index 60610b2f9..acd787f2f 100644 --- a/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java +++ b/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java @@ -735,6 +735,7 @@ public void selfReferenceParentError() { } /** + * * Verify GID assignment order when germplasm entry numbers are sorted ascending in file * Preview shows Germplasm Name in file order, Entry No ascending (also file order in this case), no GID at this stage * Germplasm view shows GID asc, Test1 lowest, Test 3 highest @@ -778,7 +779,7 @@ public void entryNoAscending(boolean commit) { @ValueSource(booleans = {false, true}) @SneakyThrows public void entryNoDescending(boolean commit) { - String pathname = "src/test/resources/files/germplasm_import/entry_no_desc_pedigree.csv"; + String pathname = "src/test/resources/files/germplasm_import/entry_no_desc.csv"; Table fileData = Table.read().file(pathname); String listName = "EntryNoDesc"; String listDescription = "Entry numbers in descending order with pedigree"; @@ -900,19 +901,6 @@ private void checkEntryNoCommitFields(Table fileData, JsonArray previewRows, int } } assertTrue(referenceFound, "Germplasm UUID reference not found"); - - // TODO: add synonyms? - /* - // Synonyms - String[] splitGermplasmName = germplasm.get("germplasmName").getAsString().split(" "); - String scope = splitGermplasmName[splitGermplasmName.length - 1]; - JsonArray synonyms = germplasm.getAsJsonArray("synonyms"); - for (JsonElement synonym: synonyms) { - String synonymName = synonym.getAsJsonObject().get("synonym").getAsString(); - assertNotNull(synonymName); - assertTrue(synonymName.contains(scope), "Germplasm synonym was not properly scoped"); - } - */ } private JsonObject importGermplasm(String pathname, String listName, String listDescription, Boolean commit) throws InterruptedException { diff --git a/src/test/resources/files/germplasm_import/entry_no_asc.csv b/src/test/resources/files/germplasm_import/entry_no_asc.csv new file mode 100644 index 000000000..f2f135bef --- /dev/null +++ b/src/test/resources/files/germplasm_import/entry_no_asc.csv @@ -0,0 +1,4 @@ +GID,Germplasm Name,Breeding Method,Source,Female Parent GID,Male Parent GID,Entry No,Female Parent Entry No,Male Parent Entry No,External UID,Synonyms +,EntryNoAscGerm 1,BCR,Test,,,1,2,3,1, +,EntryNoAscGerm 2,BCR,Test,,,2,,,2, +,EntryNoAscGerm 3,BCR,Test,,,3,,,3, \ No newline at end of file diff --git a/src/test/resources/files/germplasm_import/entry_no_desc.csv b/src/test/resources/files/germplasm_import/entry_no_desc.csv new file mode 100644 index 000000000..a47a1f7d4 --- /dev/null +++ b/src/test/resources/files/germplasm_import/entry_no_desc.csv @@ -0,0 +1,4 @@ +GID,Germplasm Name,Breeding Method,Source,Female Parent GID,Male Parent GID,Entry No,Female Parent Entry No,Male Parent Entry No,External UID,Synonyms +,EntryNoDescGerm 1,BCR,Test,,,3,,,3, +,EntryNoDescGerm 2,BCR,Test,,,2,,,2, +,EntryNoDescGerm 3,BCR,Test,,,1,2,3,1, \ No newline at end of file From 6dd00737eb63cfa66535c003e7c61d7443615f98 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:39:34 -0400 Subject: [PATCH 107/132] Removed debug comments from processor --- .../importer/services/processors/GermplasmProcessor.java | 7 ------- 1 file changed, 7 deletions(-) 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 09be22389..aad1720d9 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 @@ -285,7 +285,6 @@ public Map process(ImportUpload upload, List
importRows, Map Date: Tue, 1 Apr 2025 10:59:16 -0400 Subject: [PATCH 108/132] Added gid assignment order check for committed data --- .../importer/GermplasmFileImportTest.java | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java b/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java index acd787f2f..239345290 100644 --- a/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java +++ b/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java @@ -822,6 +822,7 @@ private void checkEntryNoFields(Table fileData, JsonArray previewRows, boolean c /** * Check fields relevant to preview for descending entry no tests + * * @param fileData * @param previewRows */ @@ -857,6 +858,17 @@ private void checkEntryNoPreviewFields(Table fileData, JsonArray previewRows, in } } + /** + * Check properties of processed and committed germplasm objects match what would be expected based on file contents. + * Of particular importance are: + * - germplasm are in entry no order + * - gids are assigned in entry no order + * - correct pedigree was assigned to germplasm based on file specification + * + * @param fileData raw file data + * @param previewRows processed preview rows + * @param i row index + */ private void checkEntryNoCommitFields(Table fileData, JsonArray previewRows, int i) { JsonObject germplasm = previewRows.get(i).getAsJsonObject().getAsJsonObject("germplasm").getAsJsonObject("brAPIObject"); // Check commit specific items @@ -868,12 +880,17 @@ private void checkEntryNoCommitFields(Table fileData, JsonArray previewRows, int assertTrue(additionalInfo.has(BrAPIAdditionalInfoFields.CREATED_DATE), "createdDate is missing"); // Accession Number assertTrue(germplasm.has("accessionNumber"), "accessionNumber missing"); - // TODO: check that gids are assigned in entry no order - /* + + // check that gids are assigned in entry no order if (i > 0) { - int lastEntryNo = previewRows.get(i-1). + JsonObject previousGermplasm = previewRows.get(i-1).getAsJsonObject().getAsJsonObject("germplasm").getAsJsonObject("brAPIObject"); + int lastEntryNo = previousGermplasm.getAsJsonObject("additionalInfo").get("importEntryNumber").getAsInt(); + int lastGid = previousGermplasm.get("accessionNumber").getAsInt(); + int currentEntryNo = additionalInfo.get("importEntryNumber").getAsInt(); + int currentGid = germplasm.get("accessionNumber").getAsInt(); + assertEquals(1, currentEntryNo-lastEntryNo, "Expected entry number to be monotonically increasing"); + assertEquals(1, currentGid-lastGid, "Expected GID to be monotonically increasing"); } - */ // TODO: pedigree /* From 5f9c8042a0eff378910d8d758cb0cceca69277b4 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:26:02 -0400 Subject: [PATCH 109/132] Added pedigree check for committed data --- .../importer/GermplasmFileImportTest.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java b/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java index 239345290..122ee4412 100644 --- a/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java +++ b/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java @@ -809,7 +809,7 @@ private void checkEntryNoFields(Table fileData, JsonArray previewRows, boolean c checkEntryNoPreviewFields(fileData, previewRows, i, motherName, fatherName); } else { // commit checks - checkEntryNoCommitFields(fileData, previewRows, i); + checkEntryNoCommitFields(fileData, previewRows, i, motherName, fatherName); } } @@ -869,7 +869,7 @@ private void checkEntryNoPreviewFields(Table fileData, JsonArray previewRows, in * @param previewRows processed preview rows * @param i row index */ - private void checkEntryNoCommitFields(Table fileData, JsonArray previewRows, int i) { + private void checkEntryNoCommitFields(Table fileData, JsonArray previewRows, int i, String motherName, String fatherName) { JsonObject germplasm = previewRows.get(i).getAsJsonObject().getAsJsonObject("germplasm").getAsJsonObject("brAPIObject"); // Check commit specific items // Germplasm name (display name) @@ -892,20 +892,20 @@ private void checkEntryNoCommitFields(Table fileData, JsonArray previewRows, int assertEquals(1, currentGid-lastGid, "Expected GID to be monotonically increasing"); } - // TODO: pedigree - /* - // Pedigree (germplasm names) - String pedigree = germplasm.get("pedigree").getAsString(); - String mother = !pedigree.isBlank() ? pedigree.split("/")[0] : null; - String father = !pedigree.isBlank() && pedigree.split("/").length > 1 ? pedigree.split("/")[1] : null; - String regexMatcher = "^(.*\\b) \\[([A-Z]{2,6})-(\\d+)\\]$"; - assertTrue(mother.matches(String.format(regexMatcher, femaleParents.get(i))), "Wrong mother"); - if (!maleParents.get(i).isBlank()) { - assertTrue(father.matches(String.format(regexMatcher, maleParents.get(i))), "Wrong father"); - } else { - assertNull(father, "Wrong father"); + // pedigree check just for single germplasm in file with pedigree info + String fileFemaleEntryNo = fileData.getString(i, "Female Parent Entry No"); + String fileMaleEntryNo = fileData.getString(i, "Male Parent Entry No"); + + // only care about entry nos for this test case, not using GIDs + if (isNotBlank(fileFemaleEntryNo) && isNotBlank(fileMaleEntryNo)) { + String pedigree = germplasm.get("pedigree").getAsString(); + String[] pedigreeParts = pedigree.split("/"); + String mother = pedigreeParts[0]; + String father = pedigreeParts[1]; + String regexMatcher = "^(.*\\b) \\[([A-Z]{2,6})-(\\d+)\\]$"; + assertTrue(mother.matches(String.format(regexMatcher, motherName)), "Wrong mother"); + assertTrue(father.matches(String.format(regexMatcher, fatherName)), "Wrong father"); } - */ // External Reference germplasm JsonArray externalReferences = germplasm.getAsJsonArray("externalReferences"); From 8076cd1b1f2eb033e9ea0117361dce83e35943d2 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:36:10 -0400 Subject: [PATCH 110/132] Cleaned up comments in tests --- .../importer/GermplasmFileImportTest.java | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java b/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java index 122ee4412..d248d42ad 100644 --- a/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java +++ b/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java @@ -735,14 +735,10 @@ public void selfReferenceParentError() { } /** + * Test preview and commit data when entry numbers are in ascending order in file. This is the normal case and here + * to catch any possible regressions * - * Verify GID assignment order when germplasm entry numbers are sorted ascending in file - * Preview shows Germplasm Name in file order, Entry No ascending (also file order in this case), no GID at this stage - * Germplasm view shows GID asc, Test1 lowest, Test 3 highest - * Germplasm List shows GID asc, Entry No asc - * - * Preview table ordered by entry number ascending regardless of file order - * + * @param commit controls whether import is preview or commit */ @ParameterizedTest @ValueSource(booleans = {false, true}) @@ -763,17 +759,14 @@ public void entryNoAscending(boolean commit) { checkEntryNoFields(fileData, previewRows, commit, listName, listDescription, "EntryNoAscGerm 2", "EntryNoAscGerm 3"); } - /** - * Verify GID assignment order when germplasm entry numbers are sorted descending in file - * Preview shows Germplasm Name in entry no order, Entry No ascending (also file order in this case), no GID at this stage - * Germplasm view shows GID desc, Test1 highest, Test 3 lowest GIDs in entry no order, not file order - * Germplasm List shows GID asc, Entry No asc - */ - /** * Prior to BI-2573 this file would result in a false positive circular dependency error. The reason is that when * entry no order did not match germplasm file order, pedigree information was assigned to the wrong germplasm * record due to inconsistencies in sorting during processing. + * + * Test preview and commit data when entry numbers are in descending order in file + * + * @param commit controls whether import is preview or commit */ @ParameterizedTest @ValueSource(booleans = {false, true}) @@ -794,6 +787,17 @@ public void entryNoDescending(boolean commit) { checkEntryNoFields(fileData, previewRows, commit, listName, listDescription, "EntryNoDescGerm 2", "EntryNoDescGerm 1"); } + /** + * Shared method to perform entry number order tests for preview and commit + * + * @param fileData raw file data + * @param previewRows processed preview rows + * @param commit whether checking preview or commit fields + * @param listName name of list when committing data + * @param listDescription list description when committing data + * @param motherName name of mother for pedigree check (from file) + * @param fatherName name of father for pedigree check (from file) + */ private void checkEntryNoFields(Table fileData, JsonArray previewRows, boolean commit, String listName, String listDescription, String motherName, String fatherName) { @@ -815,16 +819,17 @@ private void checkEntryNoFields(Table fileData, JsonArray previewRows, boolean c if (commit) { // Check the germplasm list - // TODO: check germplasm list order checkGermplasmList(Germplasm.constructGermplasmListName(listName, validProgram), listDescription, germplasmNames); } } /** - * Check fields relevant to preview for descending entry no tests + * Check important field in preview data beyond basic info + * - entry number assignment + * - pedigree assignment * - * @param fileData - * @param previewRows + * @param fileData raw file data + * @param previewRows processed preview rows */ private void checkEntryNoPreviewFields(Table fileData, JsonArray previewRows, int i, String motherName, String fatherName) { JsonObject germplasm = previewRows.get(i).getAsJsonObject().getAsJsonObject("germplasm").getAsJsonObject("brAPIObject"); From 21ca293813a211fdbf1792dbb535478b9ebb5cfc Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Thu, 3 Apr 2025 14:56:20 +0000 Subject: [PATCH 111/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 08cf3f1e2..8286ff703 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+916 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/8be0c65555e65c5e70ea7e20a272662e9366f41f +version=v1.1.0+918 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/d4258a0f808cc45ec797526c6470421b9a0d0366 From 46de11390f8f03fbce668db81c242a25a0e1cbe7 Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Thu, 27 Mar 2025 16:28:53 -0400 Subject: [PATCH 112/132] [BI-2567] speed up data gathering for germplasm list and list-detail pages --- .../v2/services/BrAPIGermplasmService.java | 5 ++- .../brapi/v2/services/BrAPIListService.java | 44 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) 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 231978368..5242041c9 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java @@ -189,9 +189,10 @@ public List getGermplasmByList(UUID programId, String listDbId) List germplasm = germplasmDAO.getGermplasmByRawName(germplasmNames, programId); Map germplasmByName = new HashMap<>(); + // set the list ID in the germplasm additional info + germplasm.forEach(x -> x.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_LIST_ID, listId)); + for (BrAPIGermplasm g : germplasm) { - // set the list ID in the germplasm additional info - germplasm.forEach(x -> x.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_LIST_ID, listId)); // Add to map. germplasmByName.put(g.getGermplasmName(), g); } 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 246d2991d..2bfeccb22 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIListService.java @@ -1,5 +1,7 @@ package org.breedinginsight.brapi.v2.services; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import io.micronaut.context.annotation.Property; import io.micronaut.http.HttpResponse; import lombok.extern.slf4j.Slf4j; @@ -9,8 +11,7 @@ import org.brapi.v2.model.core.BrAPIListTypes; import org.brapi.v2.model.core.request.BrAPIListSearchRequest; import org.brapi.v2.model.core.response.BrAPIListsSingleResponse; -import org.breedinginsight.api.model.v1.response.DataResponse; -import org.breedinginsight.api.model.v1.response.Response; +import org.brapi.v2.model.germ.BrAPIGermplasm; import org.breedinginsight.brapi.v2.dao.BrAPIGermplasmDAO; import org.breedinginsight.brapi.v2.dao.BrAPIListDAO; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; @@ -20,9 +21,7 @@ import javax.inject.Inject; import javax.inject.Singleton; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; @Slf4j @@ -66,27 +65,36 @@ public List getListSummariesByTypeAndXref( Optional programXrefOptional = Utilities.getExternalReference(list.getExternalReferences(),Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.PROGRAMS)); return programXrefOptional.isPresent() && programXrefOptional.get().getReferenceID().equals(program.getId().toString()); }).collect(Collectors.toList()); - for (BrAPIListSummary list: programLists) { + // Map of pairs. + HashMap itemsFromEachList = new HashMap<>(); + for (BrAPIListSummary list: programLists) { // remove the program key from the list name list.setListName(Utilities.removeProgramKeyAndUnknownAdditionalData(list.getListName(), program.getKey())); - // set the owner of the list items as the list owner BrAPIListsSingleResponse listDetails = listDAO.getListById(list.getListDbId(), program.getId()); - List listItemNames = listDetails.getResult().getData(); - if (type != null) { - switch (type) { - case GERMPLASM: - String createdBy = germplasmDAO.getGermplasmByRawName(listItemNames, program.getId()).get(0) + // Add first item from list to hashmap. + itemsFromEachList.put(list.getListDbId(), listDetails.getResult().getData().get(0)); + } + if (type == BrAPIListTypes.GERMPLASM) { + // Fetch one germplasm for each list from cache. + List germplasmRepresentatives = germplasmDAO.getGermplasmByRawName((new ArrayList<>(itemsFromEachList.values())), program.getId()); + // Build hashmap of germplasm by name. + HashMap germplasmByName = new HashMap<>(); + for (BrAPIGermplasm germplasm: germplasmRepresentatives) { + germplasmByName.put(germplasm.getGermplasmName(), germplasm); + } + // For each list, set list owner name from createdBy stored in germplasm additional info. + for (BrAPIListSummary list: programLists) { + String strippedName = Utilities.removeProgramKeyAnyAccession(itemsFromEachList.get(list.getListDbId()), program.getKey()); + + list.setListOwnerName( + germplasmByName.get(strippedName) .getAdditionalInfo() .getAsJsonObject("createdBy") .get("userName") - .getAsString(); - list.setListOwnerName(createdBy); - case OBSERVATIONVARIABLES: - default: - break; - } + .getAsString() + ); } } From bf825689cab5e3335b3163516c42a5c428c656b8 Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Fri, 28 Mar 2025 10:27:02 -0400 Subject: [PATCH 113/132] [BI-2567] cleaned up code (moved code from a foreach loop to a simular for loop) --- .../brapi/v2/services/BrAPIGermplasmService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 5242041c9..7272e2b90 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java @@ -189,10 +189,9 @@ public List getGermplasmByList(UUID programId, String listDbId) List germplasm = germplasmDAO.getGermplasmByRawName(germplasmNames, programId); Map germplasmByName = new HashMap<>(); - // set the list ID in the germplasm additional info - germplasm.forEach(x -> x.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_LIST_ID, listId)); - for (BrAPIGermplasm g : germplasm) { + // set the list ID in the germplasm additional info + g.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_LIST_ID, listId); // Add to map. germplasmByName.put(g.getGermplasmName(), g); } From dccdf964190f1e9576a098d0a7bc2d2b787fc766 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Fri, 4 Apr 2025 13:11:29 +0000 Subject: [PATCH 114/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 8286ff703..ec1e82717 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+918 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/d4258a0f808cc45ec797526c6470421b9a0d0366 +version=v1.1.0+920 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/83f392e18d60bba157751d18ae72699b68824f3a From 116be9e8ffaa176eed073edbf4edcb7000d7a76f Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Mon, 7 Apr 2025 15:23:30 +0000 Subject: [PATCH 115/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index ec1e82717..b6fe9ac8d 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+920 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/83f392e18d60bba157751d18ae72699b68824f3a +version=v1.1.0+922 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/dccdf964190f1e9576a098d0a7bc2d2b787fc766 From a41e184ca688634e10ca85f6fa89aae789edd490 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Mon, 7 Apr 2025 17:00:27 -0400 Subject: [PATCH 116/132] Use appropriate ids for both preview and commit --- .../processors/GermplasmProcessor.java | 94 ++++++++++++++----- .../duplicate_names_circular_dependency.csv | 6 ++ 2 files changed, 78 insertions(+), 22 deletions(-) create mode 100644 src/test/resources/files/germplasm_import/duplicate_names_circular_dependency.csv 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 aad1720d9..b6987488c 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 @@ -17,6 +17,7 @@ package org.breedinginsight.brapps.importer.services.processors; import com.google.gson.Gson; +import com.google.gson.JsonElement; import io.micronaut.context.annotation.Property; import io.micronaut.context.annotation.Prototype; import io.micronaut.http.HttpStatus; @@ -58,6 +59,9 @@ import java.util.function.Supplier; import java.util.stream.Collectors; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + @Slf4j @Prototype public class GermplasmProcessor implements Processor { @@ -327,10 +331,7 @@ public Map process(ImportUpload upload, List
[ - ]) - // for !commit: Validate for circular pedigree dependencies. - createPostOrder(commit); + createPostOrder(); // Construct our response object return getStatisticsMap(importRows); @@ -428,11 +429,11 @@ private boolean canUpdatePedigree(BrAPIGermplasm existingGermplasm, Germplasm ge } private boolean hasPedigreeString(BrAPIGermplasm germplasm) { - return StringUtils.isNotBlank(germplasm.getPedigree()); + return isNotBlank(germplasm.getPedigree()); } private boolean hasPedigree(BrAPIGermplasm germplasm) { - return StringUtils.isNotBlank(germplasm.getPedigree()) + return isNotBlank(germplasm.getPedigree()) || germplasm.getAdditionalInfo().has(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_GID) || germplasm.getAdditionalInfo().has(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_GID) || (germplasm.getAdditionalInfo().has(BrAPIAdditionalInfoFields.FEMALE_PARENT_UNKNOWN) && @@ -458,9 +459,9 @@ private boolean arePedigreesEqual(BrAPIGermplasm existingGermplasm, Germplasm ge String existingMalePedigree = getParentId(existingGermplasm, existingPedigreeGIDString, BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_GID, BrAPIAdditionalInfoFields.MALE_PARENT_UNKNOWN); StringBuilder germplasmPedigreeGIDString = new StringBuilder(); - if (StringUtils.isNotBlank(germplasm.getFemaleParentAccessionNumber())) { + if (isNotBlank(germplasm.getFemaleParentAccessionNumber())) { germplasmPedigreeGIDString.append(germplasm.getFemaleParentAccessionNumber()); - } else if (StringUtils.isNotBlank(germplasm.getFemaleParentEntryNo())) { + } else if (isNotBlank(germplasm.getFemaleParentEntryNo())) { Integer femaleParentIdx = germplasmIndexByEntryNo.get(germplasm.getFemaleParentEntryNo()); BrAPIImport femaleParentRow = importRows.get(femaleParentIdx); BrAPIGermplasm femaleGerm = dbGermplasmByName.get(femaleParentRow.getGermplasm() @@ -474,9 +475,9 @@ private boolean arePedigreesEqual(BrAPIGermplasm existingGermplasm, Germplasm ge germplasmPedigreeGIDString.append(existingFemalePedigree); } germplasmPedigreeGIDString.append("/"); - if (StringUtils.isNotBlank(germplasm.getMaleParentAccessionNumber())) { + if (isNotBlank(germplasm.getMaleParentAccessionNumber())) { germplasmPedigreeGIDString.append(germplasm.getMaleParentAccessionNumber()); - } else if (StringUtils.isNotBlank(germplasm.getMaleParentEntryNo())) { + } else if (isNotBlank(germplasm.getMaleParentEntryNo())) { Integer maleParentIdx = germplasmIndexByEntryNo.get(germplasm.getMaleParentEntryNo()); BrAPIImport maleParentRow = importRows.get(maleParentIdx); BrAPIGermplasm maleGerm = dbGermplasmByName.get(maleParentRow.getGermplasm() @@ -518,7 +519,7 @@ private String getParentId(BrAPIGermplasm existingGermplasm, StringBuilder pedig private boolean canUpdatePedigreeNoEqualsCheck(BrAPIGermplasm existingGermplasm, Germplasm germplasm) { - return StringUtils.isBlank(existingGermplasm.getPedigree()) && + return isBlank(existingGermplasm.getPedigree()) && germplasm.pedigreeExists(); } @@ -550,25 +551,62 @@ private void validatePedigree(Germplasm germplasm, Integer rowNumber, Validation String femaleParentGID = germplasm.getFemaleParentAccessionNumber(); String maleParentGID = germplasm.getMaleParentAccessionNumber(); - if(StringUtils.isNotBlank(maleParentEntryNo) && StringUtils.isBlank(femaleParentEntryNo) && StringUtils.isBlank(femaleParentGID)) { + if(isNotBlank(maleParentEntryNo) && isBlank(femaleParentEntryNo) && isBlank(femaleParentGID)) { validationErrors.addError(rowNumber, new ValidationError("Male Parent Entry No", missingFemaleParent, HttpStatus.UNPROCESSABLE_ENTITY)); - } else if(StringUtils.isNotBlank(maleParentGID) && StringUtils.isBlank(femaleParentEntryNo) && StringUtils.isBlank(femaleParentGID)) { + } else if(isNotBlank(maleParentGID) && isBlank(femaleParentEntryNo) && isBlank(femaleParentGID)) { validationErrors.addError(rowNumber, new ValidationError("Male Parent GID", missingFemaleParent, HttpStatus.UNPROCESSABLE_ENTITY)); } } + private String getImportId(BrAPIGermplasm germplasm) { + String gid = germplasm.getAccessionNumber(); + String entryNo = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_IMPORT_ENTRY_NUMBER).getAsString(); + return generateImportId(gid, entryNo); + } + + private String getMotherImportId(BrAPIGermplasm germplasm) { + JsonElement motherGidElement = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_GID); + JsonElement motherEntryNoElement = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_ENTRY_NO); + String motherGid = !motherGidElement.isJsonNull() ? motherGidElement.getAsString() : null; + String motherEntryNo = !motherEntryNoElement.isJsonNull() ? motherEntryNoElement.getAsString() : null; + return generateImportId(motherGid, motherEntryNo); + } + + private String getFatherImportId(BrAPIGermplasm germplasm) { + JsonElement fatherGidElement = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_GID); + JsonElement fatherEntryNoElement = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_ENTRY_NO); + String fatherGid = !fatherGidElement.isJsonNull() ? fatherGidElement.getAsString() : null; + String fatherEntryNo = !fatherEntryNoElement.isJsonNull() ? fatherEntryNoElement.getAsString() : null; + return generateImportId(fatherGid, fatherEntryNo); + } + + private String generateImportId(String gid, String entryNo) { + if (gid == null && entryNo == null) return null; + return isNotBlank(gid) ? "GID " + gid : "ENTRY NO " + entryNo; + } + + private boolean maleParentPresent(BrAPIGermplasm germplasm) { + boolean fatherGidNull = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_GID).isJsonNull(); + boolean fatherEntryNoNull = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_ENTRY_NO).isJsonNull(); + return !fatherGidNull || !fatherEntryNoNull; + } + + private boolean femaleParentUnknown(BrAPIGermplasm germplasm) { + return germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.FEMALE_PARENT_UNKNOWN).getAsBoolean(); + } + + private boolean maleParentUnknown(BrAPIGermplasm germplasm) { + return germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.MALE_PARENT_UNKNOWN).getAsBoolean(); + } + /* This will set the postOrder and validate for circular pedigree dependencies. */ - private void createPostOrder(boolean commit) { + private void createPostOrder() { + Set created = null; // Construct a dependency tree for POSTing order - if(commit){ - created = existingGermplasm.stream().map(BrAPIGermplasm::getGermplasmName).collect(Collectors.toSet()); - } - else { - created = existingGermplasm.stream().map(BrAPIGermplasm::getDefaultDisplayName).collect(Collectors.toSet()); - } + created = existingGermplasm.stream().map(this::getImportId).collect(Collectors.toSet()); //todo this gets messy @@ -579,7 +617,7 @@ private void createPostOrder(boolean commit) { for (BrAPIGermplasm germplasm : newGermplasmList) { // If we've already planned this germplasm, skip - if ( (commit && created.contains(germplasm.getGermplasmName())) || (!commit && created.contains(germplasm.getDefaultDisplayName())) ) { + if (created.contains(getImportId(germplasm))) { continue; } @@ -589,8 +627,19 @@ private void createPostOrder(boolean commit) { continue; } + String femaleImportId = getMotherImportId(germplasm); + String maleImportId = getFatherImportId(germplasm); + + if (created.contains(femaleImportId) || femaleParentUnknown(germplasm)) { + if (!maleParentPresent(germplasm) || created.contains(maleImportId) || maleParentUnknown(germplasm)) { + createList.add(germplasm); + } + } + + /* // If both parents have been created already, add it List pedigreeArray = List.of(germplasm.getPedigree().split("/")); + // name + gid or name + entry no if no gid String femaleParent = pedigreeArray.get(0); String maleParent = pedigreeArray.size() > 1 ? pedigreeArray.get(1) : null; if (created.contains(femaleParent) || germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.FEMALE_PARENT_UNKNOWN).getAsBoolean()) { @@ -598,11 +647,12 @@ private void createPostOrder(boolean commit) { createList.add(germplasm); } } + */ } totalRecorded += createList.size(); if (createList.size() > 0) { - created.addAll(createList.stream().map(BrAPIGermplasm::getGermplasmName).collect(Collectors.toList())); + created.addAll(createList.stream().map(this::getImportId).collect(Collectors.toList())); postOrder.add(createList); } else if (totalRecorded < newGermplasmList.size()) { // We ran into circular dependencies, throw an error diff --git a/src/test/resources/files/germplasm_import/duplicate_names_circular_dependency.csv b/src/test/resources/files/germplasm_import/duplicate_names_circular_dependency.csv new file mode 100644 index 000000000..a602f6233 --- /dev/null +++ b/src/test/resources/files/germplasm_import/duplicate_names_circular_dependency.csv @@ -0,0 +1,6 @@ +GID,Germplasm Name,Breeding Method,Source,Female Parent GID,Male Parent GID,Entry No,Female Parent Entry No,Male Parent Entry No,External UID,Synonyms +,TestDup,BCR,Test,1,2,1,,,1, +,TestDup,BCR,Test,1,,2,,3,2, +,TestDup,BCR,Test,,,3,,,3, +,TestDup,BCR,Test,,,4,,,4, +,TestDup,BCR,Test,,,5,3,4,5, \ No newline at end of file From 8a134c345160b532096a55cf949af10a7113c9ba Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:02:39 -0400 Subject: [PATCH 117/132] Refactored to clean up a bit --- .../germplasm/GermplasmImportService.java | 7 +- .../germplasm/GermplasmImportIdUtils.java | 94 +++++++++++++++++++ .../{ => germplasm}/GermplasmProcessor.java | 91 ++++-------------- .../importer/GermplasmFileImportTest.java | 3 +- 4 files changed, 114 insertions(+), 81 deletions(-) create mode 100644 src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmImportIdUtils.java rename src/main/java/org/breedinginsight/brapps/importer/services/processors/{ => germplasm}/GermplasmProcessor.java (89%) diff --git a/src/main/java/org/breedinginsight/brapps/importer/model/imports/germplasm/GermplasmImportService.java b/src/main/java/org/breedinginsight/brapps/importer/model/imports/germplasm/GermplasmImportService.java index 0caebe65e..64d4c3b23 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/model/imports/germplasm/GermplasmImportService.java +++ b/src/main/java/org/breedinginsight/brapps/importer/model/imports/germplasm/GermplasmImportService.java @@ -18,18 +18,13 @@ package org.breedinginsight.brapps.importer.model.imports.germplasm; import lombok.extern.slf4j.Slf4j; -import org.breedinginsight.brapps.importer.model.ImportUpload; -import org.breedinginsight.brapps.importer.model.imports.BrAPIImport; import org.breedinginsight.brapps.importer.model.imports.BrAPIImportService; import org.breedinginsight.brapps.importer.model.imports.ImportServiceContext; import org.breedinginsight.brapps.importer.model.response.ImportPreviewResponse; import org.breedinginsight.brapps.importer.model.workflow.ImportWorkflow; -import org.breedinginsight.brapps.importer.services.processors.GermplasmProcessor; +import org.breedinginsight.brapps.importer.services.processors.germplasm.GermplasmProcessor; import org.breedinginsight.brapps.importer.services.processors.Processor; import org.breedinginsight.brapps.importer.services.processors.ProcessorManager; -import org.breedinginsight.model.Program; -import org.breedinginsight.model.User; -import tech.tablesaw.api.Table; import javax.inject.Inject; import javax.inject.Provider; diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmImportIdUtils.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmImportIdUtils.java new file mode 100644 index 000000000..4fe10e0c7 --- /dev/null +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmImportIdUtils.java @@ -0,0 +1,94 @@ +package org.breedinginsight.brapps.importer.services.processors.germplasm; + +import com.google.gson.JsonElement; +import org.apache.commons.lang3.StringUtils; +import org.brapi.v2.model.germ.BrAPIGermplasm; +import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; + +/** + * Utility class for managing germplasm import identifiers and pedigree relationships. + */ +public class GermplasmImportIdUtils { + + private GermplasmImportIdUtils() { + // Private constructor to prevent instantiation + } + + /** + * Generates an import ID for a germplasm based on its GID or entry number. + * @param gid The germplasm ID + * @param entryNo The entry number + * @return The generated import ID or null if both parameters are null + */ + public static String generateImportId(String gid, String entryNo) { + if (gid == null && entryNo == null) return null; + return StringUtils.isNotBlank(gid) ? "GID " + gid : "ENTRY NO " + entryNo; + } + + /** + * Gets the import ID for a germplasm. + * @param germplasm The germplasm object + * @return The import ID + */ + public static String getImportId(BrAPIGermplasm germplasm) { + String gid = germplasm.getAccessionNumber(); + String entryNo = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_IMPORT_ENTRY_NUMBER).getAsString(); + return generateImportId(gid, entryNo); + } + + /** + * Gets the import ID for the mother/female parent of a germplasm. + * @param germplasm The germplasm object + * @return The import ID of the mother/female parent + */ + public static String getMotherImportId(BrAPIGermplasm germplasm) { + JsonElement motherGidElement = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_GID); + JsonElement motherEntryNoElement = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_ENTRY_NO); + String motherGid = !motherGidElement.isJsonNull() ? motherGidElement.getAsString() : null; + String motherEntryNo = !motherEntryNoElement.isJsonNull() ? motherEntryNoElement.getAsString() : null; + return generateImportId(motherGid, motherEntryNo); + } + + /** + * Gets the import ID for the father/male parent of a germplasm. + * @param germplasm The germplasm object + * @return The import ID of the father/male parent + */ + public static String getFatherImportId(BrAPIGermplasm germplasm) { + JsonElement fatherGidElement = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_GID); + JsonElement fatherEntryNoElement = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_ENTRY_NO); + String fatherGid = !fatherGidElement.isJsonNull() ? fatherGidElement.getAsString() : null; + String fatherEntryNo = !fatherEntryNoElement.isJsonNull() ? fatherEntryNoElement.getAsString() : null; + return generateImportId(fatherGid, fatherEntryNo); + } + + /** + * Checks if a male parent is present for a germplasm. + * @param germplasm The germplasm object + * @return true if a male parent is present, false otherwise + */ + public static boolean maleParentPresent(BrAPIGermplasm germplasm) { + boolean fatherGidNull = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_GID).isJsonNull(); + boolean fatherEntryNoNull = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_ENTRY_NO).isJsonNull(); + return !fatherGidNull || !fatherEntryNoNull; + } + + /** + * Checks if the female parent is unknown for a germplasm. + * @param germplasm The germplasm object + * @return true if the female parent is unknown, false otherwise + */ + public static boolean femaleParentUnknown(BrAPIGermplasm germplasm) { + return germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.FEMALE_PARENT_UNKNOWN).getAsBoolean(); + } + + /** + * Checks if the male parent is unknown for a germplasm. + * @param germplasm The germplasm object + * @return true if the male parent is unknown, false otherwise + */ + public static boolean maleParentUnknown(BrAPIGermplasm germplasm) { + return germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.MALE_PARENT_UNKNOWN).getAsBoolean(); + } + +} \ No newline at end of file diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/GermplasmProcessor.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmProcessor.java similarity index 89% rename from src/main/java/org/breedinginsight/brapps/importer/services/processors/GermplasmProcessor.java rename to src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmProcessor.java index b6987488c..d4d2389a8 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/GermplasmProcessor.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmProcessor.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.breedinginsight.brapps.importer.services.processors; +package org.breedinginsight.brapps.importer.services.processors.germplasm; import com.google.gson.Gson; import com.google.gson.JsonElement; @@ -43,6 +43,7 @@ import org.breedinginsight.brapps.importer.model.response.ImportObjectState; import org.breedinginsight.brapps.importer.model.response.ImportPreviewStatistics; import org.breedinginsight.brapps.importer.model.response.PendingImportObject; +import org.breedinginsight.brapps.importer.services.processors.Processor; import org.breedinginsight.dao.db.tables.pojos.ProgramBreedingMethodEntity; import org.breedinginsight.daos.BreedingMethodDAO; import org.breedinginsight.model.Program; @@ -59,9 +60,6 @@ import java.util.function.Supplier; import java.util.stream.Collectors; -import static org.apache.commons.lang3.StringUtils.isBlank; -import static org.apache.commons.lang3.StringUtils.isNotBlank; - @Slf4j @Prototype public class GermplasmProcessor implements Processor { @@ -429,11 +427,11 @@ private boolean canUpdatePedigree(BrAPIGermplasm existingGermplasm, Germplasm ge } private boolean hasPedigreeString(BrAPIGermplasm germplasm) { - return isNotBlank(germplasm.getPedigree()); + return StringUtils.isNotBlank(germplasm.getPedigree()); } private boolean hasPedigree(BrAPIGermplasm germplasm) { - return isNotBlank(germplasm.getPedigree()) + return StringUtils.isNotBlank(germplasm.getPedigree()) || germplasm.getAdditionalInfo().has(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_GID) || germplasm.getAdditionalInfo().has(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_GID) || (germplasm.getAdditionalInfo().has(BrAPIAdditionalInfoFields.FEMALE_PARENT_UNKNOWN) && @@ -459,9 +457,9 @@ private boolean arePedigreesEqual(BrAPIGermplasm existingGermplasm, Germplasm ge String existingMalePedigree = getParentId(existingGermplasm, existingPedigreeGIDString, BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_GID, BrAPIAdditionalInfoFields.MALE_PARENT_UNKNOWN); StringBuilder germplasmPedigreeGIDString = new StringBuilder(); - if (isNotBlank(germplasm.getFemaleParentAccessionNumber())) { + if (StringUtils.isNotBlank(germplasm.getFemaleParentAccessionNumber())) { germplasmPedigreeGIDString.append(germplasm.getFemaleParentAccessionNumber()); - } else if (isNotBlank(germplasm.getFemaleParentEntryNo())) { + } else if (StringUtils.isNotBlank(germplasm.getFemaleParentEntryNo())) { Integer femaleParentIdx = germplasmIndexByEntryNo.get(germplasm.getFemaleParentEntryNo()); BrAPIImport femaleParentRow = importRows.get(femaleParentIdx); BrAPIGermplasm femaleGerm = dbGermplasmByName.get(femaleParentRow.getGermplasm() @@ -475,9 +473,9 @@ private boolean arePedigreesEqual(BrAPIGermplasm existingGermplasm, Germplasm ge germplasmPedigreeGIDString.append(existingFemalePedigree); } germplasmPedigreeGIDString.append("/"); - if (isNotBlank(germplasm.getMaleParentAccessionNumber())) { + if (StringUtils.isNotBlank(germplasm.getMaleParentAccessionNumber())) { germplasmPedigreeGIDString.append(germplasm.getMaleParentAccessionNumber()); - } else if (isNotBlank(germplasm.getMaleParentEntryNo())) { + } else if (StringUtils.isNotBlank(germplasm.getMaleParentEntryNo())) { Integer maleParentIdx = germplasmIndexByEntryNo.get(germplasm.getMaleParentEntryNo()); BrAPIImport maleParentRow = importRows.get(maleParentIdx); BrAPIGermplasm maleGerm = dbGermplasmByName.get(maleParentRow.getGermplasm() @@ -519,7 +517,7 @@ private String getParentId(BrAPIGermplasm existingGermplasm, StringBuilder pedig private boolean canUpdatePedigreeNoEqualsCheck(BrAPIGermplasm existingGermplasm, Germplasm germplasm) { - return isBlank(existingGermplasm.getPedigree()) && + return StringUtils.isBlank(existingGermplasm.getPedigree()) && germplasm.pedigreeExists(); } @@ -551,54 +549,13 @@ private void validatePedigree(Germplasm germplasm, Integer rowNumber, Validation String femaleParentGID = germplasm.getFemaleParentAccessionNumber(); String maleParentGID = germplasm.getMaleParentAccessionNumber(); - if(isNotBlank(maleParentEntryNo) && isBlank(femaleParentEntryNo) && isBlank(femaleParentGID)) { + if(StringUtils.isNotBlank(maleParentEntryNo) && StringUtils.isBlank(femaleParentEntryNo) && StringUtils.isBlank(femaleParentGID)) { validationErrors.addError(rowNumber, new ValidationError("Male Parent Entry No", missingFemaleParent, HttpStatus.UNPROCESSABLE_ENTITY)); - } else if(isNotBlank(maleParentGID) && isBlank(femaleParentEntryNo) && isBlank(femaleParentGID)) { + } else if(StringUtils.isNotBlank(maleParentGID) && StringUtils.isBlank(femaleParentEntryNo) && StringUtils.isBlank(femaleParentGID)) { validationErrors.addError(rowNumber, new ValidationError("Male Parent GID", missingFemaleParent, HttpStatus.UNPROCESSABLE_ENTITY)); } } - private String getImportId(BrAPIGermplasm germplasm) { - String gid = germplasm.getAccessionNumber(); - String entryNo = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_IMPORT_ENTRY_NUMBER).getAsString(); - return generateImportId(gid, entryNo); - } - - private String getMotherImportId(BrAPIGermplasm germplasm) { - JsonElement motherGidElement = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_GID); - JsonElement motherEntryNoElement = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_ENTRY_NO); - String motherGid = !motherGidElement.isJsonNull() ? motherGidElement.getAsString() : null; - String motherEntryNo = !motherEntryNoElement.isJsonNull() ? motherEntryNoElement.getAsString() : null; - return generateImportId(motherGid, motherEntryNo); - } - - private String getFatherImportId(BrAPIGermplasm germplasm) { - JsonElement fatherGidElement = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_GID); - JsonElement fatherEntryNoElement = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_ENTRY_NO); - String fatherGid = !fatherGidElement.isJsonNull() ? fatherGidElement.getAsString() : null; - String fatherEntryNo = !fatherEntryNoElement.isJsonNull() ? fatherEntryNoElement.getAsString() : null; - return generateImportId(fatherGid, fatherEntryNo); - } - - private String generateImportId(String gid, String entryNo) { - if (gid == null && entryNo == null) return null; - return isNotBlank(gid) ? "GID " + gid : "ENTRY NO " + entryNo; - } - - private boolean maleParentPresent(BrAPIGermplasm germplasm) { - boolean fatherGidNull = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_GID).isJsonNull(); - boolean fatherEntryNoNull = germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_ENTRY_NO).isJsonNull(); - return !fatherGidNull || !fatherEntryNoNull; - } - - private boolean femaleParentUnknown(BrAPIGermplasm germplasm) { - return germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.FEMALE_PARENT_UNKNOWN).getAsBoolean(); - } - - private boolean maleParentUnknown(BrAPIGermplasm germplasm) { - return germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.MALE_PARENT_UNKNOWN).getAsBoolean(); - } - /* This will set the postOrder and validate for circular pedigree dependencies. */ @@ -606,7 +563,7 @@ private void createPostOrder() { Set created = null; // Construct a dependency tree for POSTing order - created = existingGermplasm.stream().map(this::getImportId).collect(Collectors.toSet()); + created = existingGermplasm.stream().map(GermplasmImportIdUtils::getImportId).collect(Collectors.toSet()); //todo this gets messy @@ -617,7 +574,7 @@ private void createPostOrder() { for (BrAPIGermplasm germplasm : newGermplasmList) { // If we've already planned this germplasm, skip - if (created.contains(getImportId(germplasm))) { + if (created.contains(GermplasmImportIdUtils.getImportId(germplasm))) { continue; } @@ -627,32 +584,20 @@ private void createPostOrder() { continue; } - String femaleImportId = getMotherImportId(germplasm); - String maleImportId = getFatherImportId(germplasm); + String femaleImportId = GermplasmImportIdUtils.getMotherImportId(germplasm); + String maleImportId = GermplasmImportIdUtils.getFatherImportId(germplasm); - if (created.contains(femaleImportId) || femaleParentUnknown(germplasm)) { - if (!maleParentPresent(germplasm) || created.contains(maleImportId) || maleParentUnknown(germplasm)) { + if (created.contains(femaleImportId) || GermplasmImportIdUtils.femaleParentUnknown(germplasm)) { + if (!GermplasmImportIdUtils.maleParentPresent(germplasm) || created.contains(maleImportId) || GermplasmImportIdUtils.maleParentUnknown(germplasm)) { createList.add(germplasm); } } - /* - // If both parents have been created already, add it - List pedigreeArray = List.of(germplasm.getPedigree().split("/")); - // name + gid or name + entry no if no gid - String femaleParent = pedigreeArray.get(0); - String maleParent = pedigreeArray.size() > 1 ? pedigreeArray.get(1) : null; - if (created.contains(femaleParent) || germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.FEMALE_PARENT_UNKNOWN).getAsBoolean()) { - if (maleParent == null || created.contains(maleParent) || germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.MALE_PARENT_UNKNOWN).getAsBoolean()) { - createList.add(germplasm); - } - } - */ } totalRecorded += createList.size(); if (createList.size() > 0) { - created.addAll(createList.stream().map(this::getImportId).collect(Collectors.toList())); + created.addAll(createList.stream().map(GermplasmImportIdUtils::getImportId).collect(Collectors.toList())); postOrder.add(createList); } else if (totalRecorded < newGermplasmList.size()) { // We ran into circular dependencies, throw an error diff --git a/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java b/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java index d248d42ad..a9985273b 100644 --- a/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java +++ b/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java @@ -20,7 +20,7 @@ import org.breedinginsight.brapps.importer.model.imports.germplasm.GermplasmImportService; import org.breedinginsight.brapps.importer.model.response.ImportObjectState; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; -import org.breedinginsight.brapps.importer.services.processors.GermplasmProcessor; +import org.breedinginsight.brapps.importer.services.processors.germplasm.GermplasmProcessor; import org.breedinginsight.dao.db.tables.pojos.BiUserEntity; import org.breedinginsight.dao.db.tables.pojos.ProgramBreedingMethodEntity; import org.breedinginsight.daos.BreedingMethodDAO; @@ -32,7 +32,6 @@ import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import tech.tablesaw.api.Row; import tech.tablesaw.api.Table; import javax.inject.Inject; From aa4a43eec0de4418a717484d4f9dcd96a23efe7e Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:06:57 -0400 Subject: [PATCH 118/132] Added file header --- .../germplasm/GermplasmImportIdUtils.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmImportIdUtils.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmImportIdUtils.java index 4fe10e0c7..5c695aaee 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmImportIdUtils.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmImportIdUtils.java @@ -1,3 +1,19 @@ +/* + * 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.brapps.importer.services.processors.germplasm; import com.google.gson.JsonElement; From 8610b9a5849e0ce306e07d30abc3fbc1ff0aeaac Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:45:37 -0400 Subject: [PATCH 119/132] Added test case --- .../importer/GermplasmFileImportTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java b/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java index a9985273b..13d60a714 100644 --- a/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java +++ b/src/test/java/org/breedinginsight/brapps/importer/GermplasmFileImportTest.java @@ -786,6 +786,32 @@ public void entryNoDescending(boolean commit) { checkEntryNoFields(fileData, previewRows, commit, listName, listDescription, "EntryNoDescGerm 2", "EntryNoDescGerm 1"); } + /** + * Prior to BI-2593 this file would result in a false positive circular dependency error. This was due to germplasm + * references not being unique in the preview and commit phases of the postOrder method. + * + * @param commit controls whether import is preview or commit + */ + @Order(7) // want some existing gids to reference + @ParameterizedTest + @ValueSource(booleans = {false, true}) + @SneakyThrows + public void duplicateNames(boolean commit) { + String pathname = "src/test/resources/files/germplasm_import/duplicate_names_circular_dependency.csv"; + Table fileData = Table.read().file(pathname); + String listName = "DuplicateNames"; + String listDescription = "Duplicate names with pedigree"; + + JsonObject result = importGermplasm(pathname, listName, listDescription, commit); + assertEquals(200, result.getAsJsonObject("progress").get("statuscode").getAsInt()); + + // preview table is sorted by entry number + fileData = fileData.sortAscendingOn("Entry No"); + + JsonArray previewRows = result.get("preview").getAsJsonObject().get("rows").getAsJsonArray(); + checkEntryNoFields(fileData, previewRows, commit, listName, listDescription, "TestDup", "TestDup"); + } + /** * Shared method to perform entry number order tests for preview and commit * From 5c1b90a8fb8f51f5af31ef976e3afd8827b78609 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:49:31 -0400 Subject: [PATCH 120/132] Switched to using gids to track germplasm --- .../v2/services/BrAPIGermplasmService.java | 11 ++++--- .../breedinginsight/utilities/Utilities.java | 32 +++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) 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 7272e2b90..e0602e046 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java @@ -187,13 +187,13 @@ public List getGermplasmByList(UUID programId, String listDbId) // get list BrAPI germplasm variables List germplasmNames = listResponse.getResult().getData(); List germplasm = germplasmDAO.getGermplasmByRawName(germplasmNames, programId); - Map germplasmByName = new HashMap<>(); + Map germplasmByGid = new HashMap<>(); for (BrAPIGermplasm g : germplasm) { // set the list ID in the germplasm additional info g.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_LIST_ID, listId); // Add to map. - germplasmByName.put(g.getGermplasmName(), g); + germplasmByGid.put(g.getAccessionNumber(), g); } // Get the program key. @@ -201,12 +201,15 @@ public List getGermplasmByList(UUID programId, String listDbId) .orElseThrow(ApiException::new) .getKey(); + // Extract gids from list names + List gids = germplasmNames.stream().map(Utilities::extractGid).collect(Collectors.toList()); + // Build list from BrAPI list that preserves ordering and duplicates and assigns sequential entry numbers. List germplasmList = new ArrayList<>(); int entryNumber = 0; - for (String germplasmName : germplasmNames) { + for (String gid : gids) { ++entryNumber; - BrAPIGermplasm listEntry = cloneBrAPIGermplasm(germplasmByName.get(Utilities.removeProgramKeyAndUnknownAdditionalData(germplasmName, programKey))); + BrAPIGermplasm listEntry = cloneBrAPIGermplasm(germplasmByGid.get(gid)); // Set entry number. listEntry.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_IMPORT_ENTRY_NUMBER, entryNumber); germplasmList.add(listEntry); diff --git a/src/main/java/org/breedinginsight/utilities/Utilities.java b/src/main/java/org/breedinginsight/utilities/Utilities.java index 20f3254d6..31990928c 100644 --- a/src/main/java/org/breedinginsight/utilities/Utilities.java +++ b/src/main/java/org/breedinginsight/utilities/Utilities.java @@ -29,6 +29,7 @@ import java.sql.Statement; import java.util.*; import java.util.function.Function; +import java.util.regex.Matcher; import java.util.regex.Pattern; public class Utilities { @@ -183,6 +184,37 @@ public static String removeProgramKeyAndUnknownAdditionalData(String original, S return stripped; } + /** + * Extracts the germplasm identifier (GID) from a germplasm name string that contains + * a key in the format "[PROGKEY-NUMBER]". + * + *

This method searches for a pattern matching "[anything-digits]" in the input string + * and returns the numeric portion if found. The prefix before the hyphen can be any sequence + * of characters.

+ * + * @param germplasmNameWithKey The germplasm name string containing the identifier in the format + * "[PROGKEY-NUMBER]", e.g., "TestDup [DEMO-12]" + * @return The numeric portion after the hyphen as a String if the pattern is found, + * or null if the pattern is not found in the input string + * @throws NullPointerException If the input string is null + * + * @example + *
+     * String gid = extractGid("TestDup [DEMO-12]"); // Returns "12"
+     * String gid = extractGid("Wheat [BRC-789]");   // Returns "789"
+     * String gid = extractGid("NoPattern");         // Returns null
+     * 
+ */ + public static String extractGid(String germplasmNameWithKey) { + Pattern pattern = Pattern.compile("\\[(.*?)-(\\d+)\\]"); + Matcher matcher = pattern.matcher(germplasmNameWithKey); + + if (matcher.find()) { + return matcher.group(2); + } + return null; + } + public static String generateApiExceptionLogMessage(ApiException e) { return new StringBuilder("BrAPI Exception: \n\t").append("message: ") .append(e.getMessage()) From 7bdf11c00b9322aa8eb1cce3f99fc5c439b59df2 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Wed, 9 Apr 2025 15:31:46 +0000 Subject: [PATCH 121/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index b6fe9ac8d..88a710d18 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+922 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/dccdf964190f1e9576a098d0a7bc2d2b787fc766 +version=v1.1.0+924 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/8c0ed2278a591998781b39c656d22c314c2cf897 From d982380e83ae791af5a8ed1c3f7a6e1ee6cbba58 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Wed, 9 Apr 2025 15:50:57 -0400 Subject: [PATCH 122/132] Removed unused code --- .../brapi/v2/services/BrAPIGermplasmService.java | 5 ----- 1 file changed, 5 deletions(-) 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 e0602e046..7e4a01e67 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java @@ -196,11 +196,6 @@ public List getGermplasmByList(UUID programId, String listDbId) germplasmByGid.put(g.getAccessionNumber(), g); } - // Get the program key. - String programKey = programService.getById(programId) - .orElseThrow(ApiException::new) - .getKey(); - // Extract gids from list names List gids = germplasmNames.stream().map(Utilities::extractGid).collect(Collectors.toList()); From c643ea074447e0d86ac6e2fb965635eb6790b883 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Wed, 9 Apr 2025 19:53:31 +0000 Subject: [PATCH 123/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 88a710d18..370a8d713 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+924 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/8c0ed2278a591998781b39c656d22c314c2cf897 +version=v1.1.0+928 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/88640ec738554246f68fdcb78ba1790e046ac9cb From 1062f71cafcc585a8f494df2e91f22701993a7dc Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 15 Apr 2025 13:11:31 -0400 Subject: [PATCH 124/132] [BI-2595] - added logging --- .../org/breedinginsight/services/UserService.java | 2 ++ .../breedinginsight/utilities/email/EmailUtil.java | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/breedinginsight/services/UserService.java b/src/main/java/org/breedinginsight/services/UserService.java index 7502ff8b2..b78fdf8a6 100644 --- a/src/main/java/org/breedinginsight/services/UserService.java +++ b/src/main/java/org/breedinginsight/services/UserService.java @@ -390,6 +390,8 @@ private void sendAccountSignUpEmail(BiUserEntity user, SignedJWT jwtToken) { String filledBody = emailTemplate.render(); String subject = "Activate DeltaBreed Account"; + log.debug(filledBody); + // Send email emailUtil.sendEmail(user.getEmail(), subject, filledBody); } diff --git a/src/main/java/org/breedinginsight/utilities/email/EmailUtil.java b/src/main/java/org/breedinginsight/utilities/email/EmailUtil.java index a839715a1..7aef12550 100644 --- a/src/main/java/org/breedinginsight/utilities/email/EmailUtil.java +++ b/src/main/java/org/breedinginsight/utilities/email/EmailUtil.java @@ -19,6 +19,7 @@ import io.micronaut.context.annotation.Property; import io.micronaut.http.server.exceptions.HttpServerException; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import javax.inject.Singleton; @@ -29,6 +30,7 @@ import java.util.Date; import java.util.Properties; +@Slf4j @Singleton public class EmailUtil { @@ -66,6 +68,8 @@ protected PasswordAuthentication getPasswordAuthentication() { public void sendEmail(String toEmail, String subject, String body){ try { + log.debug("Sending email to: " + toEmail + " from: " + fromEmail + " with subject: " + subject); + Session session = getSmtpHost(); MimeMessage msg = new MimeMessage(session); //set message headers @@ -83,11 +87,16 @@ public void sendEmail(String toEmail, String subject, String body){ msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail, false)); Transport.send(msg); + + log.debug("Email sent to: " + toEmail + " from: " + fromEmail + " with subject: " + subject); } - catch (UnsupportedEncodingException | MessagingException e) { + catch (UnsupportedEncodingException e) { + log.debug("UnsupportedEncodingException " + e.getMessage()); + throw new HttpServerException(e.getMessage()); + } catch (MessagingException e) { + log.debug("MessagingException " + e.getMessage()); throw new HttpServerException(e.getMessage()); } } - } From 7f081b6def4dd3a122ebce75667569780a8eac11 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Tue, 15 Apr 2025 17:19:31 +0000 Subject: [PATCH 125/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 370a8d713..d4a61b089 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+928 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/88640ec738554246f68fdcb78ba1790e046ac9cb +version=v1.1.0+934 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/c2698fe74ca8d93d4cb45d4d0faa91d3efc67840 From 1bdd132bb9240dd9b30f246c2dc213ad90c67b91 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Fri, 18 Apr 2025 16:23:01 -0400 Subject: [PATCH 126/132] [BI-2595] - reverted dependency changes --- pom.xml | 222 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 122 insertions(+), 100 deletions(-) diff --git a/pom.xml b/pom.xml index 4280fd0b1..fe89f2ca6 100644 --- a/pom.xml +++ b/pom.xml @@ -90,9 +90,11 @@ 4.9.3 4.3.1 2.1-SNAPSHOT - 2.9.2 - - 1.26.1 + 2.11.0 + 2.2.1 + + 4.1.2 + 4.1.2 1.6.2 4.3.1 1.0.0-SNAPSHOT @@ -377,14 +379,34 @@ ${brapi-java-client.version} - org.apache.tika - tika-app - ${tika-app.version} + org.apache.commons + commons-csv + ${apache-commons-csv.version} + + + org.apache.poi + poi + ${apache-poi.version} + + + org.apache.poi + poi-ooxml + ${apache-poi-ooxml.version} org.apache.commons - commons-compress - ${commons-compress.version} + commons-lang3 + ${apache-commons-lang.version} + + + commons-io + commons-io + ${commons-io.version} + + + org.apache.tika + tika-app + ${tika-app.version} com.sun.mail @@ -468,7 +490,7 @@ shade - + *:* @@ -477,9 +499,9 @@ META-INF/*.RSA - - - + + + ${exec.mainClass} @@ -514,98 +536,98 @@ ${maven.compiler.target} - - org.jooq - jooq-codegen-maven - ${jooq.version} - - - - generate - - - + + org.jooq + jooq-codegen-maven + ${jooq.version} + + + + generate + + + + + + + org.postgresql.Driver + jdbc:postgresql://${DB_SERVER}/${DB_NAME} + ${DB_USER} + ${DB_PASSWORD} + + + org.breedinginsight.generation.JooqDaoGenerator + + org.jooq.meta.postgres.PostgresDatabase + .* + public + true + true + false + true + true + true + true + false + + flyway_schema_history|base_entity|base_track_edit_entity|spatial_ref_sys + + - - - org.postgresql.Driver + + + + + + PASCAL + $0_ENTITY + + + PASCAL + $0_TABLE + +
+
+
+
+ + + org.breedinginsight.dao.db + target/generated-sources/jooq + + + true + true + false + true + +
+
+ + + org.breedinginsight + bi-jooq-codegen + ${jooq.version} + + +
+ + org.flywaydb + flyway-maven-plugin + ${flyway.version} + jdbc:postgresql://${DB_SERVER}/${DB_NAME} ${DB_USER} ${DB_PASSWORD} - - - org.breedinginsight.generation.JooqDaoGenerator - - org.jooq.meta.postgres.PostgresDatabase - .* - public - true - true - false - true - true - true - true - false - - flyway_schema_history|base_entity|base_track_edit_entity|spatial_ref_sys - - - - - - - - - PASCAL - $0_ENTITY - - - PASCAL - $0_TABLE - -
-
-
-
- - - org.breedinginsight.dao.db - target/generated-sources/jooq - - - true - true - false - true - -
-
- - - org.breedinginsight - bi-jooq-codegen - ${jooq.version} - - -
- - org.flywaydb - flyway-maven-plugin - ${flyway.version} - - jdbc:postgresql://${DB_SERVER}/${DB_NAME} - ${DB_USER} - ${DB_PASSWORD} - - - - org.postgresql - postgresql - ${postgres.version} - - - + + + + org.postgresql + postgresql + ${postgres.version} + + +
From baa7cc0af7f96f3bc759e895aa009de4813a34de Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Wed, 23 Apr 2025 21:31:54 +0000 Subject: [PATCH 127/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index d4a61b089..dda4d99a7 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+934 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/c2698fe74ca8d93d4cb45d4d0faa91d3efc67840 +version=v1.1.0+940 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/fa351401536751d7103ddf509e4d311ca34994bf From 88d42cdda9e212e689e1ad348a07e903706d502d Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Thu, 24 Apr 2025 14:58:40 -0400 Subject: [PATCH 128/132] [REVERT ME] - temporary test configuration change --- src/test/java/org/breedinginsight/BrAPITest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/breedinginsight/BrAPITest.java b/src/test/java/org/breedinginsight/BrAPITest.java index 8c5c35bad..e6e6072d6 100644 --- a/src/test/java/org/breedinginsight/BrAPITest.java +++ b/src/test/java/org/breedinginsight/BrAPITest.java @@ -49,7 +49,7 @@ public class BrAPITest extends DatabaseTest { public BrAPITest() { super(); - brapiContainer = new GenericContainer<>("breedinginsight/brapi-java-server:develop") + brapiContainer = new GenericContainer<>("breedinginsight/brapi-java-server:rc") .withNetwork(super.getNetwork()) .withImagePullPolicy(PullPolicy.ageBased(Duration.ofMinutes(60))) .withExposedPorts(8080) From d3fd92241b5b992c94cf5982f6b16f730154d3ae Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Thu, 24 Apr 2025 19:37:30 +0000 Subject: [PATCH 129/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index dda4d99a7..99563b0b5 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+940 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/fa351401536751d7103ddf509e4d311ca34994bf +version=v1.1.0+943 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/88d42cdda9e212e689e1ad348a07e903706d502d From a05d1e1a83381577986ab7c5b904b4c7d39e4ffa Mon Sep 17 00:00:00 2001 From: David Randolph Phillips Date: Wed, 23 Apr 2025 16:00:22 -0400 Subject: [PATCH 130/132] [BI-2608] Changed column-name from 'Long' to 'Elevation' in error message --- .../create/workflow/steps/ValidatePendingImportObjectsStep.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/create/workflow/steps/ValidatePendingImportObjectsStep.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/create/workflow/steps/ValidatePendingImportObjectsStep.java index b1391c483..ee570c238 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/create/workflow/steps/ValidatePendingImportObjectsStep.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/create/workflow/steps/ValidatePendingImportObjectsStep.java @@ -363,7 +363,7 @@ private void validateGeoCoordinates(ValidationErrors validationErrors, int rowNu } if (elevationBadValue) { - ExperimentUtilities.addRowError(ExperimentObservation.Columns.LONG, "Invalid Elevation value (numerals expected)", validationErrors, rowNum); + ExperimentUtilities.addRowError(ExperimentObservation.Columns.ELEVATION, "Invalid Elevation value (numerals expected)", validationErrors, rowNum); } } From f5d06b40cc7770fb121fdec88de024558ecc2a92 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Tue, 29 Apr 2025 17:35:06 +0000 Subject: [PATCH 131/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 99563b0b5..3e26e3f83 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+943 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/88d42cdda9e212e689e1ad348a07e903706d502d +version=v1.1.0+948 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/d50dc656df8d551465d34fed891b89709dcb365c From bda55336d745e8b5cad7d33a8a295b70a45d13a8 Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Wed, 30 Apr 2025 16:02:41 +0000 Subject: [PATCH 132/132] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index c774c2cdf..a795160df 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -16,5 +16,5 @@ -version=v1.1.0+948 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/d50dc656df8d551465d34fed891b89709dcb365c +version=v1.1.0+951 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/4d8e00f84bd85e31d2ece453cf67c8d7fbe2a4d2