From 2ee419ace9ba89625b38da950786a7cc2e4a321a Mon Sep 17 00:00:00 2001 From: Nikolay Bonev Date: Wed, 4 Sep 2024 10:36:18 +0300 Subject: [PATCH] improve error handling and fix a bug on assets index --- app/modules/asset/service.server.ts | 2 ++ app/modules/booking/service.server.ts | 4 +++- app/routes/_layout+/assets._index.tsx | 4 ++-- app/utils/error.ts | 17 +++++++++++++++++ .../permissions/permission.validator.server.ts | 4 +++- 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/app/modules/asset/service.server.ts b/app/modules/asset/service.server.ts index 168eb0084..05b200bd5 100644 --- a/app/modules/asset/service.server.ts +++ b/app/modules/asset/service.server.ts @@ -47,6 +47,7 @@ import type { ErrorLabel } from "~/utils/error"; import { ShelfError, isLikeShelfError, + isNotFoundError, maybeUniqueConstraintViolation, } from "~/utils/error"; import { getCurrentSearchParams } from "~/utils/http.server"; @@ -102,6 +103,7 @@ export async function getAsset({ "The asset you are trying to access does not exist or you do not have permission to access it.", additionalData: { id, organizationId }, label, + shouldBeCaptured: !isNotFoundError(cause), }); } } diff --git a/app/modules/booking/service.server.ts b/app/modules/booking/service.server.ts index 3360e7ce0..6c2bdacd9 100644 --- a/app/modules/booking/service.server.ts +++ b/app/modules/booking/service.server.ts @@ -14,7 +14,7 @@ import { getStatusClasses, isOneDayEvent } from "~/utils/calendar"; import { calcTimeDifference } from "~/utils/date-fns"; import { sendNotification } from "~/utils/emitter/send-notification.server"; import type { ErrorLabel } from "~/utils/error"; -import { ShelfError } from "~/utils/error"; +import { isNotFoundError, ShelfError } from "~/utils/error"; import { getCurrentSearchParams } from "~/utils/http.server"; import { ALL_SELECTED_KEY } from "~/utils/list"; import { Logger } from "~/utils/logger"; @@ -870,6 +870,7 @@ export async function getBooking( }, }); } catch (cause) { + const is404 = isNotFoundError(cause); throw new ShelfError({ cause, title: "Booking not found", @@ -877,6 +878,7 @@ export async function getBooking( "The booking you are trying to access does not exist or you do not have permission to access it.", additionalData: { booking }, label, + shouldBeCaptured: !is404, }); } } diff --git a/app/routes/_layout+/assets._index.tsx b/app/routes/_layout+/assets._index.tsx index d7f346ca8..01a075e49 100644 --- a/app/routes/_layout+/assets._index.tsx +++ b/app/routes/_layout+/assets._index.tsx @@ -67,7 +67,7 @@ import { PermissionEntity, } from "~/utils/permissions/permission.data"; import { userHasPermission } from "~/utils/permissions/permission.validator.client"; -import { validatePermission } from "~/utils/permissions/permission.validator.server"; +import { hasPermission } from "~/utils/permissions/permission.validator.server"; import { requirePermission } from "~/utils/roles.server"; import { canImportAssets } from "~/utils/subscription.server"; import { tw } from "~/utils/tw"; @@ -194,7 +194,7 @@ export async function loader({ context, request }: LoaderFunctionArgs) { modelName, canImportAssets: canImportAssets(tierLimit) && - (await validatePermission({ + (await hasPermission({ organizationId, userId, roles: role ? [role] : [], diff --git a/app/utils/error.ts b/app/utils/error.ts index 431150d04..eccc628bf 100644 --- a/app/utils/error.ts +++ b/app/utils/error.ts @@ -1,5 +1,6 @@ import { createId } from "@paralleldrive/cuid2"; import { Prisma } from "@prisma/client"; +import type { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; import type { ValidationError } from "./http"; /** @@ -172,6 +173,8 @@ export class ShelfError extends Error { : shouldBeCaptured) ?? true; this.status = isLikeShelfError(cause) ? status || cause.status || 500 + : isNotFoundError(cause) + ? 404 : status || 500; this.traceId = traceId || createId(); } @@ -190,6 +193,20 @@ export function isLikeShelfError(cause: unknown): cause is ShelfError { ); } +/** + * This helper function is used to check if an error is an instance of `ShelfError` or an object that looks like an `ShelfError`. + */ +export function isNotFoundError( + cause: unknown +): cause is PrismaClientKnownRequestError { + return ( + typeof cause === "object" && + cause !== null && + "code" in cause && + cause.code === "P2025" + ); +} + export function makeShelfError( cause: unknown, additionalData?: AdditionalData diff --git a/app/utils/permissions/permission.validator.server.ts b/app/utils/permissions/permission.validator.server.ts index fe0a21205..4c7030755 100644 --- a/app/utils/permissions/permission.validator.server.ts +++ b/app/utils/permissions/permission.validator.server.ts @@ -16,7 +16,9 @@ export interface PermissionCheckProps { entity: PermissionEntity; } -async function hasPermission(params: PermissionCheckProps): Promise { +export async function hasPermission( + params: PermissionCheckProps +): Promise { let { userId, entity, action, organizationId, roles } = params; try {