refactor: split up routers to separate lambdas (#8041)
* 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 <roae.85@gmail.com> Co-authored-by: Alex van Andel <me@alexvanandel.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: Keith Williams <keithwillcode@gmail.com> Co-authored-by: zomars <zomars@me.com> Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
This commit is contained in:
parent
98a3508b8f
commit
cdba1920fc
|
@ -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<string, string>> = {
|
||||
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;
|
||||
},
|
||||
});
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -0,0 +1,4 @@
|
|||
import ethRouter from "@calcom/app-store/rainbow/trpc/router";
|
||||
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
|
||||
|
||||
export default createNextApiHandler(ethRouter);
|
|
@ -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);
|
|
@ -0,0 +1,4 @@
|
|||
import { featureFlagRouter } from "@calcom/features/flags/server/router";
|
||||
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
|
||||
|
||||
export default createNextApiHandler(featureFlagRouter);
|
|
@ -0,0 +1,4 @@
|
|||
import { insightsRouter } from "@calcom/features/insights/server/trpc-router";
|
||||
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
|
||||
|
||||
export default createNextApiHandler(insightsRouter);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -0,0 +1,4 @@
|
|||
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
|
||||
import { loggedInViewerRouter } from "@calcom/trpc/server/routers/loggedInViewer/_router";
|
||||
|
||||
export default createNextApiHandler(loggedInViewerRouter);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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<AppRouter, NextPageContext, "ExperimentalSusp
|
|||
}),
|
||||
splitLink({
|
||||
// check for context property `skipBatch`
|
||||
condition: (op) => {
|
||||
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);
|
||||
},
|
||||
}),
|
||||
],
|
||||
/**
|
||||
|
|
|
@ -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<string, string>> = {
|
||||
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;
|
||||
},
|
||||
});
|
||||
};
|
|
@ -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"] },
|
||||
},
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue
Block a user