Skip to content

Add export support for classification and tag level - #31938

Merged
anuj-kumary merged 21 commits into
mainfrom
issue-23053
Aug 26, 2026
Merged

Add export support for classification and tag level#31938
anuj-kumary merged 21 commits into
mainfrom
issue-23053

Conversation

@anuj-kumary

@anuj-kumary anuj-kumary commented Aug 24, 2026

Copy link
Copy Markdown
Member

Describe your changes:

Fixes #23053

Add export support for classification and tag level

Screen.Recording.2026-08-24.at.1.17.32.PM.mov

Type of change:

  • Bug fix
  • Improvement
  • New feature
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation

High-level design:

N/A — small change.

Tests:

Use cases covered

Unit tests

Backend integration tests

Ingestion integration tests

Playwright (UI) tests

Manual testing performed

UI screen recording / screenshots:

Not applicable.

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes <issue-number>: <short explanation>
  • My PR is linked to a GitHub issue via Fixes #<issue-number> above.
  • I have commented on my code, particularly in hard-to-understand areas.
  • For JSON Schema changes: I updated the migration scripts or explained why it is not needed.
  • For UI changes: I attached a screen recording and/or screenshots above.
  • I have added tests (unit / integration / Playwright as applicable) and listed them above.

Greptile Summary

This PR adds classification-level CSV import and export across the service API and classification UI.

  • Implements synchronous and asynchronous classification CSV endpoints.
  • Adds CSV serialization, parsing, and preservation of tag fields not represented in CSV.
  • Exposes permission-aware import and export actions in classification details.
  • Adds backend integration, frontend unit, and Playwright coverage.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ClassificationRepository.java Implements classification-scoped CSV import and export, including tag field parsing, serialization, and preservation of fields absent from the CSV.
openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/ClassificationResource.java Adds synchronous and asynchronous REST endpoints for classification CSV import and export.
openmetadata-service/src/main/resources/json/data/classification/classificationCsvDocumentation.json Documents the supported classification tag CSV columns and correct owner and reviewer separators.
openmetadata-ui/src/main/resources/ui/src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx Adds permission-aware import and export actions for non-system classifications.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ClassificationResourceIT.java Covers export contents, tag creation and updates, system-classification rejection, and preservation of non-CSV fields.

Sequence Diagram

sequenceDiagram
  actor User
  participant UI as Classification UI
  participant API as ClassificationResource
  participant Repo as ClassificationRepository
  participant CSV as ClassificationCsv
  participant Tags as TagRepository

  alt Export
    User->>UI: Select Export
    UI->>API: "GET /classifications/name/{name}/exportAsync"
    API->>Repo: exportToCsv(name, user)
    Repo->>Tags: listAllForCSV(...)
    Tags-->>Repo: Classification tags
    Repo->>CSV: exportCsv(tags)
    CSV-->>User: CSV export job/result
  else Import
    User->>UI: Upload CSV
    UI->>API: "PUT /classifications/name/{name}/import"
    API->>Repo: importFromCsv(name, csv, dryRun, user)
    Repo->>CSV: importCsv(...)
    CSV->>Tags: Create or update tags
    Tags-->>CSV: Import results
    CSV-->>User: Validation/import result
  end
Loading

Reviews (16): Last reviewed commit: "Merge branch 'main' into issue-23053" | Re-trigger Greptile

@anuj-kumary anuj-kumary self-assigned this Aug 24, 2026
@anuj-kumary anuj-kumary added the safe to test Add this label to run secure Github workflows on PRs label Aug 24, 2026
@github-actions github-actions Bot added the UI UI specific issues label Aug 24, 2026
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

⚠️ UI Checkstyle passed — lint findings in changed files

🔍 ESLint findings in this PR's files — 0 error(s), 9 warning(s)

Errors block the build. Warnings do not yet — they are rules whose backlog is still
being worked down, listed so this PR does not add to it. See docs/ui-code-quality-gate.md.

0 error(s), 9 warning(s) across 2 changed file(s).

