Skip to content

fix(ui): prevent 400 on second domain tag update (#32358) - #32444

Merged
anuj-kumary merged 1 commit into
2.0from
backport/32358-domain-tag-update-400-2.0
Sep 2, 2026
Merged

anuj-kumary merged 1 commit into
2.0from
backport/32358-domain-tag-update-400-2.0

Conversation

@anuj-kumary

@anuj-kumary anuj-kumary commented Sep 2, 2026

Copy link
Copy Markdown
Member

Summary

Backport of #32358 to 2.0.

Fixes the Sentry issue where PATCH /api/v1/domains/{id} returns 400 when a user updates tags on a domain twice in succession (first update succeeds, second fails).

Sentry: https://collate-3b.sentry.io/issues/7703855605/

Root cause — two contributing bugs:

  • TagsContainerV2.handleSave converted style: nullstyle: {} via option.style ?? {}. This created a spurious replace /tags/0/style operation in the fast-json-patch diff that could interact with tag validation (mutually-exclusive checks after addDerivedTags) to reject the request.
  • DomainDetailPage.handleDomainUpdate replaced the entire React Query cache with the PATCH response via setActiveDomain(response). The PATCH endpoint only returns patchFields (parent, children, experts, tags, owners, followers) — not extension, votes, or certification. Subsequent updates built their compare() diff from this incomplete baseline.

Fix:

  • Preserve the server's style value as-is (style: option.style instead of style: option.style ?? {})
  • Keep the optimistic setActiveDomain(response) but follow it with queryClient.invalidateQueries({ queryKey: domainCacheKey }), so the cache is re-hydrated with the full entity (extension, votes, certification) rather than a partial one

Backport notes

Cherry-picked cleanly from c0e42293 (squash merge of #32358) with -x; no conflicts and no adaptation needed. Diff is byte-identical to the original: 4 files, +138/-4. queryClient and domainCacheKey already exist on 2.0 in DomainDetailPage.component.tsx, so the added line resolves without further changes.

Test plan

  • Navigate to a domain, add a classification tag, verify PATCH succeeds
  • Immediately add a second tag from a different classification, verify PATCH succeeds (was 400 before)
  • Verify existing tags are preserved after both updates
  • Removing a certification no longer leaves the stale value in the UI (covered by the invalidateQueries change and the Playwright DomainTierCertificationVoting test)

🤖 Generated with Claude Code


skip-pr-checks applied per the 2.0 backport convention (#32416, #32395): a backport has no issue of its own to link — the original issue is tracked on #32358.

* fix(ui): prevent 400 on second domain tag update

Two bugs caused PATCH /api/v1/domains/{id} to fail with 400 when a user
updated tags twice in succession:

1. TagsContainerV2.handleSave converted style: null to style: {} via the
   nullish-coalescing fallback, producing a spurious replace operation in
   the JSON-Patch diff that could interact with tag validation to reject
   the request.

2. DomainDetailPage.handleDomainUpdate replaced the entire React-Query
   cache with the PATCH response, which only carries patchFields
   (parent, children, experts, tags, owners, followers) and omits
   extension, votes, and certification. Subsequent updates built their
   diff from this incomplete baseline.

Fix: preserve the server's style value as-is and merge the PATCH
response into the existing cache instead of replacing it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(ui): add tests for domain tag update fixes

- TagsContainerV2: verify style: null is preserved as null (not
  converted to {}) when passing tag data through handleSave.
- DomainDetailPage: verify handleDomainUpdate merges the PATCH response
  with the existing cached domain, so fields not returned by the PATCH
  endpoint (e.g. extension) are preserved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): invalidate domain query after PATCH instead of blind merge

The previous cache-merge approach ({ ...prev, ...response }) preserved
stale fields like certification when the PATCH response omitted them.
After clearing a certification the old value survived in the UI because
the PATCH endpoint does not include certification in patchFields.

Replace the merge with setActiveDomain(response) for an immediate
optimistic update, followed by queryClient.invalidateQueries to re-fetch
the full entity with all fields. This fixes the Playwright
DomainTierCertificationVoting test which asserts the add-certification
button appears after removing a certification.

Update the DomainDetailPage test to verify invalidateQueries is called
after a successful PATCH.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
(cherry picked from commit c0e4229)
@anuj-kumary
anuj-kumary requested a review from a team as a code owner September 2, 2026 08:44
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

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

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

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.

@anuj-kumary anuj-kumary added UI UI specific issues safe to test Add this label to run secure Github workflows on PRs labels Sep 2, 2026
@gitar-bot

gitar-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

Backport fixes a 400 error on successive domain tag updates by preserving the server's style value in TagsContainerV2.handleSave and invalidating the React Query cache in DomainDetailPage.handleDomainUpdate to re-hydrate with the full entity. 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 👍 / 👎 | Powered by Gitar — free for open source

@anuj-kumary anuj-kumary added the skip-pr-checks Bypass PR metadata validation check label Sep 2, 2026
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 67%
67.11% (80544/120007) 51.63% (49400/95671) 52.52% (14729/28043)

@sonarqubecloud

sonarqubecloud Bot commented Sep 2, 2026

Copy link
Copy Markdown

@anuj-kumary anuj-kumary self-assigned this Sep 2, 2026
@anuj-kumary
anuj-kumary enabled auto-merge (squash) September 2, 2026 09:48
@anuj-kumary
anuj-kumary merged commit 5df2ee0 into 2.0 Sep 2, 2026
184 of 198 checks passed
@anuj-kumary
anuj-kumary deleted the backport/32358-domain-tag-update-400-2.0 branch September 2, 2026 09:49
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 skip-pr-checks Bypass PR metadata validation check UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants