Skip to content

feat(workflow): add inputPorts, outputPorts, glossaryTerms as WorkflowTriggerFields#28120

Merged
yan-3005 merged 5 commits into
mainfrom
ram/workflow-trigger-port-fields
May 20, 2026
Merged

feat(workflow): add inputPorts, outputPorts, glossaryTerms as WorkflowTriggerFields#28120
yan-3005 merged 5 commits into
mainfrom
ram/workflow-trigger-port-fields

Conversation

@yan-3005
Copy link
Copy Markdown
Contributor

@yan-3005 yan-3005 commented May 14, 2026

Summary

  • Add inputPorts, outputPorts, and glossaryTerms to the WorkflowTriggerFields enum in workflowTriggerFields.json so bulk port operations on Data Products correctly trigger event-based workflows
  • FilterEntityImpl.passesFieldBasedFilter silently dropped port change events before this fix because neither field was a recognized WorkflowTriggerField
  • Unit tests added to FilterEntityImplTest covering recognition, include, and exclude filter behaviour for all three new fields
  • Integration test scenario appended to test_CustomApprovalWorkflowForNewEntities: adds a table as inputPort, removes it, then adds as outputPort — each operation must produce an approval task, proving the workflow trigger fires end-to-end

Fixes https://github.com/open-metadata/openmetadata-collate/issues/4092

Test plan

  • Unit: FilterEntityImplTest — 17 tests pass (run locally)
  • Integration: WorkflowDefinitionResourceIT#test_CustomApprovalWorkflowForNewEntities — new Step 11 validates inputPorts/outputPorts port changes trigger the approval workflow

Summary by Gitar

  • Refactored cache invalidation:
    • Added a negative cache (NotFoundCache) entry in EntityRepository.java cleanup to prevent race conditions during entity deletion.
    • Added markEntityNotFound to short-circuit L1/Redis lookups after invalidation.
  • Bulk operation enhancements:
    • Added support for dryRun in bulk asset operations within DataProductRepository.java and EntityRepository.java.
    • Suppressed event generation and state updates when dryRun is enabled.
  • Workflow task improvements:
    • Captured approver information and metadata during task approval transitions in TaskWorkflowHandler.java.
    • Refactored resolveResolutionType to prevent premature task closure on Approved or Granted states.

This will update automatically on new commits.

…wTriggerFields

- Add inputPorts, outputPorts, glossaryTerms to workflowTriggerFields.json enum
- Unit tests in FilterEntityImplTest verifying all three fields pass the
  passesFieldBasedFilter check, including include/exclude config
- Integration test scenario appended to test_CustomApprovalWorkflowForNewEntities:
  adds table as inputPort, removes it, adds as outputPort — each port change
  must produce an approval task proving the workflow trigger fires correctly
Copilot AI review requested due to automatic review settings May 14, 2026 16:03
@yan-3005 yan-3005 added safe to test Add this label to run secure Github workflows on PRs To release Will cherry-pick this PR into the release branch backend labels May 14, 2026
@yan-3005 yan-3005 self-assigned this May 14, 2026
@github-actions
Copy link
Copy Markdown
Contributor

✅ TypeScript Types Auto-Updated

The generated TypeScript types have been automatically updated based on JSON schema changes in this PR.

@github-actions github-actions Bot requested a review from a team as a code owner May 14, 2026 16:08
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 adds new workflow trigger fields so data product port changes and glossary term-related changes can participate in event-based workflow filtering.

Changes:

  • Adds glossaryTerms, inputPorts, and outputPorts to WorkflowTriggerFields.
  • Adds unit coverage for recognizing the new trigger fields and include/exclude behavior for port fields.
  • Appends an integration scenario for data product input/output port workflow triggers.

Reviewed changes

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

File Description
openmetadata-spec/src/main/resources/json/schema/type/workflowTriggerFields.json Extends the workflow trigger field enum.
openmetadata-service/src/test/java/org/openmetadata/service/governance/workflows/elements/triggers/impl/FilterEntityImplTest.java Adds unit tests for the new trigger fields.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/WorkflowDefinitionResourceIT.java Adds port-trigger workflow validation to an existing approval workflow test.

