Skip to content

Fixes #24208: team ownership broken for special-character names and partial name matches#27453

Merged
harshach merged 40 commits intoopen-metadata:mainfrom
miantalha45:fix/team-ownership-special-chars
Apr 26, 2026
Merged

Fixes #24208: team ownership broken for special-character names and partial name matches#27453
harshach merged 40 commits intoopen-metadata:mainfrom
miantalha45:fix/team-ownership-special-chars

Conversation

@miantalha45
Copy link
Copy Markdown
Contributor

@miantalha45 miantalha45 commented Apr 17, 2026

Changes

Changes

  • URL-encoded the query_filter parameter so team names with & don't break the ES query
  • Added exact name lookup before fuzzy search to prevent partial matches (e.g. "AI Product" vs "AI Products")
  • Fixed manifest key cleanup to preserve list types, preventing Pydantic validation errors
  • Fixed Table Version column pagination: switched to number-based paging for locally-sliced historical columns so Next/Previous buttons work correctly without a search query
  • Added unit tests and Playwright E2E tests covering all three cases

Related

Fixes #24208

Type of change:

  • Bug fix

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes <issue-number>: <short explanation>
  • I have commented on my code, particularly in hard-to-understand areas.

Before

image

After

image

Summary by Gitar

  • Test maintenance:
    • Removed TeamOwnershipSpecialCharacters.spec.ts as the E2E test file, which was previously added as part of the fix.

This will update automatically on new commits.

@miantalha45 miantalha45 requested review from a team as code owners April 17, 2026 06:41
Copilot AI review requested due to automatic review settings April 17, 2026 06:41
@github-actions
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!

@github-actions
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/src/metadata/ingestion/ometa/mixins/user_mixin.py 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

Fixes team/user ownership resolution when names contain special characters (e.g., &) and when fuzzy search returns partial-name matches, improving dbt ingestion and UI ownership assignment reliability.

Changes:

  • URL-encode the ingestion ES query_filter parameter to prevent special characters from breaking query-string parsing.
  • Prefer exact get_by_name lookups before falling back to ES fuzzy search for both Teams and Users.
  • Add unit tests (ingestion) and Playwright E2E coverage (UI) for special-character ownership and exact-name matching scenarios.

Reviewed changes

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

File Description
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/TeamOwnershipSpecialCharacters.spec.ts Adds E2E coverage for assigning team owners with & and for avoiding partial-name mismatches.
ingestion/tests/unit/metadata/ingestion/ometa/test_user_mixin.py Adds unit tests for URL-encoding of ES query filters and exact-match preference in reference resolution.
ingestion/src/metadata/ingestion/ometa/mixins/user_mixin.py Encodes ES query_filter and updates reference lookup to try exact get_by_name before fuzzy search, with early return on empty input.

import { TableClass } from '../../support/entity/TableClass';
import { TeamClass } from '../../support/team/TeamClass';
import { getApiContext, redirectToHomePage, uuid } from '../../utils/common';
import { addOwner, removeOwner } from '../../utils/entity';
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

removeOwner is imported but never used in this spec. This will trigger lint/TS unused-import checks in the Playwright suite; please remove the unused import (or use it as part of cleanup if intended).

Suggested change
import { addOwner, removeOwner } from '../../utils/entity';
import { addOwner } from '../../utils/entity';

Copilot uses AI. Check for mistakes.
entity=Team, name=name, from_=0, size=1
)
raw_filter = query.split("query_filter=")[1].split("&from=")[0]
assert "&" not in raw_filter
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

test_other_special_characters_are_encoded only asserts that & is not present in the encoded query_filter, but the test name/documentation implies broader coverage (e.g., / and whitespace). Either expand assertions to verify the other characters are actually URL-encoded, or rename the test to reflect what it validates.

Suggested change
assert "&" not in raw_filter
assert "&" not in raw_filter
assert "/" not in raw_filter
assert " " not in raw_filter
assert "%26" in raw_filter
assert "%2F" in raw_filter
assert "%20" in raw_filter
decoded = unquote(raw_filter)
parsed = json.loads(decoded)
assert name in parsed["query"]["query_string"]["query"]

Copilot uses AI. Check for mistakes.
Comment on lines +186 to +193
if not name:
return None

maybe_team = self.get_by_name(entity=Team, fqn=name)
if maybe_team is None:
maybe_team = self._search_by_name(
entity=Team, name=name, from_count=from_count, size=size, fields=fields
)
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

PR description mentions a fix to the dbt manifest key cleanup to preserve list types and avoid Pydantic validation errors, but that change is not present in the current codebase (e.g., DbtServiceSource.remove_manifest_non_required_keys still overwrites all non-required top-level keys with {} regardless of original type). If that fix is required for #24208, it needs to be included in this PR or the description should be updated to match what’s actually changed.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@miantalha45 this seems like a valid comment if the issue also was related to dbt?

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.

@pmbrull I have solve this suggestion.

@github-actions
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!

Copilot AI review requested due to automatic review settings April 17, 2026 08:45
@github-actions
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!

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 17, 2026

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 61%
62.03% (60348/97285) 42.02% (31640/75286) 45.05% (9504/21096)

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 7 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

