refactor: Specific error status codes (#18847)

This commit is contained in:
Omar López
2025-01-24 07:09:54 +00:00
committed by GitHub
parent b3f946dd0b
commit 1fd4c3fd4c
4 changed files with 54 additions and 8 deletions
+1 -4
View File
@@ -93,10 +93,7 @@ test.describe("free user", () => {
await page.goto(bookingUrl);
// book same time spot again
await bookTimeSlot(page, {
/** FIXME: this should be a 409 conflict */
expectedStatusCode: 500,
});
await bookTimeSlot(page, { expectedStatusCode: 409 });
await page.locator("[data-testid=booking-fail]").waitFor({ state: "visible" });
});
+19 -1
View File
@@ -1,5 +1,9 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { describe, it, expect, vi } from "vitest";
import { describe, expect, it, vi } from "vitest";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { TRPCError } from "@trpc/server";
import { defaultResponder } from "./defaultResponder";
@@ -19,4 +23,18 @@ describe("defaultResponder", () => {
await defaultResponder(f)(req, res);
expect(res.json).not.toHaveBeenCalled();
});
it("should respond with status code 409 for NoAvailableUsersFound", async () => {
const f = vi.fn().mockRejectedValue(new Error(ErrorCode.NoAvailableUsersFound));
const req = {} as NextApiRequest;
const res = { status: vi.fn().mockReturnThis(), json: vi.fn() } as unknown as NextApiResponse;
await defaultResponder(f)(req, res);
expect(res.status).toHaveBeenCalledWith(409);
});
it("Rate limit should respond with a 429 status code", async () => {
const f = vi.fn().mockRejectedValue(new TRPCError({ code: "TOO_MANY_REQUESTS" }));
const req = {} as NextApiRequest;
const res = { status: vi.fn().mockReturnThis(), json: vi.fn() } as unknown as NextApiResponse;
await defaultResponder(f)(req, res);
expect(res.status).toHaveBeenCalledWith(429);
});
});
+2 -2
View File
@@ -27,9 +27,9 @@ export function defaultResponder<T>(
const error = getServerErrorFromUnknown(err);
// we don't want to report Bad Request errors to Sentry / console
if (!(error.statusCode >= 400 && error.statusCode < 500)) {
console.error(err);
console.error(error);
const captureException = (await import("@sentry/nextjs")).captureException;
captureException(err);
captureException(error);
}
return res
.status(error.statusCode)
@@ -3,6 +3,8 @@ import Stripe from "stripe";
import type { ZodIssue } from "zod";
import { ZodError } from "zod";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { TRPCError } from "@trpc/server";
import { getHTTPStatusCodeFromError } from "@trpc/server/http";
@@ -70,7 +72,8 @@ export function getServerErrorFromUnknown(cause: unknown): HttpError {
};
}
if (cause instanceof Error) {
return getHttpError({ statusCode: 500, cause });
const statusCode = getStatusCode(cause);
return getHttpError({ statusCode, cause });
}
if (typeof cause === "string") {
// @ts-expect-error https://github.com/tc39/proposal-error-cause
@@ -83,6 +86,34 @@ export function getServerErrorFromUnknown(cause: unknown): HttpError {
});
}
function getStatusCode(cause: Error): number {
switch (cause.message) {
case ErrorCode.RequestBodyWithouEnd:
case ErrorCode.MissingPaymentCredential:
case ErrorCode.MissingPaymentAppId:
case ErrorCode.AvailabilityNotFoundInSchedule:
return 400;
case ErrorCode.CancelledBookingsCannotBeRescheduled:
return 403;
case ErrorCode.NoAvailableUsersFound:
case ErrorCode.HostsUnavailableForBooking:
case ErrorCode.PaymentCreationFailure:
case ErrorCode.ChargeCardFailure:
case ErrorCode.AlreadySignedUpForBooking:
case ErrorCode.BookingSeatsFull:
case ErrorCode.NotEnoughAvailableSeats:
return 409;
case ErrorCode.EventTypeNotFound:
case ErrorCode.BookingNotFound:
return 404;
case ErrorCode.UnableToSubscribeToThePlatform:
case ErrorCode.UpdatingOauthClientError:
case ErrorCode.CreatingOauthClientError:
default:
return 500;
}
}
function getHttpError<T extends Error>({ statusCode, cause }: { statusCode: number; cause: T }) {
const redacted = redactError(cause);
return new HttpError({ statusCode, message: redacted.message, cause: redacted });