Comment on lines +41 to +43
"glossaryTerms",
"inputPorts",
"outputPorts"
Comment on lines +5645 to +5648
// Step 11: Verify inputPorts and outputPorts changes trigger the approval workflow
// "inputPorts" and "outputPorts" are WorkflowTriggerFields, so bulk port operations
// must produce a ChangeEvent with the correct field name that the workflow filter recognises.
LOG.info("Step 11: Verifying inputPorts/outputPorts changes trigger the approval workflow");
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 14, 2026

🟡 Playwright Results — all passed (9 flaky)

✅ 4140 passed · ❌ 0 failed · 🟡 9 flaky · ⏭️ 89 skipped

Shard Passed Failed Flaky Skipped
✅ Shard 1 299 0 0 4
🟡 Shard 2 775 0 3 11
🟡 Shard 3 775 0 3 7
✅ Shard 4 782 0 0 12
🟡 Shard 5 771 0 2 47
🟡 Shard 6 738 0 1 8
🟡 9 flaky test(s) (passed on retry)
  • Features/Glossary/GlossaryWorkflow.spec.ts › should display correct status badge color and icon (shard 2, 1 retry)
  • Features/Glossary/GlossaryWorkflow.spec.ts › should start term as Draft when glossary has reviewers (shard 2, 2 retries)
  • Features/KnowledgeCenter.spec.ts › Article mentions in description should working for Knowledge Center (shard 2, 1 retry)
  • Features/RTL.spec.ts › Verify Following widget functionality (shard 3, 1 retry)
  • Features/Table.spec.ts › Table pagination with sorting should works (shard 3, 1 retry)
  • Features/Table.spec.ts › Tags term should be consistent for search (shard 3, 1 retry)
  • Pages/Entity.spec.ts › Tier Add, Update and Remove (shard 5, 1 retry)
  • Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts › Should remove user owner for knowledgeCenter (shard 5, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 6, 1 retry)

📦 Download artifacts

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

…rnance workflows

- Add inputPorts, outputPorts, glossaryTerms to WorkflowTriggerFields enum
- Fix executeBulkPortsOperation to update entity changeDescription so
  FilterEntityImpl reads the correct changed fields
- Add governance-bot impersonation to applyPatchEntityFieldAction to
  prevent entityStatus updates from triggering spurious workflow signals
- Pass caller username through port endpoints to fix self-approval check
  incorrectly removing the reviewer when entity updatedBy was set to the
  reviewer from a prior task resolution
Comment on lines 396 to +403
ChangeEvent changeEvent =
getChangeEvent(dataProduct, change, DATA_PRODUCT, dataProduct.getVersion());
getChangeEvent(dataProduct, change, DATA_PRODUCT, dataProduct.getVersion(), updatedBy);
Entity.getCollectionDAO().changeEventDAO().insert(JsonUtils.pojoToJson(changeEvent));
DataProduct entityToUpdate = get(null, dataProduct.getId(), getFields("*"));
entityToUpdate.setChangeDescription(change);
entityToUpdate.setUpdatedBy(updatedBy);
storeEntity(entityToUpdate, true);
invalidate(entityToUpdate);
Copy link
Copy Markdown

@gitar-bot gitar-bot Bot May 15, 2026

Choose a reason for hiding this comment

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

⚠️ Bug: ChangeEvent embeds stale entity without updated version/updatedBy

In executeBulkPortsOperation, the ChangeEvent at line 397 is constructed with the original dataProduct object which has not been refreshed after relationships were added. Its updatedBy still reflects the previous updater (not the current user), and its version is never incremented. The fresh entity fetched at line 399 gets setUpdatedBy(updatedBy) and setChangeDescription(change) but is only used for storeEntity — the ChangeEvent sent to subscribers contains the stale snapshot.

This means:

  • Workflow consumers (like WorkflowEventConsumer) see an entity snapshot with the wrong updatedBy inside the event payload.
  • The entity version is never incremented by storeEntity, so consecutive port operations produce change events with identical previousVersion, which may confuse version-tracking logic.

