Skip to content

ATLAS-5322: Fix glossary bulk import relation parsing and error reporting. - #712

Open
UmeshPatil-1 wants to merge 2 commits into
apache:masterfrom
UmeshPatil-1:ATLAS-5322
Open

ATLAS-5322: Fix glossary bulk import relation parsing and error reporting.#712
UmeshPatil-1 wants to merge 2 commits into
apache:masterfrom
UmeshPatil-1:ATLAS-5322

Conversation

@UmeshPatil-1

@UmeshPatil-1 UmeshPatil-1 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

Problem

  • Relation columns in import CSV support multiple formats (GlossaryName:TermName, TermName@GlossaryName, and same-glossary TermName shorthand), but parsing/lookup was inconsistent.
  • During bulk import, terms created in pass 1 were not always resolvable during pass 2 relation linking because GUID cache was incomplete.
  • A term could be reported as successful in pass 1 and failed in pass 2 without reconciling the final BulkImportResponse.
  • UI showed only the first failure remark instead of a meaningful summary and per-term error lines.

Backend changes (repository)

GlossaryTermUtils.java

  • Added resolveRelatedTermReference() to normalize and validate three relation formats:
    • TermName@GlossaryName (qualified name)
    • GlossaryName:TermName (legacy colon format — backward compatible)
    • TermName (same-glossary shorthand — best-effort, not a hard failure if unresolved)
  • Added lookupGlossaryTermGuid() to resolve related terms from graph or in-import GUID cache.
  • Added cacheImportedTermGuid() so terms created/seen during import are available for relation linking in pass 2.
  • Improved relation error messages for explicit invalid references vs unresolved same-glossary shorthand.
  • Skip blank pipe-separated relation values.
    GlossaryService.java
  • Cache imported term GUIDs during term creation and when term already exists (re-import path).
  • Added reconcileBulkImportResponse() after relation pass:
    • Merge duplicate failure entries per term
    • Remove terms from successImportInfoList if they appear in failedImportInfoList (prevents misleading partial-success reporting)
      GlossaryServiceTest.java
  • Added regression test using glossary_healthcare_filled.csv — expects 20 successes, 0 failures.
  • Updated invalid-relation test expectation: term with failed relation is no longer reported as success after reconciliation.

UI changes

React dashboard (dashboard/)

  • Added glossaryImportUtils.ts with:
    • formatGlossaryImportFailure() → e.g. Patient@Healthcare Glossary: Reference not found
    • buildGlossaryImportFailureSummary() → e.g. Glossary import completed with 1 failure(s) out of 2 term(s). See error details.
  • Updated ImportDialog.tsx and AddUpdateGlossaryForm.tsx to use formatted toast + Error Details list.
  • Added unit tests: glossaryImportUtils.test.ts
    Legacy dashboard (dashboardv2/)
  • Updated ImportLayoutView.js to show summary notification and formatted per-term errors in Error Details modal (always shown when failures exist).

How was this patch tested?

Unit tests

  • GlossaryServiceTest — existing bulk import tests pass with updated invalid-relation expectation
  • GlossaryServiceTest — new test with glossary_healthcare_filled.csv: 20 success, 0 failed
  • glossaryImportUtils.test.ts — failure formatting and summary message

Manual REST tests

Tested using glossary_healthcare_filled.csv (20 data rows + 1 header = 20 terms) and curl commands.

  1. Health check — GET /api/atlas/v2/glossary?limit=1
    Result: HTTP 200

  2. Import healthcare CSV — POST /api/atlas/v2/glossary/import
    Result: Success: 20, Failed: 0

  3. Verify term count — GET /api/atlas/v2/glossary/{guid}
    Result: Term count: 20

  4. Patient term seeAlso relation — GET /api/atlas/v2/glossary/term/{patientTermGuid}
    Result: seeAlso count: 1, linked to Provider

  5. Provider preferredTerms — GET /api/atlas/v2/glossary/term/{providerTermGuid}
    Result: 2 terms — Physician, Nurse

  6. Colon format backward compatibility — import CSV with GlossaryName:TermName references
    Result: Success: 2, Failed: 0

  7. Negative test — invalid explicit @ reference — MissingTerm@BadRelGlossary
    Result: Entry in failedImportInfoList with clear error (not silent drop)

  8. Re-import same CSV (idempotency) — POST /api/atlas/v2/glossary/import
    Result: Success: 20, Failed: 0

  9. Automated verification script — scripts/atlas5322_verify.sh
    Result: ALL CHECKS PASSED

Example import verification:
curl -s -u admin:admin -X POST
-F "file=@glossary_healthcare_filled.csv"
"http://localhost:21000/api/atlas/v2/glossary/import"

Expected: successImportInfoList size = 20, failedImportInfoList empty.

Example relation verification:
GET /api/atlas/v2/glossary/term/{patientTermGuid}

Expected: "seeAlso" contains Provider; Provider has 2 preferredTerms.

UI manual test

  • Glossary import via React UI — on partial failure, toast shows summary count message
  • Error Details modal lists formatted lines: TermName@GlossaryName: remark
  • Legacy dashboard import view shows same improved error summary and details

