fix(explore): let chart owners overwrite when owners come through as objects - #41352
Conversation
Code Review Agent Run #b437afActionable Suggestions - 0Additional Suggestions - 2
Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
|
The flagged issue is correct. The Here is the corrected implementation for canOverwriteSlice(): boolean {
const canEdit =
this.props.can_overwrite ||
isUserAdmin(this.props.user) ||
this.isCurrentUserOwner();
return !!this.props.slice && canEdit && !this.props.slice?.is_managed_externally;
}There are no other review comments in this pull request to address. superset-frontend/src/explore/components/SaveModal.tsx |
7bfe066 to
e8c7d66
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #41352 +/- ##
=======================================
Coverage 64.60% 64.60%
=======================================
Files 2712 2712
Lines 151168 151177 +9
Branches 34769 34774 +5
=======================================
+ Hits 97658 97665 +7
- Misses 51684 51686 +2
Partials 1826 1826
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Code Review Agent Run #b9c28cActionable Suggestions - 0Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
e8c7d66 to
29cc01d
Compare
Code Review Agent Run #b28adfActionable Suggestions - 0Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Code Review Agent Run #4ad84cActionable Suggestions - 0Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
|
Tried rebasing onto master and this isn't a simple textual conflict — #38831 landed and replaced |
The Save chart modal gates "Save (Overwrite)" on canOverwriteSlice(),
which checked slice.owners.includes(user.userId). The Slice type declares
owners as { id: number }[], and the Explore bootstrap can hydrate owners in
object form, so includes() against a numeric userId never matched. A chart
owner who wasn't also an admin was therefore forced into "Save as...",
which is the path that produces the duplicate/stale-chart behavior reported
in the issue.
Normalize owner identity to the numeric id before comparing so owners are
recognized whether they arrive as plain ids or as objects.
Fixes #38911
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1abcf62 to
7e52419
Compare
Code Review Agent Run #0cdea2Actionable Suggestions - 0Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
…objects (#41352) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…objects (apache#41352) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…r test Three findings from the consolidated review, each verified against the code and each with a control run showing the new test fails against the pre-fix source. Restore was offered on externally managed dashboards. The header menu ANDs !is_managed_externally, but the history panel derived canRestore from dash_edit_perm alone -- and the panel also opens from ?version_history=true, so the menu's gate was not a gate at all. version_restore.py checks editorship and nothing else (is_managed_externally appears nowhere under superset/commands/), which left the UI as the only guard. Both consumers now share selectCanRestoreDashboard, mirroring canOverwriteSlice on the chart side; keeping it in one place is the point, since this bug was two copies of one rule drifting apart. The Save toolbar stayed live during a version preview. userCanEdit and userCanShare are both gated on !isVersionPreviewActive; userCanSaveAs was not, and the toolbar acts on whatever is hydrated -- which during a preview is the snapshot's position_data. Gated it, and made the preview hydrate pass editMode: false explicitly rather than letting hydrate re-derive it from the `edit` URL param, which outlives the navigation that set it. Note the related "in-progress edits vanish" concern does not apply: handlePreview already refuses to preview while hasUnsavedChanges and toasts. The owner overwrite test asserted nothing. SaveModal's extraction to canOverwriteSlice dropped the owner route from apache#41352, and this is the one change here outside VERSION_HISTORY. The direction is right -- UpdateChartCommand gates on raise_for_editorship, and is_editor is admin or subjects intersected with editors/extra_editors, with no owners fallback, so such an owner is answered 403 by the API and the old modal only surfaced that after the request. But the test named for it passed via the shared fixture's editors: [{id: 1}] and would have stayed green either way. Rewritten to assert the behaviour that ships, plus a companion for an owner who is an editor. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…r test Three findings from the consolidated review, each verified against the code and each with a control run showing the new test fails against the pre-fix source. Restore was offered on externally managed dashboards. The header menu ANDs !is_managed_externally, but the history panel derived canRestore from dash_edit_perm alone -- and the panel also opens from ?version_history=true, so the menu's gate was not a gate at all. version_restore.py checks editorship and nothing else (is_managed_externally appears nowhere under superset/commands/), which left the UI as the only guard. Both consumers now share selectCanRestoreDashboard, mirroring canOverwriteSlice on the chart side; keeping it in one place is the point, since this bug was two copies of one rule drifting apart. The Save toolbar stayed live during a version preview. userCanEdit and userCanShare are both gated on !isVersionPreviewActive; userCanSaveAs was not, and the toolbar acts on whatever is hydrated -- which during a preview is the snapshot's position_data. Gated it, and made the preview hydrate pass editMode: false explicitly rather than letting hydrate re-derive it from the `edit` URL param, which outlives the navigation that set it. Note the related "in-progress edits vanish" concern does not apply: handlePreview already refuses to preview while hasUnsavedChanges and toasts. The owner overwrite test asserted nothing. SaveModal's extraction to canOverwriteSlice dropped the owner route from apache#41352, and this is the one change here outside VERSION_HISTORY. The direction is right -- UpdateChartCommand gates on raise_for_editorship, and is_editor is admin or subjects intersected with editors/extra_editors, with no owners fallback, so such an owner is answered 403 by the API and the old modal only surfaced that after the request. But the test named for it passed via the shared fixture's editors: [{id: 1}] and would have stayed green either way. Rewritten to assert the behaviour that ships, plus a companion for an owner who is an editor. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…r test Three findings from the consolidated review, each verified against the code and each with a control run showing the new test fails against the pre-fix source. Restore was offered on externally managed dashboards. The header menu ANDs !is_managed_externally, but the history panel derived canRestore from dash_edit_perm alone -- and the panel also opens from ?version_history=true, so the menu's gate was not a gate at all. version_restore.py checks editorship and nothing else (is_managed_externally appears nowhere under superset/commands/), which left the UI as the only guard. Both consumers now share selectCanRestoreDashboard, mirroring canOverwriteSlice on the chart side; keeping it in one place is the point, since this bug was two copies of one rule drifting apart. The Save toolbar stayed live during a version preview. userCanEdit and userCanShare are both gated on !isVersionPreviewActive; userCanSaveAs was not, and the toolbar acts on whatever is hydrated -- which during a preview is the snapshot's position_data. Gated it, and made the preview hydrate pass editMode: false explicitly rather than letting hydrate re-derive it from the `edit` URL param, which outlives the navigation that set it. Note the related "in-progress edits vanish" concern does not apply: handlePreview already refuses to preview while hasUnsavedChanges and toasts. The owner overwrite test asserted nothing. SaveModal's extraction to canOverwriteSlice dropped the owner route from apache#41352, and this is the one change here outside VERSION_HISTORY. The direction is right -- UpdateChartCommand gates on raise_for_editorship, and is_editor is admin or subjects intersected with editors/extra_editors, with no owners fallback, so such an owner is answered 403 by the API and the old modal only surfaced that after the request. But the test named for it passed via the shared fixture's editors: [{id: 1}] and would have stayed green either way. Rewritten to assert the behaviour that ships, plus a companion for an owner who is an editor. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SUMMARY
The Save chart modal disables Save (Overwrite) for users who actually own the chart, forcing them into Save as.... That's the path behind the duplicate/stale-chart mess in #38911.
canOverwriteSlice()was checkingslice.owners.includes(user.userId). ButSlice.ownersis typed as{ id: number }[], and the Explore bootstrap can hand us owners in object form — soincludes()against a numericuserIdnever matches. Admins squeaked by on theisUserAdminbranch, but plain owners didn't, which is exactly the reporter's scenario.Fix is just to normalize the owner id before comparing, so it works whether owners arrive as plain ids or as
{ id }objects. Pulled the check out into a littleisCurrentUserOwner()helper to keepcanOverwriteSlice()readable.TESTING INSTRUCTIONS
Also added a unit test that pins the object-shaped-owners case (it fails on master, passes here).
ADDITIONAL INFORMATION