Consider either (a) re-fetching the entity before constructing the ChangeEvent and using that for both, or (b) incrementing the version explicitly (similar to how bulkAssetsOperation in the same file delegates to the updater path).

Re-fetch entity before building ChangeEvent, increment version, and use the fresh entity in the event payload so subscribers see consistent state.:

if (!success.isEmpty()) {
  List<EntityReference> successAssets =
      success.stream().map(r -> (EntityReference) r.getRequest()).collect(Collectors.toList());
  ChangeDescription change =
      addBulkAddRemoveChangeDescription(dataProduct.getVersion(), isAdd, successAssets, null);
  if (!change.getFieldsAdded().isEmpty()) {
    change.getFieldsAdded().get(0).setName(fieldName);
  }
  if (!change.getFieldsDeleted().isEmpty()) {
    change.getFieldsDeleted().get(0).setName(fieldName);
  }
  // Fetch fresh entity, update metadata, then build ChangeEvent from it
  DataProduct entityToUpdate = get(null, dataProduct.getId(), getFields("*"));
  entityToUpdate.setChangeDescription(change);
  entityToUpdate.setUpdatedBy(updatedBy);
  entityToUpdate.setVersion(EntityUtil.nextVersion(entityToUpdate.getVersion()));
  storeEntity(entityToUpdate, true);
  invalidate(entityToUpdate);
  ChangeEvent changeEvent =
      getChangeEvent(entityToUpdate, change, DATA_PRODUCT, dataProduct.getVersion(), updatedBy);
  Entity.getCollectionDAO().changeEventDAO().insert(JsonUtils.pojoToJson(changeEvent));
}

Was this helpful? React with 👍 / 👎

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 15, 2026

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 63%
63.35% (65503/103394) 44.14% (35939/81405) 46.91% (10565/22521)

Copilot AI review requested due to automatic review settings May 16, 2026 09:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 7 out of 8 changed files in this pull request and generated 6 comments.

.executeForString(
HttpMethod.POST, BASE_PATH, portWorkflow, RequestOptions.builder().build());
LOG.debug("Created port trigger workflow: {}", portWorkflowName);
waitForWorkflowDeployment(client, portWorkflowName);
Comment on lines +6159 to +6200
await()
.atMost(Duration.ofMinutes(2))
.pollInterval(Duration.ofSeconds(2))
.until(
() ->
!listOpenApprovalTasks(reviewerClient, dataProduct.getFullyQualifiedName())
.getData()
.isEmpty());
Task inputPortTask =
listOpenApprovalTasks(reviewerClient, dataProduct.getFullyQualifiedName()).getData().get(0);
reviewerClient.tasks().resolve(inputPortTask.getId().toString(), resolveApproved);
LOG.info("inputPorts add triggered and resolved approval task");

// outputPorts add — outputTable is a data product asset, satisfying the prerequisite
// Using a different table so it doesn't conflict with inputTable already in inputPorts
org.openmetadata.schema.type.api.BulkAssets outputPortAssets =
new org.openmetadata.schema.type.api.BulkAssets()
.withAssets(List.of(outputTable.getEntityReference()));
client.dataProducts().bulkAddOutputPorts(dataProduct.getFullyQualifiedName(), outputPortAssets);
LOG.debug("Added outputTable as outputPort");
await()
.atMost(Duration.ofMinutes(2))
.pollInterval(Duration.ofSeconds(2))
.until(
() ->
!listOpenApprovalTasks(reviewerClient, dataProduct.getFullyQualifiedName())
.getData()
.isEmpty());
Task outputPortTask =
listOpenApprovalTasks(reviewerClient, dataProduct.getFullyQualifiedName()).getData().get(0);
reviewerClient.tasks().resolve(outputPortTask.getId().toString(), resolveApproved);
LOG.info("outputPorts add triggered and resolved approval task");

try {
WorkflowDefinition wd = client.workflowDefinitions().getByName(portWorkflowName, null);
client.workflowDefinitions().delete(wd.getId());
LOG.debug("Deleted port trigger workflow");
} catch (Exception e) {
LOG.warn("Error deleting port trigger workflow: {}", e.getMessage());
}

