Skip to content

Fixes 32792: let CreateTableRequest carry certification directly - #32793

Open
zak-nuccio wants to merge 2 commits into
open-metadata:mainfrom
zak-nuccio:feature/create-table-certification
Open

Fixes 32792: let CreateTableRequest carry certification directly#32793
zak-nuccio wants to merge 2 commits into
open-metadata:mainfrom
zak-nuccio:feature/create-table-certification

Conversation

@zak-nuccio

Copy link
Copy Markdown
Contributor

Describe your changes:

Fixes #32792

CreateTableRequest had no certification field, so database metadata ingestion had to create/update a table first and follow up with a separate JSON Patch to /certification. That patch is invisible to sourceHash, so a source-system change that is only a certification update produces the same hash as the previous run and the bulk fast-path silently skips it.

This adds an optional certification (AssetCertification) field to CreateTableRequest, threads it through TableMapper, and narrows the bot-overwrite guard in EntityRepository.updateCertification so a bot's explicit certification value is applied instead of always being reverted.

Type of change:

  • Improvement

High-level design:

EntityRepository.updateCertification() had a guard that reverted any bot PUT once a certification existed, regardless of what the request contained — a workaround for CreateTableRequest never carrying a certification value, so every bot PUT implicitly meant "no certification" and would otherwise have wiped it on every re-sync.

Now that a bot can supply an explicit value, the guard only needs to protect against omission:

if (operation.isPut()
    && !nullOrEmpty(original.getCertification())
    && updatedByBot()
    && !overrideMetadata
    && updatedCertification == null) {
  updated.setCertification(original.getCertification());
  return;
}

This mirrors the overrideMetadata escape hatch already used for description/owners/domains on the same class, rather than introducing a new mechanism:

  • Certification omitted from the request -> preserved (most connectors still won't populate it).
  • Certification explicitly supplied (same or different value) -> proceeds to the existing validate/apply/recordChange path.
  • overrideMetadata=true -> bypasses the guard entirely, consistent with the other protected fields.

No change was needed to generate_source_hash(): it hashes the full request via model_dump(), so certification is automatically included in the hash once it's a model field.

Alternatives considered:

  • Excluding certification from sourceHash as a special case — rejected, since a certification-only source change would still look "unchanged" to the bulk fast path.
  • Keeping the create-then-patch workaround but making the patch more reliable — rejected, doesn't fix the sourceHash blind spot.

Tests:

Use cases covered

  • Ingestion sends CreateTableRequest.certification on table create -> certification is applied.
  • Ingestion omits certification on a later run -> existing certification (e.g. set through the UI) is preserved, not wiped.
  • Ingestion sends a changed certification value on a later run -> the new value is applied and sourceHash differs, so the bulk fast-path doesn't skip it.
  • overrideMetadata=true still clears certification when the request omits it, matching existing behaviour for other protected fields.

Unit tests

  • I added unit tests for the new/changed logic.
  • Files added/updated:
    • openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/EntityRepositoryCertificationTest.java — 4 new tests exercising updateCertification()'s guard directly (bot-omit-preserves, bot-explicit-applies, bot-omit-with-overrideMetadata-clears, human-omit-clears).
    • ingestion/tests/unit/utils/test_source_hash.py — certification-added/changed/omitted/equivalent-payload hash stability tests for generate_source_hash.

Backend integration tests

  • Not applicable (no new API endpoint; existing PUT /tables already accepts CreateTableRequest).

Ingestion integration tests

  • Not applicable (schema/model change only; connectors adopting this field are a separate follow-up).

Manual testing performed

  1. Ran the full EntityRepositoryCertificationTest suite (32/32 passing) and the full org.openmetadata.service.jdbi3.** package (616/616 passing).
  2. Ran ingestion/tests/unit/utils/test_source_hash.py and ingestion/tests/unit/topology/test_runner.py (all passing) after regenerating Python models from the updated schema.
  3. Verified mvn spotless:apply and mvn spotless:check clean on openmetadata-service.

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 — certification is a new optional field on CreateTableRequest, so no migration is needed (existing stored data is unaffected).
  • I have added tests (unit / integration / Playwright as applicable) and listed them above.
  • I have added tests around the new logic.

CreateTableRequest had no certification field, so database metadata
ingestion had to create/update a table first and follow up with a
separate JSON Patch to /certification once the entity existed. That
patch is also invisible to sourceHash, so a source system change that
is only a certification update produces the same hash as the previous
run and the bulk fast-path skips it, silently dropping the update.

- createTable.json: add optional certification (AssetCertification).
- TableMapper: pass CreateTable.certification through to the entity.
- EntityRepository.updateCertification: the bot-overwrite guard used
  to revert any bot PUT once a certification existed, regardless of
  what the request contained (a workaround for CreateTableRequest
  never carrying a value). Narrow it to only preserve certification
  when the request omits it, matching the overrideMetadata escape
  hatch already used for description/owners/domains on the same
  class. An explicit certification value from a bot is now applied
  instead of reverted.
- generate_source_hash needs no change: it hashes the full request via
  model_dump(), so certification is automatically included once it is
  a model field.

Tests: source-hash stability/change coverage in test_source_hash.py,
and four EntityRepositoryCertificationTest cases exercising the guard
directly (omit-preserves, explicit-applies, overrideMetadata-clears,
non-bot-clears).
Copilot AI lite review requested due to automatic review settings September 7, 2026 03:59
@zak-nuccio
zak-nuccio requested review from a team as code owners September 7, 2026 03:59
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Comment thread ingestion/tests/unit/utils/test_source_hash.py

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.

🟡 Changes recommended

The schema change is not reflected in committed UI-generated TypeScript types (e.g., generated CreateTable/CreateTableRequest models still lack certification), leaving generated artifacts out of sync.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR enables database ingestion to set/update table certification in the same PUT /tables request by adding certification to the CreateTable request model, ensuring certification-only changes affect sourceHash and are not skipped by the bulk fast-path.

Changes:

  • Add optional certification (AssetCertification) to the CreateTableRequest JSON schema.
  • Thread certification into table entity creation via TableMapper.
  • Refine EntityRepository.EntityUpdater.updateCertification() to preserve existing certification only when a bot PUT omits the field (and overrideMetadata=false), while allowing explicit bot-provided certification values.
  • Add Java unit tests for the updated certification guard and Python unit tests asserting sourceHash changes with certification payload changes.
File summaries
File Description
openmetadata-spec/src/main/resources/json/schema/api/data/createTable.json Adds optional certification field to the CreateTable request schema.
openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/TableMapper.java Maps request certification onto the Table entity during create/update mapping.
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java Narrows bot PUT “preserve certification” guard to omission-only + honors overrideMetadata.
openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/EntityRepositoryCertificationTest.java Adds unit tests covering the new guard behavior (bot omit/explicit, overrideMetadata, human).
ingestion/tests/unit/utils/test_source_hash.py Adds tests verifying generate_source_hash() changes/stability with certification added/changed/omitted.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 112 to 119
"type": "string",
"minLength": 1,
"maxLength": 32
},
"certification": {
"description": "Certification for a table",
"$ref": "../../type/assetCertification.json"
}
The backend always recomputes AssetCertification.appliedDate/expiryDate
server-side from AssetCertificationSettings when a certification is
applied, ignoring whatever the request sent for those two fields. Only
tagLabel reflects a real change. Hashing appliedDate/expiryDate would
destabilize sourceHash on every ingestion run if a connector ever
populates them with a run-time-relative value, defeating the bulk
fast-path this PR's certification support relies on.

Scoped to the certification field only (not a global key strip),
since expiryDate is also a legitimate, meaningful field on regular tag
metadata (TagLabelMetadata) that should stay part of the hash.
Copilot AI review requested due to automatic review settings September 7, 2026 04:23
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@gitar-bot

gitar-bot Bot commented Sep 7, 2026

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

Adds optional certification field to CreateTableRequest so database metadata ingestion can carry certification directly instead of requiring a separate patch, fixing sourceHash instability from certification-only changes. The guard in EntityRepository.updateCertification() now preserves existing certification when omitted by bots while allowing explicit values to be applied, with appliedDate and expiryDate excluded from sourceHash computation. Comprehensive unit tests added and all existing tests pass.

✅ 1 resolved
Edge Case: Certification appliedDate/expiryDate can destabilize sourceHash

📄 ingestion/tests/unit/utils/test_source_hash.py:45-55
generate_source_hash hashes the full request and _remove_volatile_fields only strips href/deleted/inherited, so AssetCertification's appliedDate/expiryDate are included in the hash. If a connector ever populates these with run-time-relative or now() values, the sourceHash will differ on every ingestion run even when nothing changed, defeating the bulk fast-path (the very optimization this PR aims to preserve). The unit tests only pass because they hard-code fixed timestamps. Consider treating appliedDate/expiryDate as volatile (excluded from the hash) so only the certification tag value drives change detection.

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

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.

🟡 Changes recommended

The CreateTable schema change needs the corresponding committed UI-generated TypeScript schema outputs regenerated/updated to keep spec-derived clients in sync.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

openmetadata-spec/src/main/resources/json/schema/api/data/createTable.json:119

  • The CreateTable JSON schema now includes the new optional certification field, but the committed UI-generated TypeScript types still define CreateTable without certification. This will leave UI/client type generation out of sync with the spec; please regenerate and commit the updated TS schema outputs under openmetadata-ui/src/main/resources/ui/src/generated/ (e.g., .../api/data/createTable.ts).
    },
    "certification": {
      "description": "Certification for a table",
      "$ref": "../../type/assetCertification.json"
    }

ingestion/tests/unit/utils/test_source_hash.py:567

  • This line exceeds the repo's Ruff line-length (120) and will likely fail formatting/lint checks; wrap the AssetCertification(...) call across multiple lines.
            certification=AssetCertification(tagLabel=tag_label, appliedDate=1800000000000, expiryDate=1999999999999),
  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Lite

name="test_table",
databaseSchema="service.db.schema",
columns=[Column(name="id", dataType=DataType.INT)],
certification=AssetCertification(tagLabel=tag_label, appliedDate=1700000000000, expiryDate=1731536000000),
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.

Allow CreateTableRequest to carry certification directly

2 participants