Skip to content

Commit

Permalink
feat: V2 API endpoint create phone call (#16528)
Browse files Browse the repository at this point in the history
* refactor: V2 API endpoint create phone call

* fix: type err

* fix: type and build err

* chore: change default value

* chore: move it to another route

* test: for create phone call

* chore: undo constant

* chore: remove test

* fix: make begin_message optional

* chore: improvements

* chore: begin message

* chore: remove unused import

* chore: bump platform libraries with handleCreatePhoneCall

* chore: improvement

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Benny Joo <sldisek783@gmail.com>
Co-authored-by: Morgan Vernay <morgan@cal.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
  • Loading branch information
5 people committed Sep 13, 2024
1 parent 6e237ae commit 5f2b4ae
Show file tree
Hide file tree
Showing 14 changed files with 443 additions and 124 deletions.
2 changes: 1 addition & 1 deletion apps/api/v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"dependencies": {
"@calcom/platform-constants": "*",
"@calcom/platform-enums": "*",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.33",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.34",
"@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2",
"@calcom/platform-types": "*",
"@calcom/platform-utils": "*",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { ApiProperty as DocsProperty } from "@nestjs/swagger";
import { Transform } from "class-transformer";
import { IsString, IsBoolean, IsOptional, IsEnum, Matches } from "class-validator";

export enum TemplateType {
CHECK_IN_APPOINTMENT = "CHECK_IN_APPOINTMENT",
CUSTOM_TEMPLATE = "CUSTOM_TEMPLATE",
}

export class CreatePhoneCallInput {
@IsString()
@Matches(/^\+[1-9]\d{1,14}$/, {
message:
"Invalid phone number format. Expected format: +<CountryCode><PhoneNumber> with no spaces or separators.",
})
@DocsProperty({ description: "Your phone number" })
yourPhoneNumber!: string;

@IsString()
@Matches(/^\+[1-9]\d{1,14}$/, {
message:
"Invalid phone number format. Expected format: +<CountryCode><PhoneNumber> with no spaces or separators.",
})
@DocsProperty({ description: "Number to call" })
numberToCall!: string;

@IsString()
@DocsProperty({ description: "CAL API Key" })
calApiKey!: string;

@IsBoolean()
@DocsProperty({ description: "Enabled status", default: true })
enabled = true;

@IsEnum(TemplateType)
@DocsProperty({ description: "Template type", enum: TemplateType })
templateType: TemplateType = TemplateType.CUSTOM_TEMPLATE;

@IsOptional()
@IsString()
@DocsProperty({ description: "Scheduler name" })
schedulerName?: string;

@IsOptional()
@IsString()
@Transform(({ value }) => (value ? value : undefined))
@DocsProperty({ description: "Guest name" })
guestName?: string;

@IsOptional()
@IsString()
@Transform(({ value }) => (value ? value : undefined))
@DocsProperty({ description: "Guest email" })
guestEmail?: string;

@IsOptional()
@IsString()
@Transform(({ value }) => (value ? value : undefined))
@DocsProperty({ description: "Guest company" })
guestCompany?: string;

@IsOptional()
@IsString()
@DocsProperty({ description: "Begin message" })
beginMessage?: string;

@IsOptional()
@IsString()
@DocsProperty({ description: "General prompt" })
generalPrompt?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer";
import { IsEnum, IsString, ValidateNested } from "class-validator";

import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";

class Data {
@IsString()
@ApiProperty()
callId!: string;

@IsString()
@ApiProperty()
agentId!: string;
}

export class CreatePhoneCallOutput {
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
@IsEnum([SUCCESS_STATUS, ERROR_STATUS])
status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;

@ApiProperty({
type: Data,
})
@ValidateNested()
@Type(() => Data)
data!: Data;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { CreatePhoneCallInput } from "@/ee/event-types/event-types_2024_06_14/inputs/create-phone-call.input";
import { CreatePhoneCallOutput } from "@/ee/event-types/event-types_2024_06_14/outputs/create-phone-call.output";
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
Expand Down Expand Up @@ -33,6 +35,7 @@ import {
import { ApiTags as DocsTags } from "@nestjs/swagger";

import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { handleCreatePhoneCall } from "@calcom/platform-libraries";
import {
CreateTeamEventTypeInput_2024_06_14,
GetTeamEventTypesQuery_2024_06_14,
Expand Down Expand Up @@ -91,6 +94,30 @@ export class OrganizationsEventTypesController {
};
}

@Roles("TEAM_ADMIN")
@Post("/teams/:teamId/event-types/:eventTypeId/create-phone-call")
@UseGuards(ApiAuthGuard, IsOrgGuard, IsTeamInOrg, RolesGuard)
async createPhoneCall(
@Param("eventTypeId") eventTypeId: number,
@Param("orgId", ParseIntPipe) orgId: number,
@Body() body: CreatePhoneCallInput,
@GetUser() user: UserWithProfile
): Promise<CreatePhoneCallOutput> {
const data = await handleCreatePhoneCall({
user: {
id: user.id,
timeZone: user.timeZone,
profile: { organization: { id: orgId } },
},
input: { ...body, eventTypeId },
});

return {
status: SUCCESS_STATUS,
data,
};
}

@UseGuards(IsOrgGuard, IsTeamInOrg, IsAdminAPIEnabledGuard)
@Get("/teams/:teamId/event-types")
async getTeamEventTypes(
Expand Down Expand Up @@ -138,7 +165,7 @@ export class OrganizationsEventTypesController {
@Patch("/teams/:teamId/event-types/:eventTypeId")
async updateTeamEventType(
@Param("teamId", ParseIntPipe) teamId: number,
@Param("eventTypeId") eventTypeId: number,
@Param("eventTypeId", ParseIntPipe) eventTypeId: number,
@GetUser() user: UserWithProfile,
@Body() bodyEventType: UpdateTeamEventTypeInput_2024_06_14
): Promise<UpdateTeamEventTypeOutput> {
Expand Down
167 changes: 161 additions & 6 deletions apps/api/v2/swagger/documentation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1982,6 +1982,54 @@
]
}
},
"/v2/organizations/{orgId}/teams/{teamId}/event-types/{eventTypeId}/create-phone-call": {
"post": {
"operationId": "OrganizationsEventTypesController_createPhoneCall",
"parameters": [
{
"name": "eventTypeId",
"required": true,
"in": "path",
"schema": {
"type": "number"
}
},
{
"name": "orgId",
"required": true,
"in": "path",
"schema": {
"type": "number"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreatePhoneCallInput"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreatePhoneCallOutput"
}
}
}
}
},
"tags": [
"Organizations Event Types"
]
}
},
"/v2/organizations/{orgId}/teams/event-types": {
"get": {
"operationId": "OrganizationsEventTypesController_getTeamsEventTypes",
Expand Down Expand Up @@ -3324,7 +3372,31 @@
"/v2/calendars/busy-times": {
"get": {
"operationId": "CalendarsController_getBusyTimes",
"parameters": [],
"parameters": [
{
"name": "loggedInUsersTz",
"required": true,
"in": "query",
"description": "The timezone of the logged in user represented as a string",
"example": "America/New_York",
"schema": {
"type": "string"
}
},
{
"name": "calendarsToLoad",
"required": true,
"in": "query",
"description": "An array of Calendar objects representing the calendars to be loaded",
"example": "[{ credentialId: \"1\", externalId: \"AQgtJE7RnHEeyisVq2ENs2gAAAgEGAAAACgtJE7RnHEeyisVq2ENs2gAAAhSDAAAA\" }, { credentialId: \"2\", externalId: \"AQM7RnHEeyisVq2ENs2gAAAhFDBBBBB\" }]",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
}
],
"responses": {
"200": {
"description": "",
Expand Down Expand Up @@ -7794,6 +7866,90 @@
"data"
]
},
"CreatePhoneCallInput": {
"type": "object",
"properties": {
"yourPhoneNumber": {
"type": "string",
"pattern": "/^\\+[1-9]\\d{1,14}$/",
"description": "Your phone number"
},
"numberToCall": {
"type": "string",
"pattern": "/^\\+[1-9]\\d{1,14}$/",
"description": "Number to call"
},
"calApiKey": {
"type": "string",
"description": "CAL API Key"
},
"enabled": {
"type": "object",
"default": true,
"description": "Enabled status"
},
"templateType": {
"default": "CUSTOM_TEMPLATE",
"enum": [
"CHECK_IN_APPOINTMENT",
"CUSTOM_TEMPLATE"
],
"type": "string",
"description": "Template type"
},
"schedulerName": {
"type": "string",
"description": "Scheduler name"
},
"guestName": {
"type": "string",
"description": "Guest name"
},
"guestEmail": {
"type": "string",
"description": "Guest email"
},
"guestCompany": {
"type": "string",
"description": "Guest company"
},
"beginMessage": {
"type": "string",
"description": "Begin message"
},
"generalPrompt": {
"type": "string",
"description": "General prompt"
}
},
"required": [
"yourPhoneNumber",
"numberToCall",
"calApiKey",
"enabled",
"templateType"
]
},
"CreatePhoneCallOutput": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "success",
"enum": [
"success",
"error"
]
},
"data": {
"$ref": "#/components/schemas/Data"
}
},
"required": [
"status",
"data"
]
},
"GetTeamEventTypesOutput": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -8636,7 +8792,8 @@
"MEETING_STARTED",
"RECORDING_READY",
"INSTANT_MEETING",
"RECORDING_TRANSCRIPTION_GENERATED"
"RECORDING_TRANSCRIPTION_GENERATED",
"OOO_CREATED"
]
},
"active": {
Expand Down Expand Up @@ -8709,7 +8866,8 @@
"MEETING_STARTED",
"RECORDING_READY",
"INSTANT_MEETING",
"RECORDING_TRANSCRIPTION_GENERATED"
"RECORDING_TRANSCRIPTION_GENERATED",
"OOO_CREATED"
]
},
"active": {
Expand Down Expand Up @@ -10304,9 +10462,6 @@
"rescheduleUid": {
"type": "string"
},
"recurringEventId": {
"type": "string"
},
"timeZone": {
"type": "string"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/features/ee/cal-ai-phone/retellAIService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type initProps = {
eventTypeId: number;
calApiKey: string;
loggedInUserTimeZone: string;
beginMessage?: string;
beginMessage?: string | null;
dynamicVariables: DynamicVariables;
generalPrompt: string;
};
Expand Down
2 changes: 1 addition & 1 deletion packages/features/ee/cal-ai-phone/zod-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export type TCreateRetellLLMSchema = z.infer<typeof ZCreateRetellLLMSchema>;
export const ZGetRetellLLMSchema = z
.object({
general_prompt: z.string(),
begin_message: z.string().nullable(),
begin_message: z.string().nullable().optional(),
llm_id: z.string(),
llm_websocket_url: z.string(),
general_tools: z.array(
Expand Down
Loading

0 comments on commit 5f2b4ae

Please sign in to comment.