feat: routing forms for api v2 (#18885)
* init router endpoint for v2 * fixup fixup * resolve merge conflicts * update libraries * update router controller * make router endpoint public * chore: refactor router endpoint and add team/org id * cleanup * chore: Only import Sentry when needed (#18882) * perf: prerender routes in main navigation (#18883) * chore: migrate /apps/categories pages (#18871) * migrate apps/categories pages * remove pages dir * chore: app router - /payment pages (#18152) * remove Head from payment page comp * remove route from pages router * move out of future folder * add to config.matcher * remove unneeded code * update middleware * fix * fix * perf: replace `getServerSessionForAppDir` usages with our original `getServerSession` function (#18897) * chore: migrate /apps/installation pages (#18870) * migrate apps/installation pages * fix test * chore: migrate `/auth/logout` (#18901) * migrate /auth/verify-email page * update middleware * migrate /auth/logout page * expose router endpoint * small refactor pageprops not necessary * sentry is already tracking performance * reuse logic * fixup! reuse logic * fixup! fixup! reuse logic * fixup! fixup! fixup! reuse logic --------- Co-authored-by: Morgan Vernay <morgan@cal.com> Co-authored-by: Alex van Andel <me@alexvanandel.com> Co-authored-by: Benny Joo <sldisek783@gmail.com> Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
This commit is contained in:
9 files changed
+271
-197
No files matched your search
@@ -29,7 +29,7 @@
|
||||
"@axiomhq/winston": "^1.2.0",
|
||||
"@calcom/platform-constants": "*",
|
||||
"@calcom/platform-enums": "*",
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.91",
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.92",
|
||||
"@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2",
|
||||
"@calcom/platform-types": "*",
|
||||
"@calcom/platform-utils": "*",
|
||||
|
||||
@@ -4,6 +4,7 @@ import { BillingModule } from "@/modules/billing/billing.module";
|
||||
import { ConferencingModule } from "@/modules/conferencing/conferencing.module";
|
||||
import { DestinationCalendarsModule } from "@/modules/destination-calendars/destination-calendars.module";
|
||||
import { OAuthClientModule } from "@/modules/oauth-clients/oauth-client.module";
|
||||
import { RouterModule } from "@/modules/router/router.module";
|
||||
import { StripeModule } from "@/modules/stripe/stripe.module";
|
||||
import { TimezoneModule } from "@/modules/timezones/timezones.module";
|
||||
import type { MiddlewareConsumer, NestModule } from "@nestjs/common";
|
||||
@@ -24,6 +25,7 @@ import { WebhooksModule } from "./webhooks/webhooks.module";
|
||||
AtomsModule,
|
||||
StripeModule,
|
||||
ConferencingModule,
|
||||
RouterModule,
|
||||
],
|
||||
})
|
||||
export class EndpointsModule implements NestModule {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
|
||||
import { Controller, Get, Req, NotFoundException, Res, Query, Param } from "@nestjs/common";
|
||||
import { ApiTags as DocsTags, ApiExcludeController as DocsExcludeController } from "@nestjs/swagger";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
import { getRoutedUrl } from "@calcom/platform-libraries";
|
||||
import { ApiResponse } from "@calcom/platform-types";
|
||||
|
||||
@Controller({
|
||||
path: "/v2/router",
|
||||
version: API_VERSIONS_VALUES,
|
||||
})
|
||||
@DocsTags("Router controller")
|
||||
@DocsExcludeController(true)
|
||||
export class RouterController {
|
||||
@Get("/forms/:formId")
|
||||
async getRoutingFormResponse(
|
||||
@Req() request: Request,
|
||||
@Res() res: Response,
|
||||
@Param("formId") formId: string,
|
||||
@Query() query: Record<string, string>
|
||||
): Promise<void | ApiResponse<unknown>> {
|
||||
const routedUrlData = await getRoutedUrl({ req: request, query: { ...query, form: formId } });
|
||||
|
||||
if (routedUrlData?.notFound) {
|
||||
throw new NotFoundException("Route not found. Please check the provided form parameter.");
|
||||
}
|
||||
|
||||
if (routedUrlData?.redirect?.destination) {
|
||||
return res.redirect(307, routedUrlData.redirect.destination);
|
||||
}
|
||||
|
||||
if (routedUrlData?.props) {
|
||||
return { status: "success", data: routedUrlData.props };
|
||||
}
|
||||
|
||||
return { status: "success", data: "route nor custom message found" };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||
import { RouterController } from "@/modules/router/controllers/router.controller";
|
||||
import { Module } from "@nestjs/common";
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
providers: [],
|
||||
exports: [],
|
||||
controllers: [RouterController],
|
||||
})
|
||||
export class RouterModule {}
|
||||
@@ -1,200 +1,7 @@
|
||||
import { stringify } from "querystring";
|
||||
import z from "zod";
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
import { enrichFormWithMigrationData } from "@calcom/app-store/routing-forms/enrichFormWithMigrationData";
|
||||
import { getAbsoluteEventTypeRedirectUrlWithEmbedSupport } from "@calcom/app-store/routing-forms/getEventTypeRedirectUrl";
|
||||
import getFieldIdentifier from "@calcom/app-store/routing-forms/lib/getFieldIdentifier";
|
||||
import { getSerializableForm } from "@calcom/app-store/routing-forms/lib/getSerializableForm";
|
||||
import { getServerTimingHeader } from "@calcom/app-store/routing-forms/lib/getServerTimingHeader";
|
||||
import { handleResponse } from "@calcom/app-store/routing-forms/lib/handleResponse";
|
||||
import { findMatchingRoute } from "@calcom/app-store/routing-forms/lib/processRoute";
|
||||
import { substituteVariables } from "@calcom/app-store/routing-forms/lib/substituteVariables";
|
||||
import { getFieldResponseForJsonLogic } from "@calcom/app-store/routing-forms/lib/transformResponse";
|
||||
import { getUrlSearchParamsToForward } from "@calcom/app-store/routing-forms/pages/routing-link/getUrlSearchParamsToForward";
|
||||
import type { FormResponse } from "@calcom/app-store/routing-forms/types/types";
|
||||
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { isAuthorizedToViewFormOnOrgDomain } from "@calcom/features/routing-forms/lib/isAuthorizedToViewForm";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { RoutingFormRepository } from "@calcom/lib/server/repository/routingForm";
|
||||
import { TRPCError } from "@calcom/trpc/server";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
|
||||
const log = logger.getSubLogger({ prefix: ["[routing-forms]", "[router]"] });
|
||||
const querySchema = z
|
||||
.object({
|
||||
form: z.string(),
|
||||
})
|
||||
.catchall(z.string().or(z.array(z.string())));
|
||||
|
||||
function hasEmbedPath(pathWithQuery: string) {
|
||||
const onlyPath = pathWithQuery.split("?")[0];
|
||||
return onlyPath.endsWith("/embed") || onlyPath.endsWith("/embed/");
|
||||
}
|
||||
import { getRoutedUrl } from "@calcom/lib/server";
|
||||
|
||||
export const getServerSideProps = async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
const queryParsed = querySchema.safeParse(context.query);
|
||||
const isEmbed = hasEmbedPath(context.req.url || "");
|
||||
const pageProps = {
|
||||
isEmbed,
|
||||
};
|
||||
|
||||
if (!queryParsed.success) {
|
||||
log.warn("Error parsing query", queryParsed.error);
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Known params reserved by Cal.com are form, embed, layout and other cal. prefixed params. We should exclude all of them from fieldsResponses.
|
||||
// But they must be present in `paramsToBeForwardedAsIs` as they could be needed by Booking Page as well.
|
||||
const { form: formId, "cal.isBookingDryRun": isBookingDryRunParam, ...fieldsResponses } = queryParsed.data;
|
||||
const isBookingDryRun = isBookingDryRunParam === "true";
|
||||
const paramsToBeForwardedAsIs = {
|
||||
...fieldsResponses,
|
||||
// Must be forwarded if present to Booking Page. Setting it explicitly here as it is critical to be present in the URL.
|
||||
...(isBookingDryRunParam ? { "cal.isBookingDryRun": isBookingDryRunParam } : null),
|
||||
};
|
||||
|
||||
const { currentOrgDomain } = orgDomainConfig(context.req);
|
||||
|
||||
let timeTaken: Record<string, number | null> = {};
|
||||
|
||||
const formQueryStart = performance.now();
|
||||
const form = await RoutingFormRepository.findFormByIdIncludeUserTeamAndOrg(formId);
|
||||
timeTaken.formQuery = performance.now() - formQueryStart;
|
||||
|
||||
if (!form) {
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
const { UserRepository } = await import("@calcom/lib/server/repository/user");
|
||||
const profileEnrichmentStart = performance.now();
|
||||
const formWithUserProfile = {
|
||||
...form,
|
||||
user: await UserRepository.enrichUserWithItsProfile({ user: form.user }),
|
||||
};
|
||||
timeTaken.profileEnrichment = performance.now() - profileEnrichmentStart;
|
||||
|
||||
if (
|
||||
!isAuthorizedToViewFormOnOrgDomain({ user: formWithUserProfile.user, currentOrgDomain, team: form.team })
|
||||
) {
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
const getSerializableFormStart = performance.now();
|
||||
const serializableForm = await getSerializableForm({
|
||||
form: enrichFormWithMigrationData(formWithUserProfile),
|
||||
});
|
||||
timeTaken.getSerializableForm = performance.now() - getSerializableFormStart;
|
||||
|
||||
const response: FormResponse = {};
|
||||
if (!serializableForm.fields) {
|
||||
throw new Error("Form has no fields");
|
||||
}
|
||||
serializableForm.fields.forEach((field) => {
|
||||
const fieldResponse = fieldsResponses[getFieldIdentifier(field)] || "";
|
||||
|
||||
response[field.id] = {
|
||||
label: field.label,
|
||||
value: getFieldResponseForJsonLogic({ field, value: fieldResponse }),
|
||||
};
|
||||
});
|
||||
|
||||
const matchingRoute = findMatchingRoute({ form: serializableForm, response });
|
||||
|
||||
if (!matchingRoute) {
|
||||
throw new Error("No matching route could be found");
|
||||
}
|
||||
|
||||
const decidedAction = matchingRoute.action;
|
||||
|
||||
const { v4: uuidv4 } = await import("uuid");
|
||||
let teamMembersMatchingAttributeLogic = null;
|
||||
let formResponseId = null;
|
||||
let attributeRoutingConfig = null;
|
||||
try {
|
||||
const result = await handleResponse({
|
||||
form: serializableForm,
|
||||
formFillerId: uuidv4(),
|
||||
response: response,
|
||||
chosenRouteId: matchingRoute.id,
|
||||
isPreview: isBookingDryRun,
|
||||
});
|
||||
teamMembersMatchingAttributeLogic = result.teamMembersMatchingAttributeLogic;
|
||||
formResponseId = result.formResponse.id;
|
||||
attributeRoutingConfig = result.attributeRoutingConfig;
|
||||
timeTaken = {
|
||||
...timeTaken,
|
||||
...result.timeTaken,
|
||||
};
|
||||
} catch (e) {
|
||||
if (e instanceof TRPCError) {
|
||||
return {
|
||||
props: {
|
||||
...pageProps,
|
||||
form: serializableForm,
|
||||
message: e.message,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: To be done using sentry tracing
|
||||
console.log("Server-Timing", getServerTimingHeader(timeTaken));
|
||||
|
||||
//TODO: Maybe take action after successful mutation
|
||||
if (decidedAction.type === "customPageMessage") {
|
||||
return {
|
||||
props: {
|
||||
...pageProps,
|
||||
form: serializableForm,
|
||||
message: decidedAction.value,
|
||||
},
|
||||
};
|
||||
} else if (decidedAction.type === "eventTypeRedirectUrl") {
|
||||
const eventTypeUrlWithResolvedVariables = substituteVariables(
|
||||
decidedAction.value,
|
||||
response,
|
||||
serializableForm.fields
|
||||
);
|
||||
|
||||
return {
|
||||
redirect: {
|
||||
destination: getAbsoluteEventTypeRedirectUrlWithEmbedSupport({
|
||||
eventTypeRedirectUrl: eventTypeUrlWithResolvedVariables,
|
||||
form: serializableForm,
|
||||
allURLSearchParams: getUrlSearchParamsToForward({
|
||||
formResponse: response,
|
||||
fields: serializableForm.fields,
|
||||
searchParams: new URLSearchParams(stringify(paramsToBeForwardedAsIs)),
|
||||
teamMembersMatchingAttributeLogic,
|
||||
// formResponseId is guaranteed to be set because in catch block of trpc request we return from the function and otherwise it would have been set
|
||||
formResponseId: formResponseId!,
|
||||
attributeRoutingConfig: attributeRoutingConfig ?? null,
|
||||
}),
|
||||
isEmbed: pageProps.isEmbed,
|
||||
}),
|
||||
permanent: false,
|
||||
},
|
||||
};
|
||||
} else if (decidedAction.type === "externalRedirectUrl") {
|
||||
return {
|
||||
redirect: {
|
||||
destination: `${decidedAction.value}?${stringify(context.query)}`,
|
||||
permanent: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Consider throwing error here as there is no value of decidedAction.type that would cause the flow to be here
|
||||
return {
|
||||
props: {
|
||||
...pageProps,
|
||||
form: serializableForm,
|
||||
message: "Unhandled type of action",
|
||||
},
|
||||
};
|
||||
return getRoutedUrl(context);
|
||||
};
|
||||
@@ -24,6 +24,8 @@ type GetUrlSearchParamsToForwardOptions = {
|
||||
teamMembersMatchingAttributeLogic: number[] | null;
|
||||
attributeRoutingConfig: AttributeRoutingConfig | null;
|
||||
reroutingFormResponses?: FormResponseValueOnly;
|
||||
teamId?: number | null;
|
||||
orgId?: number | null;
|
||||
};
|
||||
|
||||
export function getUrlSearchParamsToForward({
|
||||
@@ -34,6 +36,8 @@ export function getUrlSearchParamsToForward({
|
||||
formResponseId,
|
||||
attributeRoutingConfig,
|
||||
reroutingFormResponses,
|
||||
teamId,
|
||||
orgId,
|
||||
}: GetUrlSearchParamsToForwardOptions) {
|
||||
type Params = Record<string, string | string[]>;
|
||||
const paramsFromResponse: Params = {};
|
||||
@@ -101,6 +105,8 @@ export function getUrlSearchParamsToForward({
|
||||
}
|
||||
|
||||
const allQueryParams: Params = {
|
||||
...(teamId && { ["cal.teamId"]: `${teamId}` }),
|
||||
...(orgId && { ["cal.orgId"]: `${orgId}` }),
|
||||
...paramsFromCurrentUrl,
|
||||
// In case of conflict b/w paramsFromResponse and paramsFromCurrentUrl, paramsFromResponse should win as the booker probably improved upon the prefilled value.
|
||||
...paramsFromResponse,
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
// !IMPORTANT! changes to this file requires publishing new version of platform libraries in order for the changes to be applied to APIV2
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import { stringify } from "querystring";
|
||||
import z from "zod";
|
||||
|
||||
import { enrichFormWithMigrationData } from "@calcom/app-store/routing-forms/enrichFormWithMigrationData";
|
||||
import { getAbsoluteEventTypeRedirectUrlWithEmbedSupport } from "@calcom/app-store/routing-forms/getEventTypeRedirectUrl";
|
||||
import getFieldIdentifier from "@calcom/app-store/routing-forms/lib/getFieldIdentifier";
|
||||
import { getSerializableForm } from "@calcom/app-store/routing-forms/lib/getSerializableForm";
|
||||
import { getServerTimingHeader } from "@calcom/app-store/routing-forms/lib/getServerTimingHeader";
|
||||
import { handleResponse } from "@calcom/app-store/routing-forms/lib/handleResponse";
|
||||
import { findMatchingRoute } from "@calcom/app-store/routing-forms/lib/processRoute";
|
||||
import { substituteVariables } from "@calcom/app-store/routing-forms/lib/substituteVariables";
|
||||
import { getFieldResponseForJsonLogic } from "@calcom/app-store/routing-forms/lib/transformResponse";
|
||||
import { getUrlSearchParamsToForward } from "@calcom/app-store/routing-forms/pages/routing-link/getUrlSearchParamsToForward";
|
||||
import type { FormResponse } from "@calcom/app-store/routing-forms/types/types";
|
||||
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { isAuthorizedToViewFormOnOrgDomain } from "@calcom/features/routing-forms/lib/isAuthorizedToViewForm";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { RoutingFormRepository } from "@calcom/lib/server/repository/routingForm";
|
||||
import { TRPCError } from "@calcom/trpc/server";
|
||||
|
||||
const log = logger.getSubLogger({ prefix: ["[routing-forms]", "[router]"] });
|
||||
const querySchema = z
|
||||
.object({
|
||||
form: z.string(),
|
||||
})
|
||||
.catchall(z.string().or(z.array(z.string())));
|
||||
|
||||
function hasEmbedPath(pathWithQuery: string) {
|
||||
const onlyPath = pathWithQuery.split("?")[0];
|
||||
return onlyPath.endsWith("/embed") || onlyPath.endsWith("/embed/");
|
||||
}
|
||||
|
||||
export const getRoutedUrl = async (context: Pick<GetServerSidePropsContext, "query" | "req">) => {
|
||||
const queryParsed = querySchema.safeParse(context.query);
|
||||
const isEmbed = hasEmbedPath(context.req.url || "");
|
||||
const pageProps = {
|
||||
isEmbed,
|
||||
};
|
||||
|
||||
if (!queryParsed.success) {
|
||||
log.warn("Error parsing query", queryParsed.error);
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Known params reserved by Cal.com are form, embed, layout and other cal. prefixed params. We should exclude all of them from fieldsResponses.
|
||||
// But they must be present in `paramsToBeForwardedAsIs` as they could be needed by Booking Page as well.
|
||||
const { form: formId, "cal.isBookingDryRun": isBookingDryRunParam, ...fieldsResponses } = queryParsed.data;
|
||||
const isBookingDryRun = isBookingDryRunParam === "true";
|
||||
const paramsToBeForwardedAsIs = {
|
||||
...fieldsResponses,
|
||||
// Must be forwarded if present to Booking Page. Setting it explicitly here as it is critical to be present in the URL.
|
||||
...(isBookingDryRunParam ? { "cal.isBookingDryRun": isBookingDryRunParam } : null),
|
||||
};
|
||||
|
||||
const { currentOrgDomain } = orgDomainConfig(context.req);
|
||||
|
||||
let timeTaken: Record<string, number | null> = {};
|
||||
|
||||
const formQueryStart = performance.now();
|
||||
const form = await RoutingFormRepository.findFormByIdIncludeUserTeamAndOrg(formId);
|
||||
timeTaken.formQuery = performance.now() - formQueryStart;
|
||||
|
||||
if (!form) {
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
const { UserRepository } = await import("@calcom/lib/server/repository/user");
|
||||
const profileEnrichmentStart = performance.now();
|
||||
const formWithUserProfile = {
|
||||
...form,
|
||||
user: await UserRepository.enrichUserWithItsProfile({ user: form.user }),
|
||||
};
|
||||
timeTaken.profileEnrichment = performance.now() - profileEnrichmentStart;
|
||||
|
||||
if (
|
||||
!isAuthorizedToViewFormOnOrgDomain({ user: formWithUserProfile.user, currentOrgDomain, team: form.team })
|
||||
) {
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
const getSerializableFormStart = performance.now();
|
||||
const serializableForm = await getSerializableForm({
|
||||
form: enrichFormWithMigrationData(formWithUserProfile),
|
||||
});
|
||||
timeTaken.getSerializableForm = performance.now() - getSerializableFormStart;
|
||||
|
||||
const response: FormResponse = {};
|
||||
if (!serializableForm.fields) {
|
||||
throw new Error("Form has no fields");
|
||||
}
|
||||
serializableForm.fields.forEach((field) => {
|
||||
const fieldResponse = fieldsResponses[getFieldIdentifier(field)] || "";
|
||||
|
||||
response[field.id] = {
|
||||
label: field.label,
|
||||
value: getFieldResponseForJsonLogic({ field, value: fieldResponse }),
|
||||
};
|
||||
});
|
||||
|
||||
const matchingRoute = findMatchingRoute({ form: serializableForm, response });
|
||||
|
||||
if (!matchingRoute) {
|
||||
throw new Error("No matching route could be found");
|
||||
}
|
||||
|
||||
const decidedAction = matchingRoute.action;
|
||||
|
||||
const { v4: uuidv4 } = await import("uuid");
|
||||
let teamMembersMatchingAttributeLogic = null;
|
||||
let formResponseId = null;
|
||||
let attributeRoutingConfig = null;
|
||||
try {
|
||||
const result = await handleResponse({
|
||||
form: serializableForm,
|
||||
formFillerId: uuidv4(),
|
||||
response: response,
|
||||
chosenRouteId: matchingRoute.id,
|
||||
isPreview: isBookingDryRun,
|
||||
});
|
||||
teamMembersMatchingAttributeLogic = result.teamMembersMatchingAttributeLogic;
|
||||
formResponseId = result.formResponse.id;
|
||||
attributeRoutingConfig = result.attributeRoutingConfig;
|
||||
timeTaken = {
|
||||
...timeTaken,
|
||||
...result.timeTaken,
|
||||
};
|
||||
} catch (e) {
|
||||
if (e instanceof TRPCError) {
|
||||
return {
|
||||
props: {
|
||||
...pageProps,
|
||||
form: serializableForm,
|
||||
message: e.message,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: To be done using sentry tracing
|
||||
console.log("Server-Timing", getServerTimingHeader(timeTaken));
|
||||
|
||||
//TODO: Maybe take action after successful mutation
|
||||
if (decidedAction.type === "customPageMessage") {
|
||||
return {
|
||||
props: {
|
||||
...pageProps,
|
||||
form: serializableForm,
|
||||
message: decidedAction.value,
|
||||
},
|
||||
};
|
||||
} else if (decidedAction.type === "eventTypeRedirectUrl") {
|
||||
const eventTypeUrlWithResolvedVariables = substituteVariables(
|
||||
decidedAction.value,
|
||||
response,
|
||||
serializableForm.fields
|
||||
);
|
||||
|
||||
return {
|
||||
redirect: {
|
||||
destination: getAbsoluteEventTypeRedirectUrlWithEmbedSupport({
|
||||
eventTypeRedirectUrl: eventTypeUrlWithResolvedVariables,
|
||||
form: serializableForm,
|
||||
allURLSearchParams: getUrlSearchParamsToForward({
|
||||
formResponse: response,
|
||||
fields: serializableForm.fields,
|
||||
searchParams: new URLSearchParams(stringify(paramsToBeForwardedAsIs)),
|
||||
teamMembersMatchingAttributeLogic,
|
||||
// formResponseId is guaranteed to be set because in catch block of trpc request we return from the function and otherwise it would have been set
|
||||
formResponseId: formResponseId!,
|
||||
attributeRoutingConfig: attributeRoutingConfig ?? null,
|
||||
teamId: form?.teamId,
|
||||
orgId: form.team?.parentId,
|
||||
}),
|
||||
isEmbed: pageProps.isEmbed,
|
||||
}),
|
||||
permanent: false,
|
||||
},
|
||||
};
|
||||
} else if (decidedAction.type === "externalRedirectUrl") {
|
||||
return {
|
||||
redirect: {
|
||||
destination: `${decidedAction.value}?${stringify(context.query)}`,
|
||||
permanent: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Consider throwing error here as there is no value of decidedAction.type that would cause the flow to be here
|
||||
return {
|
||||
props: {
|
||||
...pageProps,
|
||||
form: serializableForm,
|
||||
message: "Unhandled type of action",
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -8,3 +8,4 @@ export { getServerErrorFromUnknown } from "./getServerErrorFromUnknown";
|
||||
export { getTranslation } from "./i18n";
|
||||
export { getDefaultLocations } from "./getDefaultLocations";
|
||||
export { default as perfObserver } from "./perfObserver";
|
||||
export { getRoutedUrl } from "./getRoutedUrl";
|
||||
@@ -31,6 +31,7 @@ import getEnabledAppsFromCredentials from "@calcom/lib/apps/getEnabledAppsFromCr
|
||||
import getAllUserBookings from "@calcom/lib/bookings/getAllUserBookings";
|
||||
import { symmetricEncrypt, symmetricDecrypt } from "@calcom/lib/crypto";
|
||||
import getBulkEventTypes from "@calcom/lib/event-types/getBulkEventTypes";
|
||||
import { getRoutedUrl } from "@calcom/lib/server/getRoutedUrl";
|
||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
|
||||
@@ -53,6 +54,7 @@ export type UpdateScheduleOutputType = Awaited<
|
||||
typeof import("@calcom/trpc/server/routers/viewer/availability/schedule/update.handler").updateHandler
|
||||
>
|
||||
>;
|
||||
|
||||
export { getEventTypeById } from "@calcom/lib/event-types/getEventTypeById";
|
||||
export { getEventTypesByViewer } from "@calcom/lib/event-types/getEventTypesByViewer";
|
||||
export { getEventTypesPublic } from "@calcom/lib/event-types/getEventTypesPublic";
|
||||
@@ -232,3 +234,5 @@ export { handleDeleteCredential };
|
||||
export { getBulkEventTypes };
|
||||
|
||||
export { getBookingFieldsWithSystemFields };
|
||||
|
||||
export { getRoutedUrl };
|
||||
Reference in new issue
Block a user