LOG.info("test_PortChangesOnDataProductTriggerWorkflow completed successfully");
org.openmetadata.schema.type.api.BulkAssets outputPortAssets =
new org.openmetadata.schema.type.api.BulkAssets()
.withAssets(List.of(outputTable.getEntityReference()));
client.dataProducts().bulkAddOutputPorts(dataProduct.getFullyQualifiedName(), outputPortAssets);
Comment on lines +399 to +402
DataProduct entityToUpdate = get(null, dataProduct.getId(), getFields("*"));
entityToUpdate.setChangeDescription(change);
entityToUpdate.setUpdatedBy(updatedBy);
storeEntity(entityToUpdate, true);
getChangeEvent(dataProduct, change, DATA_PRODUCT, dataProduct.getVersion(), updatedBy);
Entity.getCollectionDAO().changeEventDAO().insert(JsonUtils.pojoToJson(changeEvent));
DataProduct entityToUpdate = get(null, dataProduct.getId(), getFields("*"));
entityToUpdate.setChangeDescription(change);
"providesTo",
"lifecycleStage"
"lifecycleStage",
"glossaryTerms",
@yan-3005 yan-3005 enabled auto-merge (squash) May 19, 2026 16:55
@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented May 19, 2026

Code Review ⚠️ Changes requested 1 resolved / 2 findings

Adds inputPorts, outputPorts, and glossaryTerms to WorkflowTriggerFields, but the ChangeEvent in executeBulkPortsOperation embeds a stale entity that lacks updated version and updatedBy information.

⚠️ Bug: ChangeEvent embeds stale entity without updated version/updatedBy

📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DataProductRepository.java:396-403

In executeBulkPortsOperation, the ChangeEvent at line 397 is constructed with the original dataProduct object which has not been refreshed after relationships were added. Its updatedBy still reflects the previous updater (not the current user), and its version is never incremented. The fresh entity fetched at line 399 gets setUpdatedBy(updatedBy) and setChangeDescription(change) but is only used for storeEntity — the ChangeEvent sent to subscribers contains the stale snapshot.

This means:

  • Workflow consumers (like WorkflowEventConsumer) see an entity snapshot with the wrong updatedBy inside the event payload.
  • The entity version is never incremented by storeEntity, so consecutive port operations produce change events with identical previousVersion, which may confuse version-tracking logic.

Consider either (a) re-fetching the entity before constructing the ChangeEvent and using that for both, or (b) incrementing the version explicitly (similar to how bulkAssetsOperation in the same file delegates to the updater path).

Re-fetch entity before building ChangeEvent, increment version, and use the fresh entity in the event payload so subscribers see consistent state.
if (!success.isEmpty()) {
  List<EntityReference> successAssets =
      success.stream().map(r -> (EntityReference) r.getRequest()).collect(Collectors.toList());
  ChangeDescription change =
      addBulkAddRemoveChangeDescription(dataProduct.getVersion(), isAdd, successAssets, null);
  if (!change.getFieldsAdded().isEmpty()) {
    change.getFieldsAdded().get(0).setName(fieldName);
  }
  if (!change.getFieldsDeleted().isEmpty()) {
    change.getFieldsDeleted().get(0).setName(fieldName);
  }
  // Fetch fresh entity, update metadata, then build ChangeEvent from it
  DataProduct entityToUpdate = get(null, dataProduct.getId(), getFields("*"));
  entityToUpdate.setChangeDescription(change);
  entityToUpdate.setUpdatedBy(updatedBy);
  entityToUpdate.setVersion(EntityUtil.nextVersion(entityToUpdate.getVersion()));
  storeEntity(entityToUpdate, true);
  invalidate(entityToUpdate);
  ChangeEvent changeEvent =
      getChangeEvent(entityToUpdate, change, DATA_PRODUCT, dataProduct.getVersion(), updatedBy);
  Entity.getCollectionDAO().changeEventDAO().insert(JsonUtils.pojoToJson(changeEvent));
}
✅ 1 resolved
Quality: Missing newline at end of workflowTriggerFields.json

📄 openmetadata-spec/src/main/resources/json/schema/type/workflowTriggerFields.json:45
The JSON file lost its trailing newline (\ No newline at end of file). While functionally harmless, POSIX text files should end with a newline, and many linters/diff tools will flag this. This also causes noisy diffs on subsequent edits to the last line.

🤖 Prompt for agents
Code Review: Adds inputPorts, outputPorts, and glossaryTerms to WorkflowTriggerFields, but the ChangeEvent in executeBulkPortsOperation embeds a stale entity that lacks updated version and updatedBy information.

1. ⚠️ Bug: ChangeEvent embeds stale entity without updated version/updatedBy
   Files: openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DataProductRepository.java:396-403

   In `executeBulkPortsOperation`, the `ChangeEvent` at line 397 is constructed with the original `dataProduct` object which has not been refreshed after relationships were added. Its `updatedBy` still reflects the previous updater (not the current user), and its `version` is never incremented. The fresh entity fetched at line 399 gets `setUpdatedBy(updatedBy)` and `setChangeDescription(change)` but is only used for `storeEntity` — the ChangeEvent sent to subscribers contains the stale snapshot.
   
   This means:
   - Workflow consumers (like `WorkflowEventConsumer`) see an entity snapshot with the wrong `updatedBy` inside the event payload.
   - The entity version is never incremented by `storeEntity`, so consecutive port operations produce change events with identical `previousVersion`, which may confuse version-tracking logic.
   
   Consider either (a) re-fetching the entity *before* constructing the ChangeEvent and using that for both, or (b) incrementing the version explicitly (similar to how `bulkAssetsOperation` in the same file delegates to the updater path).

   Fix (Re-fetch entity before building ChangeEvent, increment version, and use the fresh entity in the event payload so subscribers see consistent state.):
   if (!success.isEmpty()) {
     List<EntityReference> successAssets =
         success.stream().map(r -> (EntityReference) r.getRequest()).collect(Collectors.toList());
     ChangeDescription change =
         addBulkAddRemoveChangeDescription(dataProduct.getVersion(), isAdd, successAssets, null);
     if (!change.getFieldsAdded().isEmpty()) {
       change.getFieldsAdded().get(0).setName(fieldName);
     }
     if (!change.getFieldsDeleted().isEmpty()) {
       change.getFieldsDeleted().get(0).setName(fieldName);
     }
     // Fetch fresh entity, update metadata, then build ChangeEvent from it
     DataProduct entityToUpdate = get(null, dataProduct.getId(), getFields("*"));
     entityToUpdate.setChangeDescription(change);
     entityToUpdate.setUpdatedBy(updatedBy);
     entityToUpdate.setVersion(EntityUtil.nextVersion(entityToUpdate.getVersion()));
     storeEntity(entityToUpdate, true);
     invalidate(entityToUpdate);
     ChangeEvent changeEvent =
         getChangeEvent(entityToUpdate, change, DATA_PRODUCT, dataProduct.getVersion(), updatedBy);
     Entity.getCollectionDAO().changeEventDAO().insert(JsonUtils.pojoToJson(changeEvent));
   }

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqubecloud
Copy link
Copy Markdown

@sonarqubecloud
Copy link
Copy Markdown

@yan-3005 yan-3005 merged commit b1781f7 into main May 20, 2026
66 of 67 checks passed
@yan-3005 yan-3005 deleted the ram/workflow-trigger-port-fields branch May 20, 2026 03:53
@github-actions
Copy link
Copy Markdown
Contributor

Failed to cherry-pick changes to the 1.13 branch.
Please cherry-pick the changes manually.
You can find more details here.

@github-actions
Copy link
Copy Markdown
Contributor

Failed to cherry-pick changes to the 1.12.9 branch.
Please cherry-pick the changes manually.
You can find more details here.

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

Labels

backend safe to test Add this label to run secure Github workflows on PRs To release Will cherry-pick this PR into the release branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants