Skip to content

Add changeSummary API endpoint and UI components#26533

Merged
harshach merged 23 commits intomainfrom
feature/change-summary-api
Apr 10, 2026
Merged

Add changeSummary API endpoint and UI components#26533
harshach merged 23 commits intomainfrom
feature/change-summary-api

Conversation

@harshach
Copy link
Copy Markdown
Collaborator

@harshach harshach commented Mar 17, 2026

Summary

  • Add GET /v1/changeSummary/{entityType}/{id} and GET /v1/changeSummary/{entityType}/name/{fqn} endpoints for retrieving per-field change metadata (who changed it, source type, timestamp)
  • Support fieldPrefix filtering for column-level queries on large tables and limit/offset pagination
  • Add DescriptionSourceBadge UI component that shows an AI badge on AI-generated descriptions (Suggested/Automated sources), with tooltip showing who accepted and when
  • Add useChangeSummary React hook and changeSummaryAPI REST client
  • Add 5 integration tests to BaseEntityIT covering all entity types: get by ID, get by FQN, fieldPrefix filtering, pagination, and 404 cases

Test plan

  • UI unit tests pass (11 tests across 2 suites)
  • ChangeSummaryResource.java compiles clean
  • Integration tests pass for TableResourceIT and TopicResourceIT (5 changeSummary tests each via BaseEntityIT)
  • Verify changeSummary tests pass for all other entity types via BaseEntityIT

Closes #26547

Screenshot 2026-04-07 at 5 08 56 PM Screenshot 2026-04-07 at 5 26 55 PM

🤖 Generated with Claude Code

…ce tracking

Add a new /v1/changeSummary/{entityType}/{id|name/fqn} endpoint that
returns per-field change metadata (who changed it, source type, timestamp).
Supports fieldPrefix filtering for column-level queries on large tables
and limit/offset pagination.

UI additions:
- DescriptionSourceBadge component showing AI badge on AI-generated descriptions
- useChangeSummary hook for fetching change summary data
- changeSummaryAPI REST client
- "accepted-by" translation key

Integration tests added to BaseEntityIT covering all entity types:
get by ID, get by FQN, fieldPrefix filtering, pagination, and 404 cases.

Closes #1648

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@harshach harshach requested a review from a team as a code owner March 17, 2026 05:14
@github-actions github-actions Bot added backend safe to test Add this label to run secure Github workflows on PRs labels Mar 17, 2026
@github-actions
Copy link
Copy Markdown
Contributor

The Java checkstyle failed.

Please run mvn spotless:apply in the root of your repository and commit the changes to this PR.
You can also use pre-commit to automate the Java code formatting.

You can install the pre-commit hooks with make install_test precommit_install.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 17, 2026

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 64%
64.22% (59688/92936) 43.8% (31184/71181) 46.92% (9385/20000)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 17, 2026

🟡 Playwright Results — all passed (26 flaky)