openmetadata-ui/src/main/resources/ui/src/components/Database/TableVersion/TableVersion.component.tsx:91

  • columnsLoading is initialized to true even though columns are now derived locally from currentVersionData (no async fetch). This makes VersionTable render in a loading state on first paint and can cause a visible spinner/flicker despite having data available. Consider initializing columnsLoading to false (or isVersionLoading) and/or computing the initial tableColumns slice in the initial state so the table can render immediately without a loading phase.
  // Pagination state for columns
  const [tableColumns, setTableColumns] = useState<Column[]>([]);
  const [columnsLoading, setColumnsLoading] = useState(true); // Start with loading state
  const [changeDescription, setChangeDescription] = useState<ChangeDescription>(

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

Comments suppressed due to low confidence (1)

openmetadata-ui/src/main/resources/ui/src/components/Database/TableVersion/TableVersion.component.tsx:149

  • searchText is included in the useMemo dependency list but is not used in the memoized object. This creates unnecessary recomputation and is misleading for future maintenance. Remove searchText from the dependency array (or use it in the memo if there’s an intended coupling).
  const paginationProps = useMemo(
    () => ({
      currentPage,
      showPagination,
      isLoading: columnsLoading,
      isNumberBased: true,
      pageSize,
      paging,
      pagingHandler: handleColumnsPageChange,
      onShowSizeChange: handlePageSizeChange,
    }),
    [
      currentPage,
      showPagination,
      columnsLoading,
      searchText,
      pageSize,
      paging,
      handleColumnsPageChange,
      handlePageSizeChange,
    ]
  );

Comment on lines +327 to +339
useEffect(() => {
if (tableFqn && !isVersionLoading) {
// Reset to first page when search changes
fetchPaginatedColumns(currentPage, searchText || undefined);
if (isVersionLoading) {
return;
}
}, [
isVersionLoading,
tableFqn,
searchText,
fetchPaginatedColumns,
pageSize,
currentPage,
]);
const offset = (currentPage - 1) * pageSize;
setTableColumns(filteredHistoricalColumns.slice(offset, offset + pageSize));
handlePagingChange({
offset,
limit: pageSize,
total: filteredHistoricalColumns.length,
});
setColumnsLoading(false);
}, [isVersionLoading, filteredHistoricalColumns, currentPage, pageSize]);
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

useEffect calls handlePagingChange but it isn’t listed in the dependency array. This can cause stale-closure behavior (and will typically fail react-hooks/exhaustive-deps). Add handlePagingChange to the dependency list (or ensure usePaging returns a stable callback and explicitly document/disable the lint rule for this line).

Copilot uses AI. Check for mistakes.
Comment on lines +234 to 237
logger.warning(
f"Failed to resolve owner reference for '{name}' due to: {err}. "
"Skipping owner assignment."
)
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

This warning is emitted for both team and user resolution, and also when is_owner=False, but the message always says "owner reference" / "Skipping owner assignment". Make the message accurate for all call paths (e.g., "Failed to resolve entity reference" and optionally include whether is_owner was requested).

Copilot uses AI. Check for mistakes.
Comment on lines +232 to 237
except Exception as err:
logger.debug(traceback.format_exc())
logger.warning(
f"Failed to resolve owner reference for '{name}' due to: {err}. "
"Skipping owner assignment."
)
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

The current approach logs the traceback separately at debug and then logs a warning without exception context. Prefer a single warning log with attached exception info (so stack traces are retained when needed) and avoid f-string interpolation in logger calls to keep logs structured and defer formatting costs.

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@miantalha45, the changes do not look related to the issue. Can we please revert these and the test file changes as well?

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.

Sure, let me revert these changes.

@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented Apr 23, 2026

Code Review ✅ Approved 1 resolved / 1 findings

Updates team ownership lookups to correctly handle special characters and exact name matching, resolving the API error propagation issue. No issues found.

✅ 1 resolved
Edge Case: get_by_name propagates non-404 APIError unlike old ES path

📄 ingestion/src/metadata/ingestion/ometa/mixins/user_mixin.py:189 📄 ingestion/src/metadata/ingestion/ometa/mixins/user_mixin.py:207
The old code exclusively used _search_by_nameget_entity_from_es, which swallows most exceptions internally. The new code calls self.get_by_name() first (lines 189, 207), which re-raises APIError for any non-404 status (e.g. 500, 503). If the API is temporarily unhealthy, this will now crash the ingestion workflow instead of gracefully falling back.

Since get_reference_by_name is not wrapped in a try/except, a transient server error during the exact-lookup step will bubble up and may abort an entire ingestion run. The old fuzzy-search fallback would have silently returned None.

This is arguably better correctness (fail loudly), but it's a behavior change worth being aware of — especially for large ingestion jobs where a single transient 500 could halt the whole pipeline.

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@miantalha45
Copy link
Copy Markdown
Contributor Author

@aniketkatkar97 I have reverted all unnecessary changes.

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 encountered an error and was unable to review this pull request. You can try again by re-requesting a 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 encountered an error and was unable to review this pull request. You can try again by re-requesting a 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 encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@miantalha45
Copy link
Copy Markdown
Contributor Author

@aniketkatkar97 @harshsoni2024 Please have a look on this PR and let me know if any changes are required.

@aniketkatkar97
Copy link
Copy Markdown
Member

@miantalha45, it's already approved. Thanks.

@miantalha45
Copy link
Copy Markdown
Contributor Author

@miantalha45, it's already approved. Thanks.

That's great.
Waiting for its merge then.

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 encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Comment thread ingestion/src/metadata/ingestion/ometa/mixins/user_mixin.py
@miantalha45
Copy link
Copy Markdown
Contributor Author

@harshach sir, i would love if you can merge this PR today as today is last day of hackathon.

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 encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Ownership association with special characters

6 participants