chore: implement a/b test middleware

pull/12045/head
Dmytro Hryshyn 2023-10-23 19:45:42 +03:00
parent 46fc67f70d
commit 845605920f
4 changed files with 58 additions and 1 deletions

View File

@ -0,0 +1,45 @@
import { getBucket } from "abTest/utils";
import type { NextFetchEvent, NextMiddleware, NextRequest } from "next/server";
import { NextResponse } from "next/server";
import z from "zod";
const ROUTE_MAP = new Map<string, boolean>([
["/event-types", Boolean(process.env.APP_ROUTER_EVENT_TYPES_ENABLED)] as const,
]);
const FUTURE_ROUTES_OVERRIDE_COOKIE_NAME = "x-calcom-future-routes-override";
const FUTURE_ROUTES_ENABLED_COOKIE_NAME = "x-calcom-future-routes-enabled";
const bucketSchema = z.union([z.literal("legacy"), z.literal("future")]).default("legacy");
export const abTestMiddlewareFactory =
(next: NextMiddleware): NextMiddleware =>
async (req: NextRequest, event: NextFetchEvent) => {
const { pathname } = req.nextUrl;
const override = req.cookies.has(FUTURE_ROUTES_OVERRIDE_COOKIE_NAME);
const enabled = ROUTE_MAP.has(pathname) ? (ROUTE_MAP.get(pathname) ?? false) || override : false;
if (pathname.includes("future") || !enabled) {
return next(req, event);
}
const safeParsedBucket = override
? { success: true as const, data: "future" as const }
: bucketSchema.safeParse(req.cookies.get(FUTURE_ROUTES_ENABLED_COOKIE_NAME)?.value);
if (!safeParsedBucket.success) {
// cookie does not exist or it has incorrect value
const res = NextResponse.next();
res.cookies.set(FUTURE_ROUTES_ENABLED_COOKIE_NAME, getBucket(), { expires: 1000 * 60 * 30 }); // 30 min in ms
return res;
}
const bucketUrlPrefix = safeParsedBucket.data === "future" ? "future" : "";
const url = req.nextUrl.clone();
url.pathname = `${bucketUrlPrefix}${pathname}/`;
return NextResponse.rewrite(url);
};

9
apps/web/abTest/utils.ts Normal file
View File

@ -0,0 +1,9 @@
import { AB_TEST_BUCKET_PROBABILITY } from "@calcom/lib/constants";
const cryptoRandom = () => {
return crypto.getRandomValues(new Uint8Array(1))[0] / 0xff;
};
export const getBucket = () => {
return cryptoRandom() * 100 < AB_TEST_BUCKET_PROBABILITY ? "future" : "legacy";
};

View File

@ -106,3 +106,4 @@ export const APP_CREDENTIAL_SHARING_ENABLED =
export const DEFAULT_LIGHT_BRAND_COLOR = "#292929";
export const DEFAULT_DARK_BRAND_COLOR = "#fafafa";
export const AB_TEST_BUCKET_PROBABILITY = Number(process.env.AB_TEST_BUCKET_PROBABILITY) ?? 10;

View File

@ -324,6 +324,8 @@
"ZOHOCRM_CLIENT_ID",
"ZOHOCRM_CLIENT_SECRET",
"ZOOM_CLIENT_ID",
"ZOOM_CLIENT_SECRET"
"ZOOM_CLIENT_SECRET",
"AB_TEST_BUCKET_PROBABILITY",
"APP_ROUTER_EVENT_TYPES_ENABLED"
]
}