fix: do not allow removal of last table view (#1366)

Closes #1358
This commit is contained in:
Thaïs 2023-08-29 10:03:56 +02:00 committed by GitHub
parent 8bb4071f09
commit 2b3e96b9ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 7 deletions

View File

@ -31,6 +31,7 @@ import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousH
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId'; import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { assertNotNull } from '~/utils/assert';
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext'; import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
import { savedTableColumnsScopedState } from '../../states/savedTableColumnsScopedState'; import { savedTableColumnsScopedState } from '../../states/savedTableColumnsScopedState';
@ -184,12 +185,16 @@ export const TableViewsDropdownButton = ({
onClick={(event) => handleEditViewButtonClick(event, view.id)} onClick={(event) => handleEditViewButtonClick(event, view.id)}
icon={<IconPencil size={theme.icon.size.sm} />} icon={<IconPencil size={theme.icon.size.sm} />}
/>, />,
<IconButton views.length > 1 ? (
key="delete" <IconButton
onClick={(event) => handleDeleteViewButtonClick(event, view.id)} key="delete"
icon={<IconTrash size={theme.icon.size.sm} />} onClick={(event) =>
/>, handleDeleteViewButtonClick(event, view.id)
]} }
icon={<IconTrash size={theme.icon.size.sm} />}
/>
) : null,
].filter(assertNotNull)}
onClick={() => handleViewSelect(view.id)} onClick={() => handleViewSelect(view.id)}
> >
<IconList size={theme.icon.size.md} /> <IconList size={theme.icon.size.md} />

View File

@ -1,4 +1,8 @@
import { UseGuards } from '@nestjs/common'; import {
BadRequestException,
ForbiddenException,
UseGuards,
} from '@nestjs/common';
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { accessibleBy } from '@casl/prisma'; import { accessibleBy } from '@casl/prisma';
@ -94,7 +98,35 @@ export class ViewResolver {
@CheckAbilities(DeleteViewAbilityHandler) @CheckAbilities(DeleteViewAbilityHandler)
async deleteManyView( async deleteManyView(
@Args() args: DeleteManyViewArgs, @Args() args: DeleteManyViewArgs,
@AuthWorkspace() workspace: Workspace,
): Promise<AffectedRows> { ): Promise<AffectedRows> {
const viewsToDelete = await this.viewService.findMany({
where: args.where,
});
if (!viewsToDelete.length) return { count: 0 };
const { objectId } = viewsToDelete[0];
if (viewsToDelete.some((view) => view.objectId !== objectId)) {
throw new BadRequestException(
`Views must have the same objectId '${objectId}'`,
);
}
const viewsNb = await this.viewService.count({
where: {
objectId: { equals: objectId },
workspaceId: { equals: workspace.id },
},
});
if (viewsNb - viewsToDelete.length <= 0) {
throw new ForbiddenException(
`Deleting last '${objectId}' view is not allowed`,
);
}
return this.viewService.deleteMany({ return this.viewService.deleteMany({
where: args.where, where: args.where,
}); });