@UmeshPatil-1 UmeshPatil-1 changed the title ATLAS-5322: Fix glossary bulk import relation parsing and error repor… ATLAS-5322: Fix glossary bulk import relation parsing and error reporting. Jul 31, 2026

@pawarprasad123 pawarprasad123 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI comments added

const successCount = response.successImportInfoList?.length || 0;
const totalCount = failedCount + successCount;

return `Glossary import completed with ${failedCount} failure(s) out of ${totalCount} term(s). See error details.`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary string is hardcoded glossary-specific ("Glossary import completed...")

  • Consider renaming to make scope explicit, e.g. buildGlossaryImportFailureSummary, and do not reuse for Business Metadata

export const formatGlossaryImportFailure = (
failure: GlossaryImportFailure
): string => {
const termLabel =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requires both childObjectName and parentObjectName for @ format

-add test for childObjectName-only case (same-glossary shorthand failures from backend)

toast.dismiss(toastId.current);
toastId.current = toast.error(
importResp.data.failedImportInfoList[0].remarks
buildGlossaryImportFailureSummary(importResp.data)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical issue Blovker: — Business Metadata regression:
ImportDialog serves both glossary and business metadata (title == "Import Business Metadata"). The PR applies glossary-specific formatting to the shared failure block:

toastId.current = toast.error(
  buildGlossaryImportFailureSummary(importResp.data)  // ← "Glossary import completed..."
);

-And error details use formatGlossaryImportFailure(), which assumes glossary semantics (child@parent). For business metadata, backend stores fields differently (parentObjectName = guid, childObjectName = attributes), producing misleading labels like attributes@guid: error.


solution:

ImportDialog is shared between glossary and business metadata imports. Please guard glossary-specific formatting:

const isGlossaryImport = title !== "Import Business Metadata";
toast.error(
  isGlossaryImport
    ? buildGlossaryImportFailureSummary(importResp.data)
    : importResp.data.failedImportInfoList[0]?.remarks ?? "Import failed"
);

Same guard needed in the error details list (line 207–218).

toast.dismiss(toastId.current);
toastId.current = toast.error(
importResp.data.failedImportInfoList[0].remarks
buildGlossaryImportFailureSummary(importResp.data)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add tests for glossary import failure: summary toast, formatted error list, and back-to-upload navigation — similar to ImportDialog.test.tsx.

});
var modalTitle = '<div class="back-button importBackBtn" title="Back to import file"><i class="fa fa-angle-left "></i> </div> <div class="modal-name">Error Details</div>';
_.each(response.failedImportInfoList, function(err_obj, index) {
var termLabel = err_obj.childObjectName || 'Unknown term';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line 131–134
issue:
termLabel bug: if parentObjectName exists but childObjectName is missing, label becomes "undefined@Glossary"

Suggestion:
Match React logic: only use @ when both exist

var termLabel = 'Unknown term';
if (err_obj.childObjectName && err_obj.parentObjectName) {
    termLabel = err_obj.childObjectName + '@' + err_obj.parentObjectName;
} else if (err_obj.childObjectName) {
    termLabel = err_obj.childObjectName;
}

failedCount = response.failedImportInfoList.length,
successCount = (response.successImportInfoList && response.successImportInfoList.length) || 0,
totalCount = failedCount + successCount,
notificationMsg = 'Glossary import completed with ' + failedCount + ' failure(s) out of ' + totalCount + ' term(s). See error details.';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hardcoded "Glossary import completed..."

Use that.isGlossary for notification text; BM import should not say "Glossary import completed...".

Suggestion:
ImportLayoutView is used for BM too (isGlossary flag exists). Use that.isGlossary to pick message

termLabel = err_obj.childObjectName + '@' + err_obj.parentObjectName;
}
errorStr += '<li>' + (index + 1) + '. ' + _.escape(termLabel) + ': ' + _.escape(err_obj.remarks || '') + '</li>';
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line 131-136

Duplicated formatting logic vs React util
Acceptable for legacy JS, but consider extracting shared helper if both dashboards must stay in sync

"Glossary import completed with 1 failure(s) out of 2 term(s). See error details."
);
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing negative / edge cases:

Only remarks (no names)
Only childObjectName
Empty both lists → "0 failure(s) out of 0 term(s)"
Only failures, no successes
Empty remarks → fallback "Import failed"

suggestion:
line 23:
Please add negative/edge tests: remarks-only, child-only, empty lists, failures-only (no successes).

primary={`${index + 1}. ${formatGlossaryImportFailure(value)}`}
/>
</ListItem>
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocker:

Test Suites: 1 failed, 2 passed, 3 total
Tests: 1 failed, 48 passed, 49 total

Failure: ImportDialog.test.tsx — expectations not updated for new behavior:

Suggestion: (ImportDialog.test.tsx line ~103–122):
Tests must be updated to match new toast summary and formatted error lines. Also add a Business Metadata failure test to ensure glossary formatting is not applied there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants