From cdba1920fce52deefeb314696f34049b7614cf7f Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 5 May 2023 18:19:10 +0200 Subject: [PATCH] refactor: split up routers to separate lambdas (#8041) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * split up routers to separate lambdas * fix responsemeta * move * add typeguards to make sure all endpoints are covered in the approuter * prettier * move slotsrouter * split ssg/ssr * make sure correct headers are sent on viewer.public * make sure correct headers are sent + use ctx.prisma * Fixed new prefetch broken by merge * Fixes after merge * Created separate API route for all tRPC routers * More fixes from refactor * Fixed tRPC query for slots * Put back extra line * Fixed type checks * Removed Endpoint type check since it loads from client * Reverted change in getSchedule test * Fix trpc routes in expectations * Fix one more route test --------- Co-authored-by: Efraín Rochín Co-authored-by: Alex van Andel Co-authored-by: Peer Richelsen Co-authored-by: Keith Williams Co-authored-by: zomars Co-authored-by: Hariom Balhara --- apps/web/pages/api/trpc/[trpc].ts | 83 ------------------- apps/web/pages/api/trpc/apiKeys/[trpc].ts | 4 + .../pages/api/trpc/appRoutingForms/[trpc].ts | 4 + apps/web/pages/api/trpc/apps/[trpc].ts | 4 + apps/web/pages/api/trpc/auth/[trpc].ts | 4 + .../web/pages/api/trpc/availability/[trpc].ts | 4 + apps/web/pages/api/trpc/bookings/[trpc].ts | 4 + .../pages/api/trpc/deploymentSetup/[trpc].ts | 4 + apps/web/pages/api/trpc/eth/[trpc].ts | 4 + apps/web/pages/api/trpc/eventTypes/[trpc].ts | 4 + apps/web/pages/api/trpc/features/[trpc].ts | 4 + apps/web/pages/api/trpc/insights/[trpc].ts | 4 + apps/web/pages/api/trpc/payments/[trpc].ts | 4 + apps/web/pages/api/trpc/public/[trpc].ts | 4 + apps/web/pages/api/trpc/saml/[trpc].ts | 4 + apps/web/pages/api/trpc/slots/[trpc].ts | 4 + apps/web/pages/api/trpc/teams/[trpc].ts | 4 + apps/web/pages/api/trpc/users/[trpc].ts | 4 + apps/web/pages/api/trpc/viewer/[trpc].ts | 4 + apps/web/pages/api/trpc/webhook/[trpc].ts | 4 + apps/web/pages/api/trpc/workflows/[trpc].ts | 4 + apps/web/playwright/availability.e2e.ts | 2 +- apps/web/playwright/booking-pages.e2e.ts | 2 +- .../playwright/tests/basic.e2e.ts | 2 +- packages/trpc/react/trpc.ts | 76 ++++++++++++++--- packages/trpc/server/createNextApiHandler.ts | 74 +++++++++++++++++ playwright.config.ts | 12 +++ 27 files changed, 235 insertions(+), 96 deletions(-) delete mode 100644 apps/web/pages/api/trpc/[trpc].ts create mode 100644 apps/web/pages/api/trpc/apiKeys/[trpc].ts create mode 100644 apps/web/pages/api/trpc/appRoutingForms/[trpc].ts create mode 100644 apps/web/pages/api/trpc/apps/[trpc].ts create mode 100644 apps/web/pages/api/trpc/auth/[trpc].ts create mode 100644 apps/web/pages/api/trpc/availability/[trpc].ts create mode 100644 apps/web/pages/api/trpc/bookings/[trpc].ts create mode 100644 apps/web/pages/api/trpc/deploymentSetup/[trpc].ts create mode 100644 apps/web/pages/api/trpc/eth/[trpc].ts create mode 100644 apps/web/pages/api/trpc/eventTypes/[trpc].ts create mode 100644 apps/web/pages/api/trpc/features/[trpc].ts create mode 100644 apps/web/pages/api/trpc/insights/[trpc].ts create mode 100644 apps/web/pages/api/trpc/payments/[trpc].ts create mode 100644 apps/web/pages/api/trpc/public/[trpc].ts create mode 100644 apps/web/pages/api/trpc/saml/[trpc].ts create mode 100644 apps/web/pages/api/trpc/slots/[trpc].ts create mode 100644 apps/web/pages/api/trpc/teams/[trpc].ts create mode 100644 apps/web/pages/api/trpc/users/[trpc].ts create mode 100644 apps/web/pages/api/trpc/viewer/[trpc].ts create mode 100644 apps/web/pages/api/trpc/webhook/[trpc].ts create mode 100644 apps/web/pages/api/trpc/workflows/[trpc].ts create mode 100644 packages/trpc/server/createNextApiHandler.ts diff --git a/apps/web/pages/api/trpc/[trpc].ts b/apps/web/pages/api/trpc/[trpc].ts deleted file mode 100644 index 4dd7642397..0000000000 --- a/apps/web/pages/api/trpc/[trpc].ts +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file contains tRPC's HTTP response handler - */ -import { z } from "zod"; - -import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; -import * as trpcNext from "@calcom/trpc/server/adapters/next"; -import { createContext as createTrpcContext } from "@calcom/trpc/server/createContext"; -import { appRouter } from "@calcom/trpc/server/routers/_app"; - -export default trpcNext.createNextApiHandler({ - router: appRouter, - /** - * @link https://trpc.io/docs/context - */ - createContext: ({ req, res }) => { - const sessionGetter = () => getServerSession({ req, res }); - - return createTrpcContext({ req, res }, sessionGetter); - }, - /** - * @link https://trpc.io/docs/error-handling - */ - onError({ error }) { - if (error.code === "INTERNAL_SERVER_ERROR") { - // send to bug reporting - console.error("Something went wrong", error); - } - }, - /** - * Enable query batching - */ - batching: { - enabled: true, - }, - /** - * @link https://trpc.io/docs/caching#api-response-caching - */ - responseMeta({ ctx, paths, type, errors }) { - // Some helpers relevant to this function only - const ONE_DAY_IN_SECONDS = 60 * 60 * 24; - // assuming you have all your public routes with the keyword `public` in them - const allPublic = paths && paths.every((path) => path.startsWith("viewer.public.")); - // checking that no procedures errored - const allOk = errors.length === 0; - // checking we're doing a query request - const isQuery = type === "query"; - const noHeaders = {}; - - // We cannot set headers on SSG queries - if (!ctx?.res) return noHeaders; - - const defaultHeaders: Record<"headers", Record> = { - headers: {}, - }; - - const timezone = z.string().safeParse(ctx.req?.headers["x-vercel-ip-timezone"]); - if (timezone.success) defaultHeaders.headers["x-cal-timezone"] = timezone.data; - - // We need all these conditions to be true to set cache headers - if (!(allPublic && allOk && isQuery)) return defaultHeaders; - - // No cache by default - defaultHeaders.headers["cache-control"] = `no-cache`; - - // Our cache can change depending on our current paths value. Since paths is an array, - // we want to create a map that can match potential paths with their desired cache value - const cacheRules = { - "viewer.public.session": `no-cache`, - "viewer.public.i18n": `no-cache`, - // Revalidation time here should be 1 second, per https://github.com/calcom/cal.com/pull/6823#issuecomment-1423215321 - "viewer.public.slots.getSchedule": `no-cache`, // FIXME - "viewer.public.cityTimezones": `max-age=${ONE_DAY_IN_SECONDS}, stale-while-revalidate`, - } as const; - - // Find which element above is an exact match for this group of paths - const matchedPath = paths.find((v) => v in cacheRules) as keyof typeof cacheRules; - - if (matchedPath) defaultHeaders.headers["cache-control"] = cacheRules[matchedPath]; - - return defaultHeaders; - }, -}); diff --git a/apps/web/pages/api/trpc/apiKeys/[trpc].ts b/apps/web/pages/api/trpc/apiKeys/[trpc].ts new file mode 100644 index 0000000000..60b65ea0a1 --- /dev/null +++ b/apps/web/pages/api/trpc/apiKeys/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { apiKeysRouter } from "@calcom/trpc/server/routers/viewer/apiKeys/_router"; + +export default createNextApiHandler(apiKeysRouter); diff --git a/apps/web/pages/api/trpc/appRoutingForms/[trpc].ts b/apps/web/pages/api/trpc/appRoutingForms/[trpc].ts new file mode 100644 index 0000000000..63037301d8 --- /dev/null +++ b/apps/web/pages/api/trpc/appRoutingForms/[trpc].ts @@ -0,0 +1,4 @@ +import appRoutingForms from "@calcom/app-store/routing-forms/trpc-router"; +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; + +export default createNextApiHandler(appRoutingForms); diff --git a/apps/web/pages/api/trpc/apps/[trpc].ts b/apps/web/pages/api/trpc/apps/[trpc].ts new file mode 100644 index 0000000000..e7e03ae67f --- /dev/null +++ b/apps/web/pages/api/trpc/apps/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { appsRouter } from "@calcom/trpc/server/routers/viewer/apps/_router"; + +export default createNextApiHandler(appsRouter); diff --git a/apps/web/pages/api/trpc/auth/[trpc].ts b/apps/web/pages/api/trpc/auth/[trpc].ts new file mode 100644 index 0000000000..28363de1d2 --- /dev/null +++ b/apps/web/pages/api/trpc/auth/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { authRouter } from "@calcom/trpc/server/routers/viewer/auth/_router"; + +export default createNextApiHandler(authRouter); diff --git a/apps/web/pages/api/trpc/availability/[trpc].ts b/apps/web/pages/api/trpc/availability/[trpc].ts new file mode 100644 index 0000000000..b8ba4c7cb8 --- /dev/null +++ b/apps/web/pages/api/trpc/availability/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { availabilityRouter } from "@calcom/trpc/server/routers/viewer/availability/_router"; + +export default createNextApiHandler(availabilityRouter); diff --git a/apps/web/pages/api/trpc/bookings/[trpc].ts b/apps/web/pages/api/trpc/bookings/[trpc].ts new file mode 100644 index 0000000000..908a82a82c --- /dev/null +++ b/apps/web/pages/api/trpc/bookings/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { bookingsRouter } from "@calcom/trpc/server/routers/viewer/bookings/_router"; + +export default createNextApiHandler(bookingsRouter); diff --git a/apps/web/pages/api/trpc/deploymentSetup/[trpc].ts b/apps/web/pages/api/trpc/deploymentSetup/[trpc].ts new file mode 100644 index 0000000000..c7dc57b734 --- /dev/null +++ b/apps/web/pages/api/trpc/deploymentSetup/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { deploymentSetupRouter } from "@calcom/trpc/server/routers/viewer/deploymentSetup/_router"; + +export default createNextApiHandler(deploymentSetupRouter); diff --git a/apps/web/pages/api/trpc/eth/[trpc].ts b/apps/web/pages/api/trpc/eth/[trpc].ts new file mode 100644 index 0000000000..9015a59cdc --- /dev/null +++ b/apps/web/pages/api/trpc/eth/[trpc].ts @@ -0,0 +1,4 @@ +import ethRouter from "@calcom/app-store/rainbow/trpc/router"; +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; + +export default createNextApiHandler(ethRouter); diff --git a/apps/web/pages/api/trpc/eventTypes/[trpc].ts b/apps/web/pages/api/trpc/eventTypes/[trpc].ts new file mode 100644 index 0000000000..90affa56f6 --- /dev/null +++ b/apps/web/pages/api/trpc/eventTypes/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { eventTypesRouter } from "@calcom/trpc/server/routers/viewer/eventTypes/_router"; + +export default createNextApiHandler(eventTypesRouter); diff --git a/apps/web/pages/api/trpc/features/[trpc].ts b/apps/web/pages/api/trpc/features/[trpc].ts new file mode 100644 index 0000000000..314293a648 --- /dev/null +++ b/apps/web/pages/api/trpc/features/[trpc].ts @@ -0,0 +1,4 @@ +import { featureFlagRouter } from "@calcom/features/flags/server/router"; +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; + +export default createNextApiHandler(featureFlagRouter); diff --git a/apps/web/pages/api/trpc/insights/[trpc].ts b/apps/web/pages/api/trpc/insights/[trpc].ts new file mode 100644 index 0000000000..f0eb3490ac --- /dev/null +++ b/apps/web/pages/api/trpc/insights/[trpc].ts @@ -0,0 +1,4 @@ +import { insightsRouter } from "@calcom/features/insights/server/trpc-router"; +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; + +export default createNextApiHandler(insightsRouter); diff --git a/apps/web/pages/api/trpc/payments/[trpc].ts b/apps/web/pages/api/trpc/payments/[trpc].ts new file mode 100644 index 0000000000..673a2b1e57 --- /dev/null +++ b/apps/web/pages/api/trpc/payments/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { paymentsRouter } from "@calcom/trpc/server/routers/viewer/payments/_router"; + +export default createNextApiHandler(paymentsRouter); diff --git a/apps/web/pages/api/trpc/public/[trpc].ts b/apps/web/pages/api/trpc/public/[trpc].ts new file mode 100644 index 0000000000..da8470cdfc --- /dev/null +++ b/apps/web/pages/api/trpc/public/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { publicViewerRouter } from "@calcom/trpc/server/routers/publicViewer/_router"; + +export default createNextApiHandler(publicViewerRouter, true); diff --git a/apps/web/pages/api/trpc/saml/[trpc].ts b/apps/web/pages/api/trpc/saml/[trpc].ts new file mode 100644 index 0000000000..d149507757 --- /dev/null +++ b/apps/web/pages/api/trpc/saml/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { ssoRouter } from "@calcom/trpc/server/routers/viewer/sso/_router"; + +export default createNextApiHandler(ssoRouter); diff --git a/apps/web/pages/api/trpc/slots/[trpc].ts b/apps/web/pages/api/trpc/slots/[trpc].ts new file mode 100644 index 0000000000..c220bf20f7 --- /dev/null +++ b/apps/web/pages/api/trpc/slots/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { slotsRouter } from "@calcom/trpc/server/routers/viewer/slots/_router"; + +export default createNextApiHandler(slotsRouter); diff --git a/apps/web/pages/api/trpc/teams/[trpc].ts b/apps/web/pages/api/trpc/teams/[trpc].ts new file mode 100644 index 0000000000..504b2692ea --- /dev/null +++ b/apps/web/pages/api/trpc/teams/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { viewerTeamsRouter } from "@calcom/trpc/server/routers/viewer/teams/_router"; + +export default createNextApiHandler(viewerTeamsRouter); diff --git a/apps/web/pages/api/trpc/users/[trpc].ts b/apps/web/pages/api/trpc/users/[trpc].ts new file mode 100644 index 0000000000..9829c4a3bb --- /dev/null +++ b/apps/web/pages/api/trpc/users/[trpc].ts @@ -0,0 +1,4 @@ +import { userAdminRouter } from "@calcom/features/ee/users/server/trpc-router"; +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; + +export default createNextApiHandler(userAdminRouter); diff --git a/apps/web/pages/api/trpc/viewer/[trpc].ts b/apps/web/pages/api/trpc/viewer/[trpc].ts new file mode 100644 index 0000000000..2e44c1f966 --- /dev/null +++ b/apps/web/pages/api/trpc/viewer/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { loggedInViewerRouter } from "@calcom/trpc/server/routers/loggedInViewer/_router"; + +export default createNextApiHandler(loggedInViewerRouter); diff --git a/apps/web/pages/api/trpc/webhook/[trpc].ts b/apps/web/pages/api/trpc/webhook/[trpc].ts new file mode 100644 index 0000000000..c19093f285 --- /dev/null +++ b/apps/web/pages/api/trpc/webhook/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { webhookRouter } from "@calcom/trpc/server/routers/viewer/webhook/_router"; + +export default createNextApiHandler(webhookRouter); diff --git a/apps/web/pages/api/trpc/workflows/[trpc].ts b/apps/web/pages/api/trpc/workflows/[trpc].ts new file mode 100644 index 0000000000..b086ebab6f --- /dev/null +++ b/apps/web/pages/api/trpc/workflows/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { workflowsRouter } from "@calcom/trpc/server/routers/viewer/workflows/_router"; + +export default createNextApiHandler(workflowsRouter); diff --git a/apps/web/playwright/availability.e2e.ts b/apps/web/playwright/availability.e2e.ts index abee0f3a49..723da4c766 100644 --- a/apps/web/playwright/availability.e2e.ts +++ b/apps/web/playwright/availability.e2e.ts @@ -33,7 +33,7 @@ test.describe("Availablity tests", () => { }); await test.step("Date override is displayed in troubleshooter", async () => { - const response = await page.waitForResponse("**/api/trpc/viewer.availability.schedule.update?batch=1"); + const response = await page.waitForResponse("**/api/trpc/availability/schedule.update?batch=1"); const json = await response.json(); // @ts-expect-error trust me bro const date = json[0].result.data.json.schedule.availability.find((a) => !!a.date); diff --git a/apps/web/playwright/booking-pages.e2e.ts b/apps/web/playwright/booking-pages.e2e.ts index 59a811e7a6..d5ef9c0ef3 100644 --- a/apps/web/playwright/booking-pages.e2e.ts +++ b/apps/web/playwright/booking-pages.e2e.ts @@ -131,7 +131,7 @@ testBothBookers.describe("pro user", () => { await page.goto("/bookings/unconfirmed"); await Promise.all([ page.click('[data-testid="confirm"]'), - page.waitForResponse((response) => response.url().includes("/api/trpc/viewer.bookings.confirm")), + page.waitForResponse((response) => response.url().includes("/api/trpc/bookings/confirm")), ]); // This is the only booking in there that needed confirmation and now it should be empty screen await expect(page.locator('[data-testid="empty-screen"]')).toBeVisible(); diff --git a/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts b/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts index 3e47c36c98..6d2d4924b1 100644 --- a/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts +++ b/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts @@ -179,7 +179,7 @@ test.describe("Routing Forms", () => { await page.goto(`/apps/routing-forms/reporting/${routingForm.id}`); // Can't keep waiting forever. So, added a timeout of 5000ms - await page.waitForResponse((response) => response.url().includes("viewer.appRoutingForms.report"), { + await page.waitForResponse((response) => response.url().includes("appRoutingForms/report"), { timeout: 5000, }); const headerEls = page.locator("[data-testid='reporting-header'] th"); diff --git a/packages/trpc/react/trpc.ts b/packages/trpc/react/trpc.ts index ea3b579f09..aa1d08b191 100644 --- a/packages/trpc/react/trpc.ts +++ b/packages/trpc/react/trpc.ts @@ -12,6 +12,58 @@ import type { TRPCClientErrorLike } from "../react"; import type { inferRouterInputs, inferRouterOutputs, Maybe } from "../server"; import type { AppRouter } from "../server/routers/_app"; +/** + * We deploy our tRPC router on multiple lambdas to keep number of imports as small as possible + * TODO: Make this dynamic based on folders in trpc server? + */ +const ENDPOINTS = [ + "apiKeys", + "appRoutingForms", + "apps", + "auth", + "availability", + "bookings", + "deploymentSetup", + "eth", + "eventTypes", + "features", + "insights", + "payments", + "public", + "saml", + "slots", + "teams", + "users", + "viewer", + "webhook", + "workflows", +] as const; +export type Endpoint = (typeof ENDPOINTS)[number]; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const resolveEndpoint = (links: any) => { + // TODO: Update our trpc routes so they are more clear. + // This function parses paths like the following and maps them + // to the correct API endpoints. + // - viewer.me - 2 segment paths like this are for logged in requests + // - viewer.public.i18n - 3 segments paths can be public or authed + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (ctx: any) => { + const parts = ctx.op.path.split("."); + let endpoint; + let path = ''; + if (parts.length == 2) { + endpoint = parts[0] as keyof typeof links; + path = parts[1]; + } else { + endpoint = parts[1] as keyof typeof links; + path = parts.splice(2, parts.length - 2).join('.'); + } + + return links[endpoint]({ ...ctx, op: { ...ctx.op, path } }); + }; +}; + /** * A set of strongly-typed React hooks from your `AppRouter` type signature with `createTRPCReact`. * @link https://trpc.io/docs/v10/react#2-create-trpc-hooks @@ -41,17 +93,21 @@ export const trpc = createTRPCNext { - return op.context.skipBatch === true; - }, + condition: (op) => !!op.context.skipBatch, // when condition is true, use normal request - true: httpLink({ url }), - // when condition is false, use batching - false: httpBatchLink({ - url, - /** @link https://github.com/trpc/trpc/issues/2008 */ - // maxBatchSize: 7 - }), + true: (runtime) => { + const links = Object.fromEntries( + ENDPOINTS.map((endpoint) => [endpoint, httpLink({ url: url + "/" + endpoint })(runtime)]) + ); + return resolveEndpoint(links); + }, + // when condition is false, use batch request + false: (runtime) => { + const links = Object.fromEntries( + ENDPOINTS.map((endpoint) => [endpoint, httpBatchLink({ url: url + "/" + endpoint })(runtime)]) + ); + return resolveEndpoint(links); + }, }), ], /** diff --git a/packages/trpc/server/createNextApiHandler.ts b/packages/trpc/server/createNextApiHandler.ts new file mode 100644 index 0000000000..05ac119017 --- /dev/null +++ b/packages/trpc/server/createNextApiHandler.ts @@ -0,0 +1,74 @@ +import { z } from "zod"; +import type { AnyRouter } from "@trpc/server"; +import * as trpcNext from "@calcom/trpc/server/adapters/next"; +import { createContext as createTrpcContext } from "@calcom/trpc/server/createContext"; + +/** + * Creates an API handler executed by Next.js. + */ +export function createNextApiHandler(router: AnyRouter, isPublic: boolean = false) { + return trpcNext.createNextApiHandler({ + router, + /** + * @link https://trpc.io/docs/context + */ + createContext: ({ req, res }) => { + return createTrpcContext({ req, res }); + }, + /** + * @link https://trpc.io/docs/error-handling + */ + onError({ error }) { + if (error.code === "INTERNAL_SERVER_ERROR") { + // send to bug reporting + console.error("Something went wrong", error); + } + }, + /** + * Enable query batching + */ + batching: { + enabled: true, + }, + /** + * @link https://trpc.io/docs/caching#api-response-caching + */ + responseMeta({ ctx, paths, type, errors }) { + const allOk = errors.length === 0; + const isQuery = type === "query"; + const noHeaders = {}; + + // We cannot set headers on SSG queries + if (!ctx?.res) return noHeaders; + + const defaultHeaders: Record<"headers", Record> = { + headers: {}, + }; + + const timezone = z.string().safeParse(ctx.req?.headers["x-vercel-ip-timezone"]); + if (timezone.success) defaultHeaders.headers["x-cal-timezone"] = timezone.data; + + // We need all these conditions to be true to set cache headers + if (!(isPublic && allOk && isQuery)) return defaultHeaders; + + // No cache by default + defaultHeaders.headers["cache-control"] = `no-cache`; + + if (isPublic && paths) { + const ONE_DAY_IN_SECONDS = 60 * 60 * 24; + const cacheRules = { + "session": `no-cache`, + "i18n": `no-cache`, + // Revalidation time here should be 1 second, per https://github.com/calcom/cal.com/pull/6823#issuecomment-1423215321 + "slots.getSchedule": `no-cache`, // FIXME + "cityTimezones": `max-age=${ONE_DAY_IN_SECONDS}, stale-while-revalidate`, + } as const; + + const matchedPath = paths.find((v) => v in cacheRules) as keyof typeof cacheRules; + if (matchedPath) defaultHeaders.headers["cache-control"] = cacheRules[matchedPath]; + } + + return defaultHeaders; + }, + }); +}; diff --git a/playwright.config.ts b/playwright.config.ts index 77f818ad84..3332efae4b 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -102,11 +102,17 @@ const config: PlaywrightTestConfig = { name: "@calcom/embed-core", testDir: "./packages/embeds/embed-core/", testMatch: /.*\.(e2e|test)\.tsx?/, + expect: { + timeout: DEFAULT_EXPECT_TIMEOUT, + }, use: { ...devices["Desktop Chrome"], baseURL: "http://localhost:3100/" }, }, { name: "@calcom/embed-react", testDir: "./packages/embeds/embed-react/", + expect: { + timeout: DEFAULT_EXPECT_TIMEOUT, + }, testMatch: /.*\.(e2e|test)\.tsx?/, use: { ...devices["Desktop Chrome"], baseURL: "http://localhost:3101/" }, }, @@ -114,12 +120,18 @@ const config: PlaywrightTestConfig = { name: "@calcom/embed-core--firefox", testDir: "./packages/embeds/", testMatch: /.*\.e2e\.tsx?/, + expect: { + timeout: DEFAULT_EXPECT_TIMEOUT, + }, use: { ...devices["Desktop Firefox"] }, }, { name: "@calcom/embed-core--webkit", testDir: "./packages/embeds/", testMatch: /.*\.e2e\.tsx?/, + expect: { + timeout: DEFAULT_EXPECT_TIMEOUT, + }, use: { ...devices["Desktop Safari"] }, }, ],