Count Rule
4 react-hooks/exhaustive-deps
2 sonarjs/cyclomatic-complexity
1 sonarjs/expression-complexity
1 openmetadata-imports/no-rest-ui-imports
1 openmetadata-imports/no-internal-barrel-imports
All findings
Location Rule Message
🟡 src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx:115:5 sonarjs/cyclomatic-complexity {"message":"Function has a complexity of 15 which is greater than 10 authorized.","cost":5,"secondaryLocations":[{"line":115,"column":4,"endLine":115,"endColumn
🟡 src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx:210:8 react-hooks/exhaustive-deps React Hook useCallback has missing dependencies: 'isVersionView' and 'navigate'. Either include them or remove the dependency array.
🟡 src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx:219:20 sonarjs/cyclomatic-complexity {"message":"Function has a complexity of 11 which is greater than 10 authorized.","cost":1,"secondaryLocations":[{"line":219,"column":19,"endLine":219,"endColum
🟡 src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx:306:9 sonarjs/expression-complexity Reduce the number of conditional operators (5) used in the expression (maximum allowed 3).
🟡 src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx:374:8 react-hooks/exhaustive-deps React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array.
🟡 src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx:446:7 react-hooks/exhaustive-deps React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array.
🟡 src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx:496:8 react-hooks/exhaustive-deps React Hook useEffect has missing dependencies: 'fetchClassificationChildren', 'handlePageChange', and 'isAddingTag'. Either include them or remove the dependenc
🟡 src/rest/tagAPI.ts:17:1 openmetadata-imports/no-rest-ui-imports REST clients must not import UI or state-layer modules. Move shared request/response types to the interface layer.
🟡 src/rest/tagAPI.ts:27:1 openmetadata-imports/no-internal-barrel-imports Import the internal module directly instead of its index barrel so unrelated siblings do not enter the bundle graph.

Fix locally (fast - only checks files changed in this branch):

make ui-checkstyle-changed

sonika-shah and others added 5 commits August 25, 2026 16:35
…tag-level export test

- Use the required-field marker 'name*' in ClassificationResourceIT import
  CSV headers so validateHeaders accepts the file; previously the header
  mismatch aborted every import (ApiStatus.ABORTED, 0 rows, tag never
  created), failing the three import tests.
- Remove the tag-level export test from TagResourceIT; export/import is
  supported at the classification level only, so /v1/tags/name/{fqn}/export
  does not exist and the test 404'd.
- Fix classificationCsvDocumentation.json owner and reviewers examples to
  the runtime 'type:name' colon format (e.g. team:marketing, user:john),
  matching addOwners/addReviewers export output.
… import

The CSV import override looked up the existing tag with Include.NON_DELETED to
preserve mutuallyExclusive. On a Redis-cached backend that lookup marks the
(not-yet-created) tag FQN in the not-found cache. The batch create path
(createManyEntitiesForImport) writes the entity through to Redis but, unlike
the batch update path, never invalidates the not-found markers, so the stale
negative marker survives and the immediate GET /tags/name/{fqn} returns 404.

Use Include.ALL, matching EntityRepository.findMatchForImport, which never
touches the NON_DELETED negative cache. Verified locally on the
postgres+elasticsearch+redis lane: ClassificationResourceIT 215 tests pass
(0 errors) with the fix; reverting to NON_DELETED reproduces 3 import-test
failures with 'tag ... not found' 404s.
The CSV import built a fresh Tag from the 11 CSV columns, so any tag field not
represented in the CSV (recognizers, autoClassificationEnabled/Priority,
deprecated, ...) was reset to its schema default on re-import - e.g. a tag's
auto-classification config was silently wiped by an export/edit/import cycle.

On update, start from the stored tag and overlay only the CSV columns, so every
non-CSV field is retained automatically - including any field added to the tag
schema later, with no per-field handling. Verified on the
postgres+elasticsearch+redis lane: autoClassificationEnabled/Priority now
survive a round-trip (were true/7 -> false/50 before); full ClassificationResourceIT
green. Adds a regression test.
@gitar-bot

gitar-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 4 resolved / 4 findings

Adds classification and tag level CSV export and import support across the service and UI, resolving tag lookup issues and preserving non-CSV fields. No issues found.

✅ 4 resolved
Quality: No tests for new classification/tag CSV export logic

📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ClassificationRepository.java:290-304 📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TagRepository.java:159-173
This PR adds new backend export endpoints (ClassificationResource/TagResource) and export logic (exportToCsv, ClassificationCsv.addRecord, tag-plus-descendants assembly) but includes no unit or integration tests. Given the repository's stated coverage expectations for changed classes, add tests covering: classification export producing the expected CSV columns/order, tag export including the tag itself plus all descendants, and correct handling of null style/entityStatus/domains fields.

Edge Case: Backend import lacks system-classification guard enforced only in UI

📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ClassificationRepository.java:288-299 📄 openmetadata-ui/src/main/resources/ui/src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx:290-302 📄 openmetadata-ui/src/main/resources/ui/src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx:277-278
The UI hides import for system-generated classifications (Tier, Certification) because their tags are platform-managed, but the new backend importFromCsv/import endpoints delegate to importCsvInternal with only an EDIT_ALL check and no provider check. A user with EditAll can therefore call the API directly to create/modify tags under a system classification, potentially corrupting platform-managed data. Consider rejecting imports when classification.getProvider() == SYSTEM at the repository/resource layer to match the UI restriction.

Edge Case: Import silently defaults mutuallyExclusive to false on empty cell

📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ClassificationRepository.java:406-409
getMutuallyExclusive returns Boolean.FALSE whenever the column is empty. When importing a CSV to update an existing tag (or one whose mutuallyExclusive was true), an empty cell will force it back to false rather than leaving the existing value unchanged, which can silently flip tag semantics. Since export always writes the field a full round-trip is safe, but hand-edited/partial CSVs will reset it; consider treating an empty cell as 'no change' during updates.

Quality: ClassificationCsv stores classification but never uses it

📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ClassificationRepository.java:294-299 📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ClassificationRepository.java:275-277 📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TagRepository.java:170-179
In ClassificationCsv, the classification field is assigned in the constructor but never read anywhere (addRecord/createEntity don't reference it). Consequently, both ClassificationRepository.exportToCsv (getByName) and TagRepository.exportToCsv (Entity.getEntity) fetch a full classification entity purely to pass it into the CSV writer, which is wasted work. Consider dropping the unused field/parameter, or use it (e.g. to validate/annotate the export) if it was intended.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@sonarqubecloud

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

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

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Export Classification Tags with Assets

4 participants