Skip to content

Fixes #29667: preserve autoClassificationConfig on classification PUT#29668

Merged
IceS2 merged 4 commits into
mainfrom
fix/preserve-autoclassification-config-on-put
Jul 3, 2026
Merged

Fixes #29667: preserve autoClassificationConfig on classification PUT#29668
IceS2 merged 4 commits into
mainfrom
fix/preserve-autoclassification-config-on-put

Conversation

@IceS2

@IceS2 IceS2 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

What

Preserve a classification's autoClassificationConfig when a PUT /v1/classifications omits it.

Why

Metadata connectors re-create source classifications with a full-replace PUT carrying only name + description (no autoClassificationConfig). ClassificationUpdater.entitySpecificUpdate recorded the absent field as a deletion (fieldDeleted), wiping any existing config on every metadata ingestion. The AutoClassification Agent then skips those classifications (autoClassificationConfig.enabled is gone), silently disabling auto-classification for PII, PersonalData, etc.

The updater already force-preserves mutuallyExclusive on update; autoClassificationConfig lacked the equivalent guard.

Change

In ClassificationUpdater, on Operation.PUT, restore autoClassificationConfig from the original entity when the incoming value is null — mirroring the existing style/lifeCycle handling. Explicit PATCH clearing still deletes it.

Fixes #29667

Greptile Summary

This PR fixes a regression where a bare PUT /v1/classifications (carrying only name + description, as emitted by metadata connectors during ingestion) would silently wipe an existing autoClassificationConfig, disabling auto-classification for PII, PersonalData, etc. after every metadata sync.

  • Core fix (ClassificationRepository.java): In ClassificationUpdater.entitySpecificUpdate, a new preserveAutoClassificationConfigOnPut() method restores autoClassificationConfig from the original entity whenever the incoming PUT has a null value, mirroring the long-standing mutuallyExclusive guard.
  • Test coverage (ClassificationResourceIT.java): A new integration test verifies both the preservation semantics (bare PUT leaves config intact) and the intentional-deletion semantics (explicit PATCH with null still removes the config), using the SDK's snapshot-based JSON Patch diff to correctly model the PATCH field-removal case.

Confidence Score: 5/5

Safe to merge — the change is a two-line guard that restores a single field on PUT, isolated to the Classification updater, with no side-effects on PATCH or other operations.

The fix is minimal and precisely targeted: it only activates on Operation.PUT with a null incoming value, cannot affect PATCH flows, and mirrors an identical guard already in place for mutuallyExclusive. The integration test covers both the bug scenario and the intentional-delete path using the SDK's snapshot-based JSON Patch, which correctly generates a remove operation when a field is nulled after a get().

No files require special attention.

Important Files Changed

Filename Overview
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ClassificationRepository.java Adds preserveAutoClassificationConfigOnPut() to guard against bare-PUT erasure of autoClassificationConfig; implementation is minimal, correctly scoped to Operation.PUT with a null check, and follows the existing mutuallyExclusive precedent.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ClassificationResourceIT.java Adds an integration test covering the ingestion-PUT preservation path and the intentional-PATCH-delete path; snapshot-based PATCH diff in the SDK correctly emits a remove op when autoClassificationConfig is set to null after a get(), so both assertions are reliable.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Connector as Metadata Connector
    participant API as PUT /v1/classifications
    participant Updater as ClassificationUpdater
    participant DB as Database

    Connector->>API: "PUT {name, description} (no autoClassificationConfig)"
    API->>Updater: entitySpecificUpdate(original, updated)
    Note over Updater: updated.autoClassificationConfig == null
    Updater->>Updater: preserveAutoClassificationConfigOnPut()
    Note over Updater: operation==PUT && incoming null → restore from original
    Updater->>DB: save with original.autoClassificationConfig preserved

    Note over Connector,DB: PATCH explicit clear path
    Connector->>API: PATCH remove autoClassificationConfig
    API->>Updater: entitySpecificUpdate(original, updated)
    Note over Updater: operation==PATCH → no override
    Updater->>DB: "save with autoClassificationConfig = null (deleted)"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Connector as Metadata Connector
    participant API as PUT /v1/classifications
    participant Updater as ClassificationUpdater
    participant DB as Database

    Connector->>API: "PUT {name, description} (no autoClassificationConfig)"
    API->>Updater: entitySpecificUpdate(original, updated)
    Note over Updater: updated.autoClassificationConfig == null
    Updater->>Updater: preserveAutoClassificationConfigOnPut()
    Note over Updater: operation==PUT && incoming null → restore from original
    Updater->>DB: save with original.autoClassificationConfig preserved

    Note over Connector,DB: PATCH explicit clear path
    Connector->>API: PATCH remove autoClassificationConfig
    API->>Updater: entitySpecificUpdate(original, updated)
    Note over Updater: operation==PATCH → no override
    Updater->>DB: "save with autoClassificationConfig = null (deleted)"
Loading

Reviews (4): Last reviewed commit: "Merge branch 'main' into fix/preserve-au..." | Re-trigger Greptile

Metadata ingestion re-creates source classifications with a bare PUT
(name + description only). The ClassificationUpdater treated the absent
autoClassificationConfig as a deletion, wiping it on every ingestion and
silently disabling the AutoClassification Agent for PII/PersonalData.

Preserve the existing autoClassificationConfig when a PUT omits it, mirroring
the existing style/lifeCycle handling. Explicit PATCH clearing still deletes it.
Copilot AI review requested due to automatic review settings July 1, 2026 15:51
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

✅ PR checks passed

The linked issue has a description and all required Shipping project fields set. Thanks!

