fix: Allow zero routingFormResponseId in API v2 available endpoint (#21301)

* Allow zero routingFormResponseId

* fix: conditionally validate routingFormResponseId to allow 0 only for dry run operations

Co-Authored-By: hariom@cal.com <hariom@cal.com>

* fix: pass _isDryRun parameter to API v2 available endpoint

Co-Authored-By: hariom@cal.com <hariom@cal.com>

* fix runtime error and improve message

* Add e2e test to verify the constraint works

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: hariom@cal.com <hariom@cal.com>
This commit is contained in:
authored and GitHub committed 2025-05-16 12:23:11 +05:30
1 parent 5bc9d71a88
commit fab364f084
3 files changed
+115 -1

No files matched your search

@@ -2,6 +2,7 @@ import { useSearchParams } from "next/navigation";
import { updateEmbedBookerState } from "@calcom/embed-core/src/embed-iframe";
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
import { isBookingDryRun } from "@calcom/features/bookings/Booker/utils/isBookingDryRun";
import { useTimesForSchedule } from "@calcom/features/schedules/lib/use-schedule/useTimesForSchedule";
import { getRoutedTeamMemberIdsFromSearchParams } from "@calcom/lib/bookings/getRoutedTeamMemberIdsFromSearchParams";
import { PUBLIC_QUERY_AVAILABLE_SLOTS_INTERVAL_SECONDS } from "@calcom/lib/constants";
@@ -97,6 +98,7 @@ export const useSchedule = ({
email,
// Ensures that connectVersion causes a refresh of the data
...(embedConnectVersion ? { embedConnectVersion } : {}),
_isDryRun: searchParams ? isBookingDryRun(searchParams) : false,
};
const options = {
@@ -1,5 +1,6 @@
import { ApiProperty, ApiPropertyOptional, ApiHideProperty } from "@nestjs/swagger";
import { Transform } from "class-transformer";
import type { ValidationArguments, ValidatorConstraintInterface } from "class-validator";
import {
IsArray,
IsBoolean,
@@ -10,10 +11,35 @@ import {
IsString,
Min,
IsEnum,
ValidatorConstraint,
Validate,
} from "class-validator";
import { SlotFormat } from "@calcom/platform-enums";
@ValidatorConstraint({ name: "routingFormResponseIdValidator", async: false })
class RoutingFormResponseIdValidator implements ValidatorConstraintInterface {
validate(routingFormResponseId: number, args: ValidationArguments) {
if (routingFormResponseId === undefined) return true;
const payload = args.object as GetAvailableSlotsInput_2024_04_15;
if (payload._isDryRun) {
return routingFormResponseId === 0;
}
return routingFormResponseId >= 1;
}
defaultMessage(args: ValidationArguments) {
const payload = args.object as GetAvailableSlotsInput_2024_04_15;
if (payload._isDryRun) {
return "routingFormResponseId must be 0 for dry run";
}
return "routingFormResponseId must be a positive number";
}
}
export class GetAvailableSlotsInput_2024_04_15 {
@IsDateString({ strict: true })
@ApiProperty({
@@ -145,7 +171,7 @@ export class GetAvailableSlotsInput_2024_04_15 {
@Transform(({ value }: { value: string }) => value && parseInt(value))
@IsNumber()
@IsOptional()
@Min(1, { message: "routingFormResponseId must be a positive number" })
@Validate(RoutingFormResponseIdValidator)
@ApiPropertyOptional()
@ApiHideProperty()
routingFormResponseId?: number;
@@ -156,6 +182,12 @@ export class GetAvailableSlotsInput_2024_04_15 {
@ApiHideProperty()
_shouldServeCache?: boolean;
@Transform(({ value }) => value && value.toLowerCase() === "true")
@IsBoolean()
@IsOptional()
@ApiHideProperty()
_isDryRun?: boolean;
@Transform(({ value }) => value && value.toLowerCase() === "true")
@IsBoolean()
@IsOptional()