✅ 3597 passed · ❌ 0 failed · 🟡 26 flaky · ⏭️ 207 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 455 0 2 2
🟡 Shard 2 641 0 2 32
🟡 Shard 3 644 0 4 26
🟡 Shard 4 620 0 7 47
🟡 Shard 5 608 0 1 67
🟡 Shard 6 629 0 10 33
🟡 26 flaky test(s) (passed on retry)
  • Features/DataAssetRulesDisabled.spec.ts › Verify the Messaging Service entity item action after rules disabled (shard 1, 1 retry)
  • Pages/SearchIndexApplication.spec.ts › Search Index Application (shard 1, 1 retry)
  • Features/BulkEditEntity.spec.ts › Glossary (shard 2, 1 retry)
  • Features/ChangeSummaryBadge.spec.ts › Automated badge should appear on entity description with Automated source (shard 2, 1 retry)
  • Features/Permissions/GlossaryPermissions.spec.ts › Team-based permissions work correctly (shard 3, 1 retry)
  • Flow/CustomizeWidgets.spec.ts › Domains Widget (shard 3, 1 retry)
  • Flow/ExploreDiscovery.spec.ts › Should display deleted assets when showDeleted is checked and deleted is not present in queryFilter (shard 3, 1 retry)
  • Flow/PersonaFlow.spec.ts › Set and remove default persona should work properly (shard 3, 1 retry)
  • Pages/Customproperties-part2.spec.ts › entityReferenceList shows item count, scrollable list, no expand toggle (shard 4, 2 retries)
  • Pages/Customproperties-part2.spec.ts › table-cp shows row count, scrollable container, no expand toggle (shard 4, 2 retries)
  • Pages/DataContracts.spec.ts › Create Data Contract and validate for Topic (shard 4, 1 retry)
  • Pages/DataContracts.spec.ts › Create Data Contract and validate for Api Collection (shard 4, 1 retry)
  • Pages/DataContractsSemanticRules.spec.ts › Validate Description Rule Is_Not_Set (shard 4, 1 retry)
  • Pages/Domains.spec.ts › Multiple consecutive domain renames preserve all associations (shard 4, 1 retry)
  • Pages/Entity.spec.ts › UpVote & DownVote entity (shard 4, 1 retry)
  • Pages/ExplorePageRightPanel.spec.ts › Should perform CRUD and Removal operations for table (shard 5, 1 retry)
  • Pages/Lineage/DataAssetLineage.spec.ts › Column lineage for mlModel -> mlModel (shard 6, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 6, 1 retry)
  • Pages/Login.spec.ts › Refresh should work (shard 6, 2 retries)
  • Pages/ODCSImportExport.spec.ts › Multi-object ODCS contract - object selector shows all schema objects (shard 6, 1 retry)
  • Pages/ProfilerConfigurationPage.spec.ts › Non admin user (shard 6, 1 retry)
  • Pages/ServiceEntity.spec.ts › Glossary Term Add, Update and Remove (shard 6, 1 retry)
  • Pages/UserDetails.spec.ts › Create team with domain and verify visibility of inherited domain in user profile after team removal (shard 6, 1 retry)
  • Pages/Users.spec.ts › Permissions for table details page for Data Consumer (shard 6, 1 retry)
  • Pages/Users.spec.ts › Should add, remove, and navigate to persona pages for Personas section (shard 6, 1 retry)
  • VersionPages/EntityVersionPages.spec.ts › Directory (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

- Add authorization checks (VIEW_BASIC) to ChangeSummaryResource endpoints
- Fix pagination defaults and simplify pagination logic
- Remove silent try-catch in integration tests, use assertThrows for 404
- Wire useChangeSummary hook into GenericProvider context
- Render AI-generated badge on entity descriptions (DescriptionV1)
- Render AI-generated badge on column descriptions (TableDescription)
- Pass changeSummary entry to ColumnDetailPanel's DescriptionSection
- Fix stale useMemo dependency in DescriptionV1 header
- Fix missing Less variable import in description-source-badge.less
- Add Playwright E2E tests for ChangeSummary badge feature

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 21, 2026 05:34
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

Adds a new backend changeSummary read API and wires it into the UI to surface AI/automated description provenance via a badge (plus accompanying tests).

Changes:

  • Backend: introduce GET /v1/changeSummary/{entityType}/{id} and /name/{fqn} endpoints with fieldPrefix filtering and limit/offset pagination.
  • UI: add changeSummaryAPI, useChangeSummary hook, and DescriptionSourceBadge to display AI/automated provenance on entity + column descriptions.
  • Tests: add integration tests in BaseEntityIT and a Playwright feature spec for the badge.

Reviewed changes

Copilot reviewed 36 out of 37 changed files in this pull request and generated 34 comments.

Show a summary per file
File Description
openmetadata-ui/src/main/resources/ui/src/rest/changeSummaryAPI.ts Adds REST client + types for changeSummary (ID-based).
openmetadata-ui/src/main/resources/ui/src/rest/changeSummaryAPI.test.ts Unit tests for the REST client.
openmetadata-ui/src/main/resources/ui/src/hooks/useChangeSummary.ts New hook to fetch and expose change summary state.
openmetadata-ui/src/main/resources/ui/src/components/common/EntityDescription/DescriptionV1.tsx Renders the new badge next to entity description header.
openmetadata-ui/src/main/resources/ui/src/components/common/DescriptionSourceBadge/description-source-badge.less Styling for the badge/tooltip.
openmetadata-ui/src/main/resources/ui/src/components/common/DescriptionSourceBadge/DescriptionSourceBadge.tsx Badge + tooltip logic based on changeSource metadata.
openmetadata-ui/src/main/resources/ui/src/components/common/DescriptionSourceBadge/DescriptionSourceBadge.test.tsx Unit tests for badge visibility behavior.
openmetadata-ui/src/main/resources/ui/src/components/common/DescriptionSourceBadge/DescriptionSourceBadge.interface.ts Props typing for the badge component.
openmetadata-ui/src/main/resources/ui/src/components/common/DescriptionSection/DescriptionSection.tsx Adds badge to the description section header.
openmetadata-ui/src/main/resources/ui/src/components/common/DescriptionSection/DescriptionSection.interface.ts Adds changeSummaryEntry prop type.
openmetadata-ui/src/main/resources/ui/src/components/Database/TableDescription/TableDescription.component.tsx Shows badge for column descriptions in table description UI.
openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/ColumnDetailPanel.component.tsx Passes per-column changeSummary entry to DescriptionSection.
openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx Fetches changeSummary and provides it via context.
openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.interface.ts Adds changeSummary to the generic context type.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ChangeSummaryBadge.spec.ts E2E coverage for entity/column badge display.
openmetadata-ui/src/main/resources/ui/src/locale/languages/*.json Adds label.accepted-by translation key across locales.
openmetadata-service/src/main/java/org/openmetadata/service/resources/ChangeSummaryResource.java New backend resource implementing the changeSummary endpoints.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BaseEntityIT.java Adds base integration tests for the new endpoints.
.gitignore Ignores .maestro.

Comment thread openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json Outdated
Comment thread openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json Outdated
Comment thread openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json Outdated
Comment thread openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json Outdated
Comment thread openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json Outdated
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 60 out of 61 changed files in this pull request and generated 3 comments.

Comment on lines +120 to +122
if (!showBadge && !actorInfo && !timestampInfo) {
return null;
}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

DescriptionSourceBadge can render an empty .description-source-container for changeSource=Manual when callers disable metadata (e.g., showAcceptedBy={false} and showTimestamp={false} as done in headers). In that case config is null, the badge isn’t rendered, and both metadata nodes are null, but the component still returns an empty wrapper because showBadge defaults to true. This leaves a focusable-but-empty element in the DOM and can affect layout. Consider computing hasBadge = showBadge && Boolean(config) and returning null when !hasBadge && !actorInfo && !timestampInfo (and add a unit test for the Manual + metadata-disabled case).

Copilot uses AI. Check for mistakes.
Comment on lines +88 to +97
const tooltipContent = changeSummaryEntry?.changedAt
? formatDateTime(changeSummaryEntry.changedAt)
: undefined;

const actorLabel = config ? t('label.accepted-by') : t('label.authored-by');

const relativeTime = changeSummaryEntry?.changedAt
? getShortRelativeTime(changeSummaryEntry.changedAt) ||
formatDate(changeSummaryEntry.changedAt)
: '';
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The tooltip behavior doesn’t match the PR description (“tooltip showing who accepted and when”). For Suggested the tooltip is always the static label.ai-suggested, and for Automated/Propagated the tooltip only shows the timestamp (formatDateTime) and never includes the actor. If the intent is to rely on the metadata row below the description, consider updating the PR description; otherwise, build a tooltip string that includes changedBy + formatted time (and use it especially when showAcceptedBy/showTimestamp are false).

Copilot uses AI. Check for mistakes.
Comment on lines +129 to +145
config.iconOnly ? (
<Tooltip title={t(config.tooltipKey)}>
<span data-testid={config.testId} role="status" tabIndex={0}>
{config.icon}
</span>
</Tooltip>
) : (
<Tooltip title={tooltipContent}>
<Tag
className={classNames(
'description-source-badge',
config.className
)}
data-testid={config.testId}
role="status"
tabIndex={0}>
{config.icon}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The badge markup uses role="status" on a focusable <span>/<Tag> but provides no aria-label/accessible name. role="status" is intended for live regions, not icons/badges, and screen readers may announce it oddly (or not at all). Consider removing the role, or using an appropriate role (img) plus aria-label (e.g., from t(config.tooltipKey) / t(config.labelKey)) so keyboard/screen-reader users get equivalent context.

Copilot uses AI. Check for mistakes.
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.

Copilot was unable to review this pull request because the user who requested the review is ineligible. To be eligible to request a review, you need a paid Copilot license, or your organization must enable Copilot code review.

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.

Copilot was unable to review this pull request because the user who requested the review is ineligible. To be eligible to request a review, you need a paid Copilot license, or your organization must enable Copilot code review.

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.

Copilot was unable to review this pull request because the user who requested the review is ineligible. To be eligible to request a review, you need a paid Copilot license, or your organization must enable Copilot code review.

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.

Copilot was unable to review this pull request because the user who requested the review is ineligible. To be eligible to request a review, you need a paid Copilot license, or your organization must enable Copilot code review.


test.describe(
'ChangeSummary DescriptionSourceBadge',
{ tag: ['@Features', '@Discovery'] },
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.

We do not have "@Features" tag, and please check usage of "@discovery"

Comment on lines +32 to +46
const entityPatchResponse = await apiContext.patch(
`/api/v1/tables/name/${table.entityResponseData?.fullyQualifiedName}?changeSource=Suggested`,
{
data: [
{
op: 'add',
path: '/description',
value: 'AI-generated entity description for badge test',
},
],
headers: {
'Content-Type': 'application/json-patch+json',
},
}
);
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.

we have table.patch, can we use that?

test('AI badge should appear on entity description with Suggested source', async ({
page,
}) => {
test.slow();
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.

We can remove the test.slow from here, test is very small, it wont take much time

test('AI badge should appear on column description with Suggested source', async ({
page,
}) => {
test.slow();
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.

same with here for test.slow, lets check everywhere

* limitations under the License.
*/

import { Tooltip } from 'antd';
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.

it will be great if we use core-component instead of antd for new development

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.

this will be going in minor release so can't use core-components here

test.describe(
'ChangeSummary DescriptionSourceBadge',
{ tag: [DOMAIN_TAGS.DISCOVERY] },
() => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Quality: Test entities never cleaned up, causing test data leak

The afterAll block that deleted the main table entity was removed, and three new test cases (automatedTable, propagatedTable, manualTable) each create entities without any cleanup. This accumulates orphaned database services, databases, schemas, and tables in the test environment across runs, which can cause flaky tests and slow down the test suite.

The original test had an afterAll block for cleanup that was deleted in this commit. The new tests create entities in test.step blocks but never call .delete() on them.

Suggested fix:

test.afterAll('Cleanup test entities', async ({ browser }) => {
  const { apiContext, afterAction } = await performAdminLogin(browser);
  await table.delete(apiContext);
  // automatedTable, propagatedTable, manualTable are local
  // to each test - consider promoting them to module scope
  // so they can be cleaned up here too.
  await afterAction();
});

Was this helpful? React with 👍 / 👎 | Reply gitar fix to apply this suggestion

@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented Apr 9, 2026

Code Review ⚠️ Changes requested 12 resolved / 14 findings

Adds changeSummary API endpoint and UI components with 11 issues resolved, but two critical findings remain: resolveBaseUrl() falls back to localhost breaking OAuth in production, and test entities lack cleanup causing data leaks across test runs.

⚠️ Security: resolveBaseUrl() falls back to localhost breaking OAuth in prod

When both the MCP configuration and the system repository fail to provide a base URL, resolveBaseUrl() silently falls back to http://localhost:8585 (line 124). In production deployments where the system settings haven't been configured yet, this causes the OAuth redirect_uri to point to localhost, which will either fail silently or redirect tokens to an unintended destination. The method should throw an error rather than use an incorrect default, since a wrong redirect_uri in an OAuth flow is a security-sensitive misconfiguration.

Suggested fix
// Instead of returning a silent default:
// return "http://localhost:8585";
throw new IllegalStateException(
    "Could not determine base URL for MCP OAuth callback. "
    + "Configure 'openMetadataUrl' in system settings or "
    + "'baseUrl' in MCP configuration.");
⚠️ Quality: Test entities never cleaned up, causing test data leak

📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ChangeSummaryBadge.spec.ts:27 📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ChangeSummaryBadge.spec.ts:146 📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ChangeSummaryBadge.spec.ts:217 📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ChangeSummaryBadge.spec.ts:268

The afterAll block that deleted the main table entity was removed, and three new test cases (automatedTable, propagatedTable, manualTable) each create entities without any cleanup. This accumulates orphaned database services, databases, schemas, and tables in the test environment across runs, which can cause flaky tests and slow down the test suite.

The original test had an afterAll block for cleanup that was deleted in this commit. The new tests create entities in test.step blocks but never call .delete() on them.

Suggested fix
test.afterAll('Cleanup test entities', async ({ browser }) => {
  const { apiContext, afterAction } = await performAdminLogin(browser);
  await table.delete(apiContext);
  // automatedTable, propagatedTable, manualTable are local
  // to each test - consider promoting them to module scope
  // so they can be cleaned up here too.
  await afterAction();
});
✅ 12 resolved
Security: Missing authorization checks in ChangeSummaryResource endpoints

📄 openmetadata-service/src/main/java/org/openmetadata/service/resources/ChangeSummaryResource.java:75 📄 openmetadata-service/src/main/java/org/openmetadata/service/resources/ChangeSummaryResource.java:125
Both getChangeSummaryById and getChangeSummaryByFqn accept a SecurityContext and the class stores an Authorizer, but neither endpoint calls authorizer.authorize() before accessing entity data. Every other resource class in the codebase (e.g., TableResource, GlossaryTermResource) performs VIEW_BASIC authorization and PII checks on GET endpoints. This means any authenticated user can retrieve the change summary (including field names and metadata about PII-tagged columns) for any entity they shouldn't have access to.

Bug: Integration tests silently swallow failures via catch-all

📄 openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BaseEntityIT.java
All five changeSummary integration tests wrap their assertions in a try/catch(Exception e) that only logs a warning and continues. If the changeSummary endpoint returns an error (e.g., 500, wrong response shape, or authorization failure), the test will pass silently. This defeats the purpose of the tests — real regressions will never be caught. For instance, get_changeSummaryById_200 catches any exception and logs "ChangeSummary endpoint not available for entity type" instead of failing the test.

Edge Case: limit=0 default makes pagination param check ambiguous

📄 openmetadata-service/src/main/java/org/openmetadata/service/resources/ChangeSummaryResource.java:190
Both limit and offset default to 0 via @DefaultValue("0"). The pagination branch triggers on if (limit > 0 || offset > 0), so a request with no query params skips pagination entirely (correct). However, an explicit ?limit=0&offset=5 would trigger the pagination branch but never add entries (since limit > 0 is false, the inner if would be skipped and the loop would add unlimited entries after the offset). This is a minor inconsistency — limit=0 in the pagination branch effectively means 'no limit' rather than 'zero results'.

Bug: Inconsistent column name extraction for changeSummary key lookup

📄 openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/ColumnDetailPanel.component.tsx:693-699 📄 openmetadata-ui/src/main/resources/ui/src/components/Database/TableDescription/TableDescription.component.tsx:43-50
ColumnDetailPanel uses naive substring((tableFqn?.length ?? 0) + 1) to extract the column name from the FQN, while TableDescription correctly uses EntityLink.getTableColumnNameFromColumnFqn() which does ANTLR-based FQN parsing.

This causes two problems:

  1. If tableFqn is undefined, (null?.length ?? 0) + 1 = 1, so substring(1) just strips the first character of the FQN instead of the table prefix — producing a wrong key.
  2. For nested columns or columns with special characters/quoting, the substring approach doesn't handle FQN escaping, while the EntityLink utility does.

This means the changeSummary badge may silently fail to match for certain columns in the detail panel, even though it works correctly in the table description view.

Bug: useChangeSummary lacks request cancellation causing stale data

📄 openmetadata-ui/src/main/resources/ui/src/hooks/useChangeSummary.ts:73-75 📄 openmetadata-ui/src/main/resources/ui/src/components/DataAssetSummaryPanelV1/DataAssetSummaryPanelV1.tsx:177-180
The useChangeSummary hook has no AbortController or cleanup function in its useEffect. In DataAssetSummaryPanelV1, when users click through different entities quickly, multiple API calls fire in parallel. If an earlier request resolves after a later one, stale change summary data overwrites the correct data for the currently viewed entity.

This is a well-known React pattern issue. The fix is to add either an AbortController or a cancelled flag in the useEffect cleanup.

...and 7 more resolved from earlier reviews

🤖 Prompt for agents
Code Review: Adds changeSummary API endpoint and UI components with 11 issues resolved, but two critical findings remain: resolveBaseUrl() falls back to localhost breaking OAuth in production, and test entities lack cleanup causing data leaks across test runs.

1. ⚠️ Security: resolveBaseUrl() falls back to localhost breaking OAuth in prod

   When both the MCP configuration and the system repository fail to provide a base URL, `resolveBaseUrl()` silently falls back to `http://localhost:8585` (line 124). In production deployments where the system settings haven't been configured yet, this causes the OAuth redirect_uri to point to localhost, which will either fail silently or redirect tokens to an unintended destination. The method should throw an error rather than use an incorrect default, since a wrong redirect_uri in an OAuth flow is a security-sensitive misconfiguration.

   Suggested fix:
   // Instead of returning a silent default:
   // return "http://localhost:8585";
   throw new IllegalStateException(
       "Could not determine base URL for MCP OAuth callback. "
       + "Configure 'openMetadataUrl' in system settings or "
       + "'baseUrl' in MCP configuration.");

2. ⚠️ Quality: Test entities never cleaned up, causing test data leak
   Files: openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ChangeSummaryBadge.spec.ts:27, openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ChangeSummaryBadge.spec.ts:146, openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ChangeSummaryBadge.spec.ts:217, openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ChangeSummaryBadge.spec.ts:268

   The `afterAll` block that deleted the main `table` entity was removed, and three new test cases (`automatedTable`, `propagatedTable`, `manualTable`) each create entities without any cleanup. This accumulates orphaned database services, databases, schemas, and tables in the test environment across runs, which can cause flaky tests and slow down the test suite.
   
   The original test had an `afterAll` block for cleanup that was deleted in this commit. The new tests create entities in `test.step` blocks but never call `.delete()` on them.

   Suggested fix:
   test.afterAll('Cleanup test entities', async ({ browser }) => {
     const { apiContext, afterAction } = await performAdminLogin(browser);
     await table.delete(apiContext);
     // automatedTable, propagatedTable, manualTable are local
     // to each test - consider promoting them to module scope
     // so they can be cleaned up here too.
     await afterAction();
   });

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

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

Comment on lines +117 to +124
// limit=1000 is the backend max. Entities with more tracked field changes
// will have entries beyond this limit silently omitted. Use fieldPrefix
// filtering when targeting a specific section (e.g., 'columns.').
const { changeSummary } = useChangeSummary(
isVersionView ? '' : type,
isVersionView ? '' : data.id ?? '',
{ limit: 1000 }
);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

GenericProvider fetches changeSummary with limit: 1000 for every non-version entity view and no fieldPrefix. This can cause large payloads and still silently truncate changeSummary for entities with >1000 tracked fields (e.g., wide tables), which can lead to missing/incorrect badges. Consider fetching only the needed prefixes (e.g., fieldPrefix=description&limit=1 by default) and loading columns. on demand (e.g., when opening the column panel).

Copilot uses AI. Check for mistakes.
Comment on lines +95 to +103
{config.iconOnly ? (
<Tooltip title={t(config.tooltipKey)}>
<output
aria-live="polite"
className="description-source-icon"
data-testid={config.testId}>
{config.icon}
</output>
</Tooltip>
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The badge icon is rendered inside an HTML <output> element with aria-live="polite". <output> is intended for form calculation results and aria-live can cause unnecessary screen-reader announcements; a non-semantic container (e.g., span) with an appropriate accessible name (or aria-hidden if decorative) would be more correct.

Copilot uses AI. Check for mistakes.
Comment on lines +84 to +106
const tooltipContent = changeSummaryEntry?.changedAt
? formatDateTime(changeSummaryEntry.changedAt)
: undefined;

const renderTooltipContent = useMemo(() => {
if (!showBadge || !config) {
return '';
}

return (
<>
{config.iconOnly ? (
<Tooltip title={t(config.tooltipKey)}>
<output
aria-live="polite"
className="description-source-icon"
data-testid={config.testId}>
{config.icon}
</output>
</Tooltip>
) : (
<Tooltip title={tooltipContent}>
<div
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

PR description says the AI badge tooltip should show who accepted and when, but the tooltip currently shows only a static label (t(config.tooltipKey)) for icon-only badges and never includes changedBy/changedAt. If the tooltip is intended to carry this metadata, include changedBy/changedAt in the tooltip content (or adjust the PR description / UX accordingly).

Copilot uses AI. Check for mistakes.
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Apr 9, 2026

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Apr 9, 2026

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

change summary & generated sources for UI highlights

5 participants