Files
calendar/packages/trpc/server/lib/toTRPCError.test.ts
T
Benny JooGitHubbenny@cal.com <sldisek783@gmail.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
3bf2a8fa6c refactor: replace TRPCError with ErrorWithCode in packages/features (#25482)
* refactor: replace TRPCError with ErrorWithCode in packages/features

This refactor moves error handling from throwing TRPCError directly in
packages/features to throwing ErrorWithCode instead. The conversion to
TRPCError now happens at the TRPC layer.

Changes:
- Add generic ErrorCode values (Unauthorized, Forbidden, NotFound,
  BadRequest, InternalServerError) to errorCodes.ts
- Update getServerErrorFromUnknown to map new ErrorCodes to proper
  HTTP status codes
- Create toTRPCError helper in packages/trpc/server/lib
- Create errorMappingMiddleware in packages/trpc/server/middlewares
- Migrate TRPCError throws in packages/features to ErrorWithCode:
  - teamService.ts
  - getEventTypeById.ts
  - eventTypeRepository.ts
  - OrganizationPermissionService.ts
  - OrganizationPaymentService.ts
  - sso.ts
  - handleCreatePhoneCall.ts
  - userCanCreateTeamGroupMapping.ts

This improves separation of concerns by making packages/features
transport-agnostic, allowing the same feature code to be reused from
tRPC, API routes, workers, etc.

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* fix: remove isTrpcCall parameter and fix lint warning

- Remove isTrpcCall parameter from get.handler.ts call since the
  feature layer no longer needs to know about tRPC
- Fix unsafe optional chaining lint warning in getEventTypesByViewer.ts
  by precomputing usersSource variable
- Complete migration of getEventTypesByViewer.ts to ErrorWithCode

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* revert

* add eslint rule

* add comment

* fix: add isTrpcCall back to getEventTypeById interface

The user reverted the removal of isTrpcCall parameter from the handler,
so we need to add it back to the interface to fix the type error.

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* test: update teamService tests to expect ErrorWithCode instead of TRPCError

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* refactor

* wip

* feat: integrate errorMappingMiddleware into base TRPC procedure

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* connect middlewares

* revert

* revert

* refactor

* rename

* fix: handle ErrorWithCode in teams server-page error handling

The error handling was checking for TRPCError, but teamService now throws
ErrorWithCode. This caused the 'This invitation is not for your account'
error message to not be displayed when a wrong user tries to use an
invitation link.

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* fix

* fix

* fix

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-12-02 08:11:17 -03:00

88 lines
2.9 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
import { TRPCError } from "@trpc/server";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { ErrorWithCode } from "@calcom/lib/errors";
import { convertErrorWithCodeToTRPCError } from "./toTRPCError";
describe("convertErrorWithCodeToTRPCError", () => {
beforeEach(() => {
vi.resetAllMocks();
});
it("converts ErrorWithCode with Forbidden code to TRPCError with FORBIDDEN", () => {
const error = new ErrorWithCode(ErrorCode.Forbidden, "This invitation is not for your account");
const result = convertErrorWithCodeToTRPCError(error);
expect(result).toBeInstanceOf(TRPCError);
expect((result as TRPCError).code).toBe("FORBIDDEN");
expect((result as TRPCError).message).toBe("This invitation is not for your account");
});
it("converts ErrorWithCode with NotFound code to TRPCError with NOT_FOUND", () => {
const error = new ErrorWithCode(ErrorCode.NotFound, "User not found");
const result = convertErrorWithCodeToTRPCError(error);
expect(result).toBeInstanceOf(TRPCError);
expect((result as TRPCError).code).toBe("NOT_FOUND");
expect((result as TRPCError).message).toBe("User not found");
});
it("converts ErrorWithCode with Unauthorized code to TRPCError with UNAUTHORIZED", () => {
const error = new ErrorWithCode(ErrorCode.Unauthorized, "Not authenticated");
const result = convertErrorWithCodeToTRPCError(error);
expect(result).toBeInstanceOf(TRPCError);
expect((result as TRPCError).code).toBe("UNAUTHORIZED");
expect((result as TRPCError).message).toBe("Not authenticated");
});
it("converts ErrorWithCode with BadRequest code to TRPCError with BAD_REQUEST", () => {
const error = new ErrorWithCode(ErrorCode.BadRequest, "Invalid input");
const result = convertErrorWithCodeToTRPCError(error);
expect(result).toBeInstanceOf(TRPCError);
expect((result as TRPCError).code).toBe("BAD_REQUEST");
expect((result as TRPCError).message).toBe("Invalid input");
});
it("returns generic Error unchanged", () => {
const error = new Error("Something went wrong");
const result = convertErrorWithCodeToTRPCError(error);
expect(result).toBe(error);
expect(result).not.toBeInstanceOf(TRPCError);
});
it("returns TRPCError unchanged", () => {
const error = new TRPCError({ code: "NOT_FOUND", message: "Resource not found" });
const result = convertErrorWithCodeToTRPCError(error);
expect(result).toBe(error);
});
it("returns string error unchanged", () => {
const error = "Something went wrong";
const result = convertErrorWithCodeToTRPCError(error);
expect(result).toBe(error);
});
it("returns unknown error type unchanged", () => {
const error = { unexpected: "object" };
const result = convertErrorWithCodeToTRPCError(error);
expect(result).toBe(error);
});
});