From c0ecfcd910958e3c65b2aa05d7ffb90e2c892583 Mon Sep 17 00:00:00 2001 From: Lauris Skraucis Date: Fri, 21 Mar 2025 14:25:59 +0100 Subject: [PATCH] docs: v2 team event schedulingType (#20289) * refactor: input scheduling type * refactor: output scheduling type --- .../organizations-event-types.e2e-spec.ts | 121 +++++++++++++++++- .../event-types/services/output.service.ts | 19 ++- .../teams-event-types.controller.e2e-spec.ts | 10 +- apps/api/v2/swagger/documentation.json | 16 ++- docs/api-reference/v2/openapi.json | 8 +- .../inputs/create-event-type.input.ts | 6 +- .../outputs/event-type.output.ts | 14 +- 7 files changed, 162 insertions(+), 32 deletions(-) diff --git a/apps/api/v2/src/modules/organizations/event-types/organizations-event-types.e2e-spec.ts b/apps/api/v2/src/modules/organizations/event-types/organizations-event-types.e2e-spec.ts index 5d2278aff9..025d3dc559 100644 --- a/apps/api/v2/src/modules/organizations/event-types/organizations-event-types.e2e-spec.ts +++ b/apps/api/v2/src/modules/organizations/event-types/organizations-event-types.e2e-spec.ts @@ -324,7 +324,7 @@ describe("Organizations Event Types Endpoints", () => { const data = responseBody.data; expect(data.title).toEqual(body.title); expect(data.hosts.length).toEqual(2); - expect(data.schedulingType).toEqual("COLLECTIVE"); + expect(data.schedulingType).toEqual("collective"); evaluateHost(body.hosts[0], data.hosts[0]); evaluateHost(body.hosts[1], data.hosts[1]); expect(data.bookingLimitsCount).toEqual(body.bookingLimitsCount); @@ -457,8 +457,8 @@ describe("Organizations Event Types Endpoints", () => { const data = responseBody.data; expect(data.length).toEqual(2); - const eventTypeCollective = data.find((eventType) => eventType.schedulingType === "COLLECTIVE"); - const eventTypeManaged = data.find((eventType) => eventType.schedulingType === "MANAGED"); + const eventTypeCollective = data.find((eventType) => eventType.schedulingType === "collective"); + const eventTypeManaged = data.find((eventType) => eventType.schedulingType === "managed"); expect(eventTypeCollective?.title).toEqual(collectiveEventType.title); expect(eventTypeCollective?.hosts.length).toEqual(2); @@ -564,7 +564,7 @@ describe("Organizations Event Types Endpoints", () => { ).toEqual(newTitle); const responseTeamEvent = responseBody.data.find( - (eventType) => eventType.schedulingType === "MANAGED" + (eventType) => eventType.schedulingType === "managed" ); expect(responseTeamEvent).toBeDefined(); expect(responseTeamEvent?.title).toEqual(newTitle); @@ -823,7 +823,7 @@ describe("Organizations Event Types Endpoints", () => { expect(managedTeamEventTypes[0].assignAllTeamMembers).toEqual(true); const responseTeamEvent = responseBody.data.find( - (eventType) => eventType.schedulingType === "MANAGED" + (eventType) => eventType.schedulingType === "managed" ); expect(responseTeamEvent).toBeDefined(); expect(responseTeamEvent?.teamId).toEqual(team.id); @@ -954,6 +954,117 @@ describe("Organizations Event Types Endpoints", () => { }); }); + it("should create a round robin team event-type", async () => { + const body: CreateTeamEventTypeInput_2024_06_14 = { + successRedirectUrl: "https://masterchief.com/argentina/flan/video/1234", + title: "Coding consultation round robin", + slug: `organizations-event-types-round-robin-${randomString()}`, + description: "Our team will review your codebase.", + lengthInMinutes: 60, + locations: [ + { + type: "integration", + integration: "cal-video", + }, + ], + bookingFields: [ + { + type: "select", + label: "select which language is your codebase in", + slug: "select-language", + required: true, + placeholder: "select language", + options: ["javascript", "python", "cobol"], + }, + ], + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + schedulingType: "roundRobin", + hosts: [ + { + userId: teammate1.id, + mandatory: true, + priority: "high", + }, + { + userId: teammate2.id, + mandatory: false, + priority: "medium", + }, + ], + bookingLimitsCount: { + day: 2, + week: 5, + }, + onlyShowFirstAvailableSlot: true, + bookingLimitsDuration: { + day: 60, + week: 100, + }, + offsetStart: 30, + bookingWindow: { + type: BookingWindowPeriodInputTypeEnum_2024_06_14.calendarDays, + value: 30, + rolling: true, + }, + bookerLayouts: { + enabledLayouts: [ + BookerLayoutsInputEnum_2024_06_14.column, + BookerLayoutsInputEnum_2024_06_14.month, + BookerLayoutsInputEnum_2024_06_14.week, + ], + defaultLayout: BookerLayoutsInputEnum_2024_06_14.month, + }, + + confirmationPolicy: { + type: ConfirmationPolicyEnum.TIME, + noticeThreshold: { + count: 60, + unit: NoticeThresholdUnitEnum.MINUTES, + }, + blockUnconfirmedBookingsInBooker: true, + }, + requiresBookerEmailVerification: true, + hideCalendarNotes: true, + hideCalendarEventDetails: true, + lockTimeZoneToggleOnBookingPage: true, + color: { + darkThemeHex: "#292929", + lightThemeHex: "#fafafa", + }, + }; + + return request(app.getHttpServer()) + .post(`/v2/organizations/${org.id}/teams/${team.id}/event-types`) + .send(body) + .expect(201) + .then((response) => { + const responseBody: ApiSuccessResponse = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + + const data = responseBody.data; + expect(data.title).toEqual(body.title); + expect(data.hosts.length).toEqual(2); + expect(data.schedulingType).toEqual("roundRobin"); + evaluateHost(body.hosts[0], data.hosts[0]); + evaluateHost(body.hosts[1], data.hosts[1]); + expect(data.bookingLimitsCount).toEqual(body.bookingLimitsCount); + expect(data.onlyShowFirstAvailableSlot).toEqual(body.onlyShowFirstAvailableSlot); + expect(data.bookingLimitsDuration).toEqual(body.bookingLimitsDuration); + expect(data.offsetStart).toEqual(body.offsetStart); + expect(data.bookingWindow).toEqual(body.bookingWindow); + expect(data.bookerLayouts).toEqual(body.bookerLayouts); + expect(data.confirmationPolicy).toEqual(body.confirmationPolicy); + expect(data.requiresBookerEmailVerification).toEqual(body.requiresBookerEmailVerification); + expect(data.hideCalendarNotes).toEqual(body.hideCalendarNotes); + expect(data.hideCalendarEventDetails).toEqual(body.hideCalendarEventDetails); + expect(data.lockTimeZoneToggleOnBookingPage).toEqual(body.lockTimeZoneToggleOnBookingPage); + expect(data.color).toEqual(body.color); + expect(data.successRedirectUrl).toEqual("https://masterchief.com/argentina/flan/video/1234"); + collectiveEventType = responseBody.data; + }); + }); + function evaluateHost(expected: Host, received: Host | undefined) { expect(expected.userId).toEqual(received?.userId); expect(expected.mandatory).toEqual(received?.mandatory); diff --git a/apps/api/v2/src/modules/organizations/event-types/services/output.service.ts b/apps/api/v2/src/modules/organizations/event-types/services/output.service.ts index d4ae9a5f93..f9891a63c6 100644 --- a/apps/api/v2/src/modules/organizations/event-types/services/output.service.ts +++ b/apps/api/v2/src/modules/organizations/event-types/services/output.service.ts @@ -100,7 +100,9 @@ export class OutputOrganizationsEventTypesService { teamId, ownerId: userId, parentEventTypeId: parentId, - schedulingType: databaseEventType.schedulingType, + schedulingType: databaseEventType.schedulingType + ? this.getResponseSchedulingType(databaseEventType.schedulingType) + : databaseEventType.schedulingType, assignAllTeamMembers: teamId ? assignAllTeamMembers : undefined, team: { id: teamId, @@ -116,6 +118,19 @@ export class OutputOrganizationsEventTypesService { }; } + getResponseSchedulingType(schedulingType: SchedulingType) { + if (schedulingType === SchedulingType.COLLECTIVE) { + return "collective"; + } + if (schedulingType === SchedulingType.ROUND_ROBIN) { + return "roundRobin"; + } + if (schedulingType === SchedulingType.MANAGED) { + return "managed"; + } + return schedulingType; + } + async getManagedEventTypeHosts(eventTypeId: number) { const children = await this.teamsEventTypesRepository.getEventTypeChildren(eventTypeId); const transformedHosts: TeamEventTypeResponseHost[] = []; @@ -144,7 +159,7 @@ export class OutputOrganizationsEventTypesService { transformedHosts.push({ userId: databaseHost.userId, name: databaseUser?.name || "", - mandatory: databaseHost.isFixed, + mandatory: !!databaseHost.isFixed, priority: getPriorityLabel(databaseHost.priority || 2), avatarUrl: databaseUser?.avatarUrl, }); diff --git a/apps/api/v2/src/modules/teams/event-types/controllers/teams-event-types.controller.e2e-spec.ts b/apps/api/v2/src/modules/teams/event-types/controllers/teams-event-types.controller.e2e-spec.ts index 87a48b9370..caae397a1d 100644 --- a/apps/api/v2/src/modules/teams/event-types/controllers/teams-event-types.controller.e2e-spec.ts +++ b/apps/api/v2/src/modules/teams/event-types/controllers/teams-event-types.controller.e2e-spec.ts @@ -303,7 +303,7 @@ describe("Organizations Event Types Endpoints", () => { const data = responseBody.data; expect(data.title).toEqual(body.title); expect(data.hosts.length).toEqual(2); - expect(data.schedulingType).toEqual("COLLECTIVE"); + expect(data.schedulingType).toEqual("collective"); evaluateHost(body.hosts[0], data.hosts[0]); evaluateHost(body.hosts[1], data.hosts[1]); expect(data.bookingLimitsCount).toEqual(body.bookingLimitsCount); @@ -423,8 +423,8 @@ describe("Organizations Event Types Endpoints", () => { const data = responseBody.data; expect(data.length).toEqual(2); - const eventTypeCollective = data.find((eventType) => eventType.schedulingType === "COLLECTIVE"); - const eventTypeManaged = data.find((eventType) => eventType.schedulingType === "MANAGED"); + const eventTypeCollective = data.find((eventType) => eventType.schedulingType === "collective"); + const eventTypeManaged = data.find((eventType) => eventType.schedulingType === "managed"); expect(eventTypeCollective?.title).toEqual(collectiveEventType.title); expect(eventTypeCollective?.hosts.length).toEqual(2); @@ -516,7 +516,7 @@ describe("Organizations Event Types Endpoints", () => { ).toEqual(newTitle); const responseTeamEvent = responseBody.data.find( - (eventType) => eventType.schedulingType === "MANAGED" + (eventType) => eventType.schedulingType === "managed" ); expect(responseTeamEvent).toBeDefined(); expect(responseTeamEvent?.title).toEqual(newTitle); @@ -562,7 +562,7 @@ describe("Organizations Event Types Endpoints", () => { expect(managedTeamEventTypes[0].assignAllTeamMembers).toEqual(true); const responseTeamEvent = responseBody.data.find( - (eventType) => eventType.schedulingType === "MANAGED" + (eventType) => eventType.schedulingType === "managed" ); expect(responseTeamEvent).toBeDefined(); expect(responseTeamEvent?.teamId).toEqual(team.id); diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json index 1103394076..32590b9519 100644 --- a/apps/api/v2/swagger/documentation.json +++ b/apps/api/v2/swagger/documentation.json @@ -13182,7 +13182,14 @@ "example": "https://masterchief.com/argentina/flan/video/9129412" }, "schedulingType": { - "type": "object" + "type": "string", + "enum": [ + "collective", + "roundRobin", + "managed" + ], + "example": "collective", + "description": "The scheduling type for the team event - collective, roundRobin or managed." }, "hosts": { "type": "array", @@ -13613,11 +13620,10 @@ }, "schedulingType": { "type": "string", - "nullable": true, "enum": [ - "ROUND_ROBIN", - "COLLECTIVE", - "MANAGED" + "roundRobin", + "collective", + "managed" ] }, "team": { diff --git a/docs/api-reference/v2/openapi.json b/docs/api-reference/v2/openapi.json index fd2f273549..1e62d8eef1 100644 --- a/docs/api-reference/v2/openapi.json +++ b/docs/api-reference/v2/openapi.json @@ -12020,7 +12020,10 @@ "example": "https://masterchief.com/argentina/flan/video/9129412" }, "schedulingType": { - "type": "object" + "type": "string", + "enum": ["collective", "roundRobin", "managed"], + "example": "collective", + "description": "The scheduling type for the team event - collective, roundRobin or managed." }, "hosts": { "type": "array", @@ -12424,8 +12427,7 @@ }, "schedulingType": { "type": "string", - "nullable": true, - "enum": ["ROUND_ROBIN", "COLLECTIVE", "MANAGED"] + "enum": ["roundRobin", "collective", "managed"] }, "team": { "$ref": "#/components/schemas/EventTypeTeam" diff --git a/packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts b/packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts index b587605876..aad9e55ede 100644 --- a/packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts +++ b/packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts @@ -460,7 +460,11 @@ export class CreateTeamEventTypeInput_2024_06_14 extends BaseCreateEventTypeInpu return value; }) @IsEnum(SchedulingType) - @DocsProperty() + @DocsProperty({ + enum: ["collective", "roundRobin", "managed"], + example: "collective", + description: "The scheduling type for the team event - collective, roundRobin or managed.", + }) schedulingType!: keyof typeof SchedulingType; @ValidateNested({ each: true }) diff --git a/packages/platform/types/event-types/event-types_2024_06_14/outputs/event-type.output.ts b/packages/platform/types/event-types/event-types_2024_06_14/outputs/event-type.output.ts index 8c1b2f0752..14c67e9ada 100644 --- a/packages/platform/types/event-types/event-types_2024_06_14/outputs/event-type.output.ts +++ b/packages/platform/types/event-types/event-types_2024_06_14/outputs/event-type.output.ts @@ -68,14 +68,6 @@ import { ValidateOutputLocations_2024_06_14, } from "./locations.output"; -enum SchedulingTypeEnum { - ROUND_ROBIN = "ROUND_ROBIN", - COLLECTIVE = "COLLECTIVE", - MANAGED = "MANAGED", -} - -export type EventTypesOutputSchedulingType = "ROUND_ROBIN" | "COLLECTIVE" | "MANAGED"; - class User_2024_06_14 { @IsInt() @DocsProperty() @@ -486,9 +478,9 @@ export class TeamEventTypeOutput_2024_06_14 extends BaseEventTypeOutput_2024_06_ @ApiPropertyOptional() assignAllTeamMembers?: boolean; - @IsEnum(SchedulingTypeEnum) - @DocsProperty({ enum: SchedulingTypeEnum, nullable: true }) - schedulingType!: EventTypesOutputSchedulingType | null; + @IsEnum(["roundRobin", "collective", "managed"] as const) + @DocsProperty({ enum: ["roundRobin", "collective", "managed"] }) + schedulingType!: "roundRobin" | "collective" | "managed" | null; @IsOptional() @IsBoolean()