-
Notifications
You must be signed in to change notification settings - Fork 1
Untangle/Hack Pedigree UUID scheme so links work on fe #553
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/BI-2902
Are you sure you want to change the base?
Changes from all commits
40c5f71
54ada76
0196eb7
2262274
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -188,14 +188,14 @@ private Map<String, BrAPIGermplasm> 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<Key = string representing germplasm UUID, value = formatted BrAPIGermplasm> | ||
| * @throws ApiException | ||
| */ | ||
| private Map<String,BrAPIGermplasm> processGermplasmForDisplay(List<BrAPIGermplasm> programGermplasm, String programKey) { | ||
| private Map<String,BrAPIGermplasm> processGermplasmForDisplay(List<BrAPIGermplasm> programGermplasm, | ||
| Program program) throws ApiException { | ||
| // Process the germplasm | ||
| Map<String, BrAPIGermplasm> programGermplasmMap = new HashMap<>(); | ||
| log.trace("processing germ for display: " + programGermplasm); | ||
|
|
@@ -227,12 +228,23 @@ private Map<String,BrAPIGermplasm> processGermplasmForDisplay(List<BrAPIGermplas | |
| // Remove program key | ||
| if (germplasm.getSynonyms() != null && !germplasm.getSynonyms().isEmpty()) { | ||
| for (BrAPIGermplasmSynonyms synonym: germplasm.getSynonyms()) { | ||
| String newSynonym = Utilities.removeProgramKey(synonym.getSynonym(), programKey, germplasm.getAccessionNumber()); | ||
| String newSynonym = Utilities.removeProgramKey(synonym.getSynonym(), program.getKey(), germplasm.getAccessionNumber()); | ||
| synonym.setSynonym(newSynonym); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // With the programCache removal, any and all outward (fe, API endpoints) facing germplasm should be prepared to use the brapi-generated germplasmDbId instead of the bi-generated exref. | ||
| // This notion works fine for the higher level Germplasm entity, but our existing Pedigree implementation is predicated on the usage of bi-generated exref IDs | ||
| // stored in germplasm.additionalInfo->male/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<String, String> pedigreeBrAPIGermplasmDbIdByBICreatedExRef = getPedigreeGermplasmDbIdByBICreatedExRef(programGermplasm, program); | ||
|
|
||
| // Update pedigree string | ||
| for (BrAPIGermplasm germplasm: programGermplasm) { | ||
| JsonObject additionalInfo = germplasm.getAdditionalInfo(); | ||
|
|
@@ -260,7 +272,7 @@ private Map<String,BrAPIGermplasm> processGermplasmForDisplay(List<BrAPIGermplas | |
| if (!name.isEmpty()) | ||
| { | ||
| // Strip program key. | ||
| name = Utilities.removeProgramKeyAndUnknownAdditionalData(name, programKey); | ||
| name = Utilities.removeProgramKeyAndUnknownAdditionalData(name, program.getKey()); | ||
| } | ||
| parentNames.add(name); | ||
| } | ||
|
|
@@ -271,7 +283,8 @@ private Map<String,BrAPIGermplasm> processGermplasmForDisplay(List<BrAPIGermplas | |
| { | ||
| gidPedigreeString = additionalInfo.has(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_GID) ? additionalInfo.get(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_GID).getAsString() : ""; | ||
| namePedigreeString = parentNames.get(0); | ||
| uuidPedigreeString = additionalInfo.has(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_UUID) ? additionalInfo.get(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_UUID).getAsString() : ""; | ||
| // Convert the bi-generated additionalInfo parent ID to the brapi germplasmDbId using map from previous step. | ||
| uuidPedigreeString = additionalInfo.has(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_UUID) ? pedigreeBrAPIGermplasmDbIdByBICreatedExRef.get(additionalInfo.get(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_UUID).getAsString()) : ""; | ||
| // Throw a descriptive error if femaleParentUUID is absent. | ||
| if (!additionalInfo.has(BrAPIAdditionalInfoFields.GERMPLASM_FEMALE_PARENT_UUID)) { | ||
| String programId = "unknown program"; | ||
|
|
@@ -290,7 +303,8 @@ private Map<String,BrAPIGermplasm> processGermplasmForDisplay(List<BrAPIGermplas | |
| { | ||
| gidPedigreeString += "/" + (additionalInfo.has(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_GID) ? additionalInfo.get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_GID).getAsString() : ""); | ||
| namePedigreeString += "/" + parentNames.get(1); | ||
| uuidPedigreeString += "/" + (additionalInfo.has(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_UUID) ? additionalInfo.get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_UUID).getAsString() : ""); | ||
| // Convert the bi-generated additionalInfo parent ID to the brapi germplasmDbId using map from previous step. | ||
| uuidPedigreeString += "/" + (additionalInfo.has(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_UUID) ? pedigreeBrAPIGermplasmDbIdByBICreatedExRef.get(additionalInfo.get(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_UUID).getAsString()) : ""); | ||
| // Throw a descriptive error if maleParentUUID is absent. | ||
| if (!additionalInfo.has(BrAPIAdditionalInfoFields.GERMPLASM_MALE_PARENT_UUID)) { | ||
| String programId = "unknown program"; | ||
|
|
@@ -318,6 +332,48 @@ private Map<String,BrAPIGermplasm> processGermplasmForDisplay(List<BrAPIGermplas | |
| return programGermplasmMap; | ||
| } | ||
|
|
||
| private Map<String, String> getPedigreeGermplasmDbIdByBICreatedExRef(List<BrAPIGermplasm> brAPIGermplasm, Program program) throws ApiException { | ||
| Map<String, String> germplasmDbIdByGeneratedExRef = new HashMap<>(); | ||
|
|
||
| for (BrAPIGermplasm germplasm : brAPIGermplasm) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. additionalInfo appears to be nullable, and the existing processing loop below explicitly handles that case. This new helper dereferences it before that normalization occurs, so one germplasm without additionalInfo could fail the entire response. Could we skip parent extraction when additionalInfo is null and also guard against JSON-null parent UUID values? |
||
| 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<String> exRefIds = new ArrayList<>(germplasmDbIdByGeneratedExRef.keySet()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When no parent UUIDs are present, exRefIds is empty, but this method still sends a BrAPI search containing the program and external-reference source filters. Because the empty ID list does not restrict the search, this may retrieve all matching germplasm for the program even though there is nothing to resolve. Could we return an empty map before making the request? |
||
|
|
||
| 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<BrAPIGermplasm> germplasmSearchedWithExRef = brAPIDAOUtil.searchNoPaging( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. searchNoPaging() does not iterate additional pages for an asynchronous search result, and the result GET uses the configured 1,000-record page size. If this lookup can return more than 1,000 distinct parents, later mappings may remain unresolved. Can we use the paging-aware search method, or confirm and enforce that this request can never exceed one result page? |
||
| api::searchGermplasmPost, | ||
| api::searchGermplasmSearchResultsDbIdGet, | ||
| searchRequest); | ||
|
|
||
| for (BrAPIGermplasm germplasm : germplasmSearchedWithExRef) { | ||
| Optional<BrAPIExternalReference> 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<BrAPIGermplasm> getBrAPIGermplasmUsingBrAPIProgramId(GermplasmQuery | |
| List<BrAPIGermplasm> result = brAPIDAOUtil.get(api::germplasmGet, germplasmQueryParams); | ||
|
|
||
| // TODO: Once cache is removed for this class, fix processGermplasmForDisplay to return List<BrAPIGermplasm> [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<BrAPIGermplasm> createBrAPIGermplasm(List<BrAPIGermplasm> 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<BrAPIGermplasm> 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<BrAPIGermplasm> createBrAPIGermplasm(List<BrAPIGermplasm> postBrAPIG | |
|
|
||
| public List<BrAPIGermplasm> updateBrAPIGermplasm(List<BrAPIGermplasm> 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<Map<String, BrAPIGermplasm>> postFunction = () -> { | ||
| List<BrAPIGermplasm> 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<BrAPIGermplasm> [BI-2906] | ||
| List<BrAPIGermplasm> 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<String> brapiGermplasmIds, GermplasmQuery germplasmQuery) throws ApiException { | ||
| BrAPIGermplasmSearchRequest searchRequest = new BrAPIGermplasmSearchRequest(); | ||
| private BrAPIGermplasmSearchRequest buildSearchRequest(Program program, List<String> brapiGermplasmIds, GermplasmQuery query) throws ApiException { | ||
| return buildSearchRequest(program, brapiGermplasmIds, query, null); | ||
| } | ||
|
|
||
| private BrAPIGermplasmSearchRequest buildSearchRequest(Program program, List<String> 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()))); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the BrAPI search does not return one of the requested parent external references, Map.get() returns null. This can serialize pedigreeByUUID as JSON null or produce values such as null/, which BI-Web cannot safely use as a germplasm link. The old implementation did not have this lookup failure mode. Could we validate that every requested parent was resolved, or intentionally use an empty value/GID fallback instead of returning null?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ultimately we have a huge problem if the search returns empty for a specific exref. In that case, we should probably just throw an error that prevents the page from loading.