This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new c0e5f5226d4 fix(explore): let chart owners overwrite when owners come
through as objects (#41352)
c0e5f5226d4 is described below
commit c0e5f5226d493324dccf8fc6ec1a847dbb452f3e
Author: Evan Rusackas <[email protected]>
AuthorDate: Mon Jul 13 03:10:00 2026 -0700
fix(explore): let chart owners overwrite when owners come through as
objects (#41352)
Co-authored-by: Claude Opus 4.8 <[email protected]>
---
.../src/explore/components/SaveModal.test.tsx | 21 +++++++++++++++++++++
.../src/explore/components/SaveModal.tsx | 22 ++++++++++++++++++++--
2 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/superset-frontend/src/explore/components/SaveModal.test.tsx
b/superset-frontend/src/explore/components/SaveModal.test.tsx
index 5538bd76f27..20c3c7977dd 100644
--- a/superset-frontend/src/explore/components/SaveModal.test.tsx
+++ b/superset-frontend/src/explore/components/SaveModal.test.tsx
@@ -358,6 +358,27 @@ test('enables overwrite option for admin non-editor', ()
=> {
expect(getByRole('radio', { name: 'Save (Overwrite)' })).toBeEnabled();
});
+test('enables overwrite option for owner when owners are objects', () => {
+ // The Slice type declares `owners` as `number[]`, but the explore
+ // bootstrap can deliver owners as `{ id: number }` objects at runtime.
+ // A chart owner must still be able to overwrite their own chart in that
case.
+ const { getByRole } = setup(
+ {},
+ mockStore({
+ ...initialState,
+ explore: {
+ ...initialState.explore,
+ slice: {
+ ...initialState.explore.slice,
+ owners: [{ id: 1 }],
+ },
+ },
+ user: { userId: 1 },
+ }),
+ );
+ expect(getByRole('radio', { name: 'Save (Overwrite)' })).toBeEnabled();
+});
+
test('updates slice name and selected dashboard', async () => {
const dashboardId = mockEvent.value;
const saveDataset = jest.fn().mockResolvedValue(undefined);
diff --git a/superset-frontend/src/explore/components/SaveModal.tsx
b/superset-frontend/src/explore/components/SaveModal.tsx
index 62ce1b47ea7..1d0bf9e2740 100755
--- a/superset-frontend/src/explore/components/SaveModal.tsx
+++ b/superset-frontend/src/explore/components/SaveModal.tsx
@@ -228,6 +228,21 @@ const SaveModal = ({
const history = useHistory();
const theme = useTheme();
+ const isCurrentUserOwner = useCallback((): boolean => {
+ const userId = user?.userId;
+ if (userId === undefined) {
+ return false;
+ }
+ // The Slice type declares `owners` as `number[]`, but the explore
+ // bootstrap payload can carry owners as API `Owner` objects (`{ id }`)
+ // instead, so normalize to the numeric id before comparing.
+ return Boolean(
+ slice?.owners?.some((owner: number | { id?: number }) =>
+ typeof owner === 'number' ? owner === userId : owner?.id === userId,
+ ),
+ );
+ }, [slice, user]);
+
const canOverwriteSlice = useCallback((): boolean => {
const userSubjects = getBootstrapData()?.common?.user_subjects ?? [];
const canEditSlice = Boolean(
@@ -238,10 +253,13 @@ const SaveModal = ({
return (
!!slice &&
- (can_overwrite || isUserAdmin(user) || canEditSlice) &&
+ (can_overwrite ||
+ isUserAdmin(user) ||
+ canEditSlice ||
+ isCurrentUserOwner()) &&
!slice?.is_managed_externally
);
- }, [can_overwrite, slice, user]);
+ }, [can_overwrite, slice, user, isCurrentUserOwner]);
const [newSliceName, setNewSliceName] = useState<string | undefined>(
sliceName,