@github-actions github-actions Bot added Ingestion safe to test Add this label to run secure Github workflows on PRs labels Jul 1, 2026

Copilot AI 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.

Pull request overview

This PR fixes a regression in the Java backend update path for Classification so that a full-replace PUT /v1/classifications that omits autoClassificationConfig no longer wipes an existing configuration (which can silently disable auto-classification after ingestion upserts).

Changes:

  • Preserve autoClassificationConfig on Operation.PUT when the incoming value is null, by copying it from the original entity before change comparison.
  • Add an integration test that reproduces the ingestion “bare PUT” scenario and verifies that PUT preserves the config while an explicit PATCH clearing still deletes it.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ClassificationRepository.java Preserves autoClassificationConfig during PUT updates to avoid unintended deletion when omitted.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ClassificationResourceIT.java Adds an IT covering the ingestion-style PUT and confirms PATCH can still clear the config.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🟡 Playwright Results — all passed (34 flaky)

✅ 4469 passed · ❌ 0 failed · 🟡 34 flaky · ⏭️ 38 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 439 0 4 16
🟡 Shard 2 803 0 7 8
🟡 Shard 3 806 0 3 7
🟡 Shard 4 804 0 7 5
🟡 Shard 5 850 0 7 0
🟡 Shard 6 767 0 6 2
🟡 34 flaky test(s) (passed on retry)
  • Features/Glossary/GlossaryPagination.spec.ts › should check for nested glossary term search (shard 1, 1 retry)
  • Flow/TestConnectionModal.spec.ts › failure state shows Edit Connection button and Retry Test button (shard 1, 2 retries)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab IS visible for supported type: metric (shard 1, 1 retry)
  • Flow/SearchRBAC.spec.ts › a table-scoped user sees tables but never dashboards (shard 1, 2 retries)
  • Features/BulkEditEntity.spec.ts › Database (shard 2, 1 retry)
  • Features/BulkEditEntity.spec.ts › Database Schema (shard 2, 2 retries)
  • Features/BulkEditEntity.spec.ts › Glossary (shard 2, 1 retry)
  • Features/BulkImport.spec.ts › Database service (shard 2, 1 retry)
  • Features/BulkImport.spec.ts › Database Schema (shard 2, 1 retry)
  • Features/DataQuality/TestCaseImportExportE2eFlow.spec.ts › Admin: Complete export-import-validate flow (shard 2, 2 retries)
  • Features/ExploreQuickFilters.spec.ts › explore tree sidebar selection is not cleared when a top dropdown filter is applied (shard 2, 1 retry)
  • Features/KnowledgeCenterTextEditor.spec.ts › Rich Text Editor - Text Formatting (shard 3, 1 retry)
  • Features/SearchExport.spec.ts › Export queues a background job and downloads from the jobs tray (shard 3, 1 retry)
  • Features/Tasks/TaskNavigation.spec.ts › navigating to /table/TASK-XXXXX should show 404 (invalid URL pattern) (shard 3, 1 retry)
  • Flow/ApiServiceRest.spec.ts › add update and delete api service type REST (shard 4, 1 retry)
  • Flow/PersonaFlow.spec.ts › Set default persona for team should work properly (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Set & Update all CP types on dashboardDataModel (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Enum (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Set table-cp custom property on column and verify in UI (shard 4, 2 retries)
  • Pages/CustomProperties.spec.ts › Set enum custom property on column and verify in UI (shard 4, 2 retries)
  • Pages/DataContractsSemanticRules.spec.ts › Validate DataProduct Rule Any_In (shard 4, 1 retry)
  • Pages/Domains.spec.ts › Domain Rbac (shard 5, 1 retry)
  • Pages/Entity.spec.ts › Tier Add, Update and Remove (shard 5, 1 retry)
  • Pages/EntityDataSteward.spec.ts › Description Add, Update and Remove for child entities (shard 5, 1 retry)
  • Pages/ExploreBrowse.spec.ts › service type drill-down disables unrelated roots and query-panel Clear resets it (shard 5, 2 retries)
  • Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts › Should remove user owner for knowledgeCenter (shard 5, 1 retry)
  • Pages/ExplorePageRightPanel.spec.ts › Should perform CRUD and Removal operations for dashboardDataModel (shard 5, 1 retry)
  • Pages/ExplorePageRightPanel.spec.ts › Should perform CRUD and Removal operations for searchIndex (shard 5, 1 retry)
  • Pages/ExplorePageRightPanel.spec.ts › Should NOT show restricted edit buttons for Data Steward for searchIndex (shard 6, 1 retry)
  • Pages/GlossaryImportExport.spec.ts › Check for Circular Reference in Glossary Import (shard 6, 1 retry)
  • ... and 4 more

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

compareAndUpdate("name", () -> updateName(updated));
}

private void preserveAutoClassificationConfigOnPut() {

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.

Nit: The method name is not great, preserveAutoClassificationConfig() is enough I feel

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

To be fair it is only on put, right? a patch would not preserve it

Copilot AI review requested due to automatic review settings July 3, 2026 07:22

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@sonarqubecloud

sonarqubecloud Bot commented Jul 3, 2026

Copy link
Copy Markdown

@IceS2 IceS2 merged commit 0237550 into main Jul 3, 2026
98 of 107 checks passed
@IceS2 IceS2 deleted the fix/preserve-autoclassification-config-on-put branch July 3, 2026 12:08
@gitar-bot

gitar-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

Restores autoClassificationConfig from the existing entity during PUT operations to prevent accidental deletion by metadata connectors. No issues found.

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 👍 / 👎 | Gitar

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

Labels

Ingestion safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Metadata ingestion wipes autoClassificationConfig from classifications, disabling AutoClassification

3 participants