mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-20 03:07:53 -05:00
refactor(dashboards): address review on removeWidgetFromDashboard
- Drop the prisma.$transaction wrapper; find + delete is two sequential steps, doesn't need a transaction. - Drop the redundant ResourceNotFoundError catch branch; the trailing `throw error` already lets it bubble. - Let action-client infer ctx / parsedInput types. Tests: cover the two catch branches (Prisma -> DatabaseError, unknown rethrow) so the new function is fully line-covered.
This commit is contained in:
@@ -334,34 +334,24 @@ const ZRemoveWidgetFromDashboardAction = z.object({
|
||||
export const removeWidgetFromDashboardAction = authenticatedActionClient
|
||||
.inputSchema(ZRemoveWidgetFromDashboardAction)
|
||||
.action(
|
||||
withAuditLogging(
|
||||
"deleted",
|
||||
"dashboardWidget",
|
||||
async ({
|
||||
ctx,
|
||||
parsedInput,
|
||||
}: {
|
||||
ctx: AuthenticatedActionClientCtx;
|
||||
parsedInput: z.infer<typeof ZRemoveWidgetFromDashboardAction>;
|
||||
}) => {
|
||||
const { organizationId, workspaceId } = await checkWorkspaceAccess(
|
||||
ctx.user.id,
|
||||
parsedInput.workspaceId,
|
||||
"readWrite"
|
||||
);
|
||||
await checkDashboardsEnabled(organizationId);
|
||||
withAuditLogging("deleted", "dashboardWidget", async ({ ctx, parsedInput }) => {
|
||||
const { organizationId, workspaceId } = await checkWorkspaceAccess(
|
||||
ctx.user.id,
|
||||
parsedInput.workspaceId,
|
||||
"readWrite"
|
||||
);
|
||||
await checkDashboardsEnabled(organizationId);
|
||||
|
||||
const widget = await removeWidgetFromDashboard(
|
||||
parsedInput.dashboardId,
|
||||
workspaceId,
|
||||
parsedInput.widgetId
|
||||
);
|
||||
const widget = await removeWidgetFromDashboard(
|
||||
parsedInput.dashboardId,
|
||||
workspaceId,
|
||||
parsedInput.widgetId
|
||||
);
|
||||
|
||||
ctx.auditLoggingCtx.organizationId = organizationId;
|
||||
ctx.auditLoggingCtx.workspaceId = workspaceId;
|
||||
ctx.auditLoggingCtx.dashboardWidgetId = widget.id;
|
||||
ctx.auditLoggingCtx.oldObject = widget;
|
||||
return { success: true };
|
||||
}
|
||||
)
|
||||
ctx.auditLoggingCtx.organizationId = organizationId;
|
||||
ctx.auditLoggingCtx.workspaceId = workspaceId;
|
||||
ctx.auditLoggingCtx.dashboardWidgetId = widget.id;
|
||||
ctx.auditLoggingCtx.oldObject = widget;
|
||||
return { success: true };
|
||||
})
|
||||
);
|
||||
|
||||
@@ -48,6 +48,7 @@ vi.mock("@formbricks/database", () => {
|
||||
findFirst: vi.fn(),
|
||||
findMany: vi.fn(),
|
||||
},
|
||||
dashboardWidget: txWidget,
|
||||
$transaction: vi.fn((cb: any) => cb({ dashboard: txDash, chart: txChart, dashboardWidget: txWidget })),
|
||||
},
|
||||
};
|
||||
@@ -704,5 +705,24 @@ describe("Dashboard Service", () => {
|
||||
).rejects.toMatchObject({ name: "ResourceNotFoundError", resourceType: "DashboardWidget" });
|
||||
expect(mockTxWidget.delete).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("wraps Prisma errors in DatabaseError", async () => {
|
||||
mockTxWidget.findFirst.mockRejectedValue(makePrismaError("P9999"));
|
||||
const { removeWidgetFromDashboard } = await import("./dashboards");
|
||||
|
||||
await expect(
|
||||
removeWidgetFromDashboard(mockDashboardId, mockWorkspaceId, mockWidgetId)
|
||||
).rejects.toMatchObject({ name: "DatabaseError" });
|
||||
});
|
||||
|
||||
test("rethrows unknown errors", async () => {
|
||||
const error = new Error("boom");
|
||||
mockTxWidget.findFirst.mockRejectedValue(error);
|
||||
const { removeWidgetFromDashboard } = await import("./dashboards");
|
||||
|
||||
await expect(removeWidgetFromDashboard(mockDashboardId, mockWorkspaceId, mockWidgetId)).rejects.toBe(
|
||||
error
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -309,21 +309,16 @@ export const removeWidgetFromDashboard = async (
|
||||
validateInputs([dashboardId, ZId], [workspaceId, ZId], [widgetId, ZId]);
|
||||
|
||||
try {
|
||||
return await prisma.$transaction(async (tx) => {
|
||||
const widget = await tx.dashboardWidget.findFirst({
|
||||
where: { id: widgetId, dashboard: { id: dashboardId, workspaceId } },
|
||||
});
|
||||
|
||||
if (!widget) {
|
||||
throw new ResourceNotFoundError("DashboardWidget", widgetId);
|
||||
}
|
||||
|
||||
return tx.dashboardWidget.delete({ where: { id: widgetId } });
|
||||
const widget = await prisma.dashboardWidget.findFirst({
|
||||
where: { id: widgetId, dashboard: { id: dashboardId, workspaceId } },
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof ResourceNotFoundError) {
|
||||
throw error;
|
||||
|
||||
if (!widget) {
|
||||
throw new ResourceNotFoundError("DashboardWidget", widgetId);
|
||||
}
|
||||
|
||||
return await prisma.dashboardWidget.delete({ where: { id: widgetId } });
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user