From 40c5f71f597a25f9952f2cc9e606d7e1505488bc Mon Sep 17 00:00:00 2001 From: Jason Loux Date: Tue, 1 Sep 2026 17:19:25 -0400 Subject: [PATCH 1/4] Add comments to fix during BrAPI endpoint fixing --- .../brapi/v2/BrAPIObservationUnitController.java | 1 + .../breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java | 4 ++-- .../org/breedinginsight/brapi/v2/dao/BrAPIPedigreeDAO.java | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java index 2be39af43..625816256 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitController.java @@ -226,6 +226,7 @@ private void setDbIds(BrAPIObservationUnit ou) { ou.programDbId(Utilities.getExternalReference(ou.getExternalReferences(), Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.PROGRAMS)) .orElseThrow(() -> new IllegalStateException("No BI external reference found")) .getReferenceID()); + // TODO: Remove this as part of [BI-3006] if (ou.getAdditionalInfo().has(BrAPIAdditionalInfoFields.GERMPLASM_UUID)) { ou.setGermplasmDbId(ou.getAdditionalInfo() .get(BrAPIAdditionalInfoFields.GERMPLASM_UUID) 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 550dc9b56..5406ea134 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationUnitDAO.java @@ -316,7 +316,7 @@ public List getObservationUnits(Program program, .orElse(true); //adding filter for germplasmDbId because we can't easily search that in the stored data object - // TODO: Add search on accessionNumber once it's been added to prod server and brapi client [BI-2978] + // TODO: Add search on germplasmDbId directly in search request [BI-3006] return matches && germplasmId.map(id -> id.equals(ou.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_UUID).getAsString())).orElse(true); }).collect(Collectors.toList()); } @@ -382,7 +382,7 @@ private void processObservationUnits(Program program, List HashMap germplasmByDbId = new HashMap<>(); if( withGID ){ - // TODO: Optimize this to use germplasm information directly in BrAPIObservationUnit by adding accession num/GID there via the prodserver/client [BI-2978] + // TODO: Optimize this to use germplasm information directly in BrAPIObservationUnit by searching on ou.germplasmDbIds in a GermplasmSearchRequest [BI-3006] this.germplasmService.getGermplasm(program.getId()).forEach((germplasm -> germplasmByDbId.put(germplasm.getGermplasmDbId(), germplasm))); } diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIPedigreeDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIPedigreeDAO.java index 3a164c07d..b89a28e7e 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIPedigreeDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIPedigreeDAO.java @@ -85,7 +85,7 @@ public List getPedigree( PedigreeQueryParams pedigreeRequest = new PedigreeQueryParams(); - // TODO: Issue with BrAPI server programDbId filtering, think germplasm are linked to program through observation + // TODO: Issue with BrAPI server programDbId filtering, think germplasm are linked to program through observation [BI- // units and doesn't work if don't have any loaded // use external refs instead for now //pedigreeSearchRequest.programDbIds(List.of(program.getBrapiProgram().getProgramDbId())); From 54ada76697d57cf52dd2bfe796bd84a4b78d0ae8 Mon Sep 17 00:00:00 2001 From: Jason Loux Date: Wed, 2 Sep 2026 15:14:48 -0400 Subject: [PATCH 2/4] Untagnle pedigree male/female UUID setting, use brapiGermplasmDbIds for this --- .../processors/germplasm/GermplasmProcessor.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmProcessor.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmProcessor.java index e0eaf3c53..a942bae3c 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmProcessor.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmProcessor.java @@ -842,10 +842,7 @@ else if (germplasmIndexByEntryNo.containsKey(germplasm.getFemaleParentEntryNo()) //entry number no longer needed for figuring out parentage, can remove (since same germplasm can have different entry numbers across multiple lists) brAPIGermplasm.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_ENTRY_NO, null); // Add femaleParentUUID to additionalInfo. - Optional femaleParentUUID = Utilities.getExternalReference(femaleParent.getExternalReferences(), BRAPI_REFERENCE_SOURCE); - if (femaleParentUUID.isPresent()) { - brAPIGermplasm.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_UUID, femaleParentUUID.get().getReferenceID()); - } + brAPIGermplasm.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_UUID, femaleParent.getGermplasmDbId()); } if (maleParent != null) { @@ -853,10 +850,8 @@ else if (germplasmIndexByEntryNo.containsKey(germplasm.getFemaleParentEntryNo()) //entry number no longer needed for figuring out parentage, can remove (since same germplasm can have different entry numbers across multiple lists) brAPIGermplasm.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_ENTRY_NO, null); // Add maleParentUUID to additionalInfo. - Optional maleParentUUID = Utilities.getExternalReference(maleParent.getExternalReferences(), BRAPI_REFERENCE_SOURCE); - if (maleParentUUID.isPresent()) { - brAPIGermplasm.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_UUID, maleParentUUID.get().getReferenceID()); - } } + brAPIGermplasm.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_UUID, maleParent.getGermplasmDbId()); + } } } } From 0196eb706379bd65537e8e862cde5399241436e3 Mon Sep 17 00:00:00 2001 From: Jason Loux Date: Fri, 4 Sep 2026 14:46:02 -0400 Subject: [PATCH 3/4] Reverse changes to setting male/femaleParentUUID to germplasmDbId, implement stopgap hack Unit tests made me aware of the use case where rows in a gemrplasm import can reference other rows in the import as parents, and when this happens, no germplasmDbId will be created until the germplasm is fully imported because of the way the import is organized. Until we manage to change this, I've created a stop-gap which will mutate this additional info on the fly from the bi-generated exrefs to the brapiGermplasmDbIds in the BrAPIGermplasmDAO.processGermplasmForDisplay method. This will lookup the germplasmDbIds of the pedigree nodes using the exrefs, and will mutate the additional info records for male/female IDs so that the links still work as expected in the frontend UI --- .../brapi/v2/dao/BrAPIGermplasmDAO.java | 96 ++++++++++++++++--- .../germplasm/GermplasmProcessor.java | 11 ++- 2 files changed, 89 insertions(+), 18 deletions(-) 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 ca896441e..ad1f6424e 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java +++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java @@ -188,14 +188,14 @@ private Map fetchProgramGermplasm(UUID programId) throws api::searchGermplasmPost, api::searchGermplasmSearchResultsDbIdGet, germplasmSearch), - program.getKey()); + program); } else { log.debug("Fetching germplasm without pagination to BrAPI"); return processGermplasmForDisplay(brAPIDAOUtil.searchNoPaging( api::searchGermplasmPost, api::searchGermplasmSearchResultsDbIdGet, germplasmSearch), - program.getKey()); + program); } } @@ -209,7 +209,8 @@ public void repopulateGermplasmCacheForProgram(UUID programId) { * @return Map * @throws ApiException */ - private Map processGermplasmForDisplay(List programGermplasm, String programKey) { + private Map processGermplasmForDisplay(List programGermplasm, + Program program) throws ApiException { // Process the germplasm Map programGermplasmMap = new HashMap<>(); log.trace("processing germ for display: " + programGermplasm); @@ -227,12 +228,23 @@ private Map processGermplasmForDisplay(Listmale/femaleParentUUID. (See GermplasmProcessor.constructPedigreeString() for assignment of this data) + // To avoid completely re-working the importer to create pedigree nodes and associated germplasm referenced first, this "hack" relates the bi-generated exrefs + // to the brapi-generated germplasmDbId via an extra lookup and processing to the database. + // This is necessary because we need to overwrite bi-generated ids in the pedigree string to use the brapi germplasmDbIds so that front end reference links work properly. + // This should always work because processGermplasmForDisplay is only ever called once Germplasm data has been created in the database, and provided we improve lookups to not fetch + // all program germplasm at once, the performance hit should be negligible. + // TODO: This hack can be removed once/if we implement [BI-2588/BI-2452] + Map pedigreeBrAPIGermplasmDbIdByBICreatedExRef = getPedigreeGermplasmDbIdByBICreatedExRef(programGermplasm, program); + // Update pedigree string for (BrAPIGermplasm germplasm: programGermplasm) { JsonObject additionalInfo = germplasm.getAdditionalInfo(); @@ -260,7 +272,7 @@ private Map processGermplasmForDisplay(List processGermplasmForDisplay(List processGermplasmForDisplay(List processGermplasmForDisplay(List getPedigreeGermplasmDbIdByBICreatedExRef(List brAPIGermplasm, Program program) throws ApiException { + Map germplasmDbIdByGeneratedExRef = new HashMap<>(); + + for (BrAPIGermplasm germplasm : brAPIGermplasm) { + if (germplasm.getAdditionalInfo().has(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_UUID)) { + germplasmDbIdByGeneratedExRef.put(germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_UUID).getAsString(), null); + } + if (germplasm.getAdditionalInfo().has(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_UUID)) { + germplasmDbIdByGeneratedExRef.put(germplasm.getAdditionalInfo().get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_UUID).getAsString(), null); + } + } + + List exRefIds = new ArrayList<>(germplasmDbIdByGeneratedExRef.keySet()); + + BrAPIGermplasmSearchRequest searchRequest = new BrAPIGermplasmSearchRequest(); + searchRequest.setExternalReferenceIds(exRefIds); + searchRequest.setExternalReferenceSources(List.of(referenceSource)); + + // Reuse some code to properly set searchRequest with brapiProgramDbId, and proper paging + searchRequest = buildSearchRequest(program, null, null, searchRequest); + + GermplasmApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(program.getId()), GermplasmApi.class); + + + List germplasmSearchedWithExRef = brAPIDAOUtil.searchNoPaging( + api::searchGermplasmPost, + api::searchGermplasmSearchResultsDbIdGet, + searchRequest); + + for (BrAPIGermplasm germplasm : germplasmSearchedWithExRef) { + Optional exRef = Utilities.getExternalReference(germplasm.getExternalReferences(), referenceSource); + + if (exRef.isPresent()) { + germplasmDbIdByGeneratedExRef.put(exRef.get().getReferenceId(), germplasm.getGermplasmDbId()); + } else { + throw new IllegalStateException("External references wasn't found for germplasm (dbid): " + germplasm.getGermplasmDbId()); + } + } + + return germplasmDbIdByGeneratedExRef; + } + /** * This method requires a BI-API program. If the BrAPIProgram inside this data model is not set, * this method will retrieve it. @@ -336,7 +392,7 @@ private List getBrAPIGermplasmUsingBrAPIProgramId(GermplasmQuery List result = brAPIDAOUtil.get(api::germplasmGet, germplasmQueryParams); // TODO: Once cache is removed for this class, fix processGermplasmForDisplay to return List [BI-2906] - return new ArrayList<>(processGermplasmForDisplay(result, program.getKey()).values()); + return new ArrayList<>(processGermplasmForDisplay(result, program).values()); } // TODO: hack for now, probably should update breedbase @@ -369,11 +425,11 @@ private String processBreedbasePedigree(String pedigree) { public List createBrAPIGermplasm(List postBrAPIGermplasmList, UUID programId, ImportUpload upload) { GermplasmApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), GermplasmApi.class); - var program = programDAO.fetchOneById(programId); + var program = new Program(programDAO.fetchOneById(programId)); try { if (!postBrAPIGermplasmList.isEmpty()) { List postResponse = brAPIDAOUtil.post(postBrAPIGermplasmList, upload, api::germplasmPost, importDAO::update); - return new ArrayList<>(processGermplasmForDisplay(postResponse, program.getKey()).values()); + return new ArrayList<>(processGermplasmForDisplay(postResponse, program).values()); } return new ArrayList<>(); } catch (Exception e) { @@ -383,12 +439,12 @@ public List createBrAPIGermplasm(List postBrAPIG public List updateBrAPIGermplasm(List putBrAPIGermplasmList, UUID programId, ImportUpload upload) { GermplasmApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), GermplasmApi.class); - var program = programDAO.fetchOneById(programId); + var program = new Program(programDAO.fetchOneById(programId)); try { if (!putBrAPIGermplasmList.isEmpty()) { Callable> postFunction = () -> { List putResponse = putGermplasm(putBrAPIGermplasmList, api); - return processGermplasmForDisplay(putResponse, program.getKey()); + return processGermplasmForDisplay(putResponse, program); }; return programGermplasmCache.post(programId, postFunction); } @@ -428,15 +484,25 @@ public BrAPIGermplasmListResponse brapiGermplasmSearchReturnResponse(Program pro // TODO: Once cache is removed for this class, fix processGermplasmForDisplay to return List [BI-2906] List processedGermplasm = - new ArrayList<>(processGermplasmForDisplay(brAPIDAOUtil.getListResult(brAPIResponse), program.getKey()).values()); + new ArrayList<>(processGermplasmForDisplay(brAPIDAOUtil.getListResult(brAPIResponse), program).values()); brAPIResponse.getResult().setData(processedGermplasm); return brAPIResponse; } - private BrAPIGermplasmSearchRequest buildSearchRequest(Program program, List brapiGermplasmIds, GermplasmQuery germplasmQuery) throws ApiException { - BrAPIGermplasmSearchRequest searchRequest = new BrAPIGermplasmSearchRequest(); + private BrAPIGermplasmSearchRequest buildSearchRequest(Program program, List brapiGermplasmIds, GermplasmQuery query) throws ApiException { + return buildSearchRequest(program, brapiGermplasmIds, query, null); + } + + private BrAPIGermplasmSearchRequest buildSearchRequest(Program program, List brapiGermplasmIds, GermplasmQuery germplasmQuery, BrAPIGermplasmSearchRequest searchRequestPassThru) throws ApiException { + BrAPIGermplasmSearchRequest searchRequest; + + if (searchRequestPassThru == null) { + searchRequest = new BrAPIGermplasmSearchRequest(); + } else { + searchRequest = searchRequestPassThru; + } searchRequest.programDbIds(List.of(brAPIDAOUtil.getBrAPIProgramDbId(program.getId()))); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmProcessor.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmProcessor.java index a942bae3c..e0eaf3c53 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmProcessor.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/germplasm/GermplasmProcessor.java @@ -842,7 +842,10 @@ else if (germplasmIndexByEntryNo.containsKey(germplasm.getFemaleParentEntryNo()) //entry number no longer needed for figuring out parentage, can remove (since same germplasm can have different entry numbers across multiple lists) brAPIGermplasm.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_ENTRY_NO, null); // Add femaleParentUUID to additionalInfo. - brAPIGermplasm.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_UUID, femaleParent.getGermplasmDbId()); + Optional femaleParentUUID = Utilities.getExternalReference(femaleParent.getExternalReferences(), BRAPI_REFERENCE_SOURCE); + if (femaleParentUUID.isPresent()) { + brAPIGermplasm.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_UUID, femaleParentUUID.get().getReferenceID()); + } } if (maleParent != null) { @@ -850,8 +853,10 @@ else if (germplasmIndexByEntryNo.containsKey(germplasm.getFemaleParentEntryNo()) //entry number no longer needed for figuring out parentage, can remove (since same germplasm can have different entry numbers across multiple lists) brAPIGermplasm.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_ENTRY_NO, null); // Add maleParentUUID to additionalInfo. - brAPIGermplasm.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_UUID, maleParent.getGermplasmDbId()); - } + Optional maleParentUUID = Utilities.getExternalReference(maleParent.getExternalReferences(), BRAPI_REFERENCE_SOURCE); + if (maleParentUUID.isPresent()) { + brAPIGermplasm.putAdditionalInfoItem(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_UUID, maleParentUUID.get().getReferenceID()); + } } } } } From 22622741f48ca8378072b502e4ca23893e4e2ac7 Mon Sep 17 00:00:00 2001 From: Jason Loux Date: Fri, 4 Sep 2026 15:32:51 -0400 Subject: [PATCH 4/4] Fix unit test --- .../breedinginsight/services/BrAPIGermplasmServiceUnitTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/org/breedinginsight/services/BrAPIGermplasmServiceUnitTest.java b/src/test/java/org/breedinginsight/services/BrAPIGermplasmServiceUnitTest.java index a47a3eebb..127feb94c 100644 --- a/src/test/java/org/breedinginsight/services/BrAPIGermplasmServiceUnitTest.java +++ b/src/test/java/org/breedinginsight/services/BrAPIGermplasmServiceUnitTest.java @@ -155,6 +155,7 @@ public void getGermplasmListExport() { when(programDAO.getProgramBrAPI(any())).thenReturn(brapiProgram); when(brAPIDAOUtil.get(any(Function.class), any(GermplasmQueryParams.class))).thenReturn(germplasm); + when(brAPIDAOUtil.getBrAPIProgramDbId(any())).thenReturn(brapiProgramDbId); //Create germplasm cache of stub data Method setupMethod = BrAPIGermplasmDAO.class.getDeclaredMethod("setup");