feat: api v2 event types ordering - user, team, org (#25177)
* feat: api-v2-event-types-ordering * sort team and org event types * revert: remove accidental changes to api-auth.strategy.ts * docs: add ordering documentation and test for event types endpoints - Added test assertion to verify event types are returned in descending order by ID (newest first) - Added API documentation to user event types endpoint describing default ordering behavior - Added API documentation to team event types endpoint describing default ordering behavior - Added API documentation to organization event types endpoints describing default ordering behavior Addresses PR feedback to document and test the ordering behavior introduced in the API v2 event types ordering feature. * feat: add optional sortCreatedAt parameter to event types endpoints - Add sortCreatedAt query parameter (SortOrderType: "asc" | "desc") to all event types endpoints - Define SortOrder enum and SortOrderType in pagination.input.ts for reusability - When not provided, no explicit ordering is applied (backward compatible) - Update user, team, and organization event types endpoints - Add comprehensive e2e tests for all sorting scenarios - Fix circular dependency in platform-types import - Thread sortCreatedAt through all service layers - Use spread pattern for conditional orderBy to avoid empty array issues Addresses PR feedback to make ordering opt-in rather than changing default behavior
This commit is contained in:
+17
-8
@@ -47,8 +47,8 @@ import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
import { handleCreatePhoneCall } from "@calcom/platform-libraries";
|
||||
import {
|
||||
CreateTeamEventTypeInput_2024_06_14,
|
||||
GetOrganizationEventTypesQuery_2024_06_14,
|
||||
GetTeamEventTypesQuery_2024_06_14,
|
||||
SkipTakePagination,
|
||||
TeamEventTypeOutput_2024_06_14,
|
||||
UpdateTeamEventTypeInput_2024_06_14,
|
||||
} from "@calcom/platform-types";
|
||||
@@ -159,12 +159,16 @@ export class OrganizationsEventTypesController {
|
||||
|
||||
@UseGuards(IsOrgGuard, IsTeamInOrg, IsAdminAPIEnabledGuard)
|
||||
@Get("/teams/:teamId/event-types")
|
||||
@ApiOperation({ summary: "Get team event types" })
|
||||
@ApiOperation({
|
||||
summary: "Get team event types",
|
||||
description:
|
||||
'Use the optional `sortCreatedAt` query parameter to order results by creation date (by ID). Accepts "asc" (oldest first) or "desc" (newest first). When not provided, no explicit ordering is applied.',
|
||||
})
|
||||
async getTeamEventTypes(
|
||||
@Param("teamId", ParseIntPipe) teamId: number,
|
||||
@Query() queryParams: GetTeamEventTypesQuery_2024_06_14
|
||||
): Promise<GetTeamEventTypesOutput> {
|
||||
const { eventSlug, hostsLimit } = queryParams;
|
||||
const { eventSlug, hostsLimit, sortCreatedAt } = queryParams;
|
||||
|
||||
if (eventSlug) {
|
||||
const eventType = await this.organizationsEventTypesService.getTeamEventTypeBySlug(
|
||||
@@ -179,7 +183,7 @@ export class OrganizationsEventTypesController {
|
||||
};
|
||||
}
|
||||
|
||||
const eventTypes = await this.organizationsEventTypesService.getTeamEventTypes(teamId);
|
||||
const eventTypes = await this.organizationsEventTypesService.getTeamEventTypes(teamId, sortCreatedAt);
|
||||
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
@@ -191,16 +195,21 @@ export class OrganizationsEventTypesController {
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
|
||||
@Get("/teams/event-types")
|
||||
@ApiOperation({ summary: "Get all team event types" })
|
||||
@ApiOperation({
|
||||
summary: "Get all team event types",
|
||||
description:
|
||||
'Use the optional `sortCreatedAt` query parameter to order results by creation date (by ID). Accepts "asc" (oldest first) or "desc" (newest first). When not provided, no explicit ordering is applied.',
|
||||
})
|
||||
async getTeamsEventTypes(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Query() queryParams: SkipTakePagination
|
||||
@Query() queryParams: GetOrganizationEventTypesQuery_2024_06_14
|
||||
): Promise<GetTeamEventTypesOutput> {
|
||||
const { skip, take } = queryParams;
|
||||
const { skip, take, sortCreatedAt } = queryParams;
|
||||
const eventTypes = await this.organizationsEventTypesService.getOrganizationsTeamsEventTypes(
|
||||
orgId,
|
||||
skip,
|
||||
take
|
||||
take,
|
||||
sortCreatedAt
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
+9
-1
@@ -1,16 +1,24 @@
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
import type { SortOrderType } from "@calcom/platform-types";
|
||||
|
||||
@Injectable()
|
||||
export class OrganizationsEventTypesRepository {
|
||||
constructor(private readonly dbRead: PrismaReadService) {}
|
||||
async getOrganizationTeamsEventTypes(orgId: number, skip: number, take: number) {
|
||||
async getOrganizationTeamsEventTypes(
|
||||
orgId: number,
|
||||
skip: number,
|
||||
take: number,
|
||||
sortCreatedAt?: SortOrderType
|
||||
) {
|
||||
return this.dbRead.prisma.eventType.findMany({
|
||||
where: {
|
||||
team: {
|
||||
parentId: orgId,
|
||||
},
|
||||
},
|
||||
...(sortCreatedAt && { orderBy: { id: sortCreatedAt } }),
|
||||
skip,
|
||||
take,
|
||||
include: { users: true, schedule: true, hosts: true, destinationCalendar: true },
|
||||
|
||||
+12
-4
@@ -1,4 +1,6 @@
|
||||
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
|
||||
|
||||
import type { SortOrderType } from "@calcom/platform-types";
|
||||
import { OrganizationsEventTypesRepository } from "@/modules/organizations/event-types/organizations-event-types.repository";
|
||||
import {
|
||||
TransformedCreateTeamEventTypeInput,
|
||||
@@ -83,16 +85,22 @@ export class OrganizationsEventTypesService {
|
||||
return this.teamsEventTypesService.getTeamEventTypeBySlug(teamId, eventTypeSlug, hostsLimit);
|
||||
}
|
||||
|
||||
async getTeamEventTypes(teamId: number): Promise<DatabaseTeamEventType[]> {
|
||||
return await this.teamsEventTypesService.getTeamEventTypes(teamId);
|
||||
async getTeamEventTypes(teamId: number, sortCreatedAt?: SortOrderType): Promise<DatabaseTeamEventType[]> {
|
||||
return await this.teamsEventTypesService.getTeamEventTypes(teamId, sortCreatedAt);
|
||||
}
|
||||
|
||||
async getOrganizationsTeamsEventTypes(
|
||||
orgId: number,
|
||||
skip = 0,
|
||||
take = 250
|
||||
take = 250,
|
||||
sortCreatedAt?: SortOrderType
|
||||
): Promise<DatabaseTeamEventType[]> {
|
||||
return await this.organizationEventTypesRepository.getOrganizationTeamsEventTypes(orgId, skip, take);
|
||||
return await this.organizationEventTypesRepository.getOrganizationTeamsEventTypes(
|
||||
orgId,
|
||||
skip,
|
||||
take,
|
||||
sortCreatedAt
|
||||
);
|
||||
}
|
||||
|
||||
async updateOrganizationTeamEventType(
|
||||
|
||||
+7
-3
@@ -136,12 +136,16 @@ export class TeamsEventTypesController {
|
||||
}
|
||||
|
||||
@Get("/")
|
||||
@ApiOperation({ summary: "Get a team event type" })
|
||||
@ApiOperation({
|
||||
summary: "Get team event types",
|
||||
description:
|
||||
'Use the optional `sortCreatedAt` query parameter to order results by creation date (by ID). Accepts "asc" (oldest first) or "desc" (newest first). When not provided, no explicit ordering is applied.',
|
||||
})
|
||||
async getTeamEventTypes(
|
||||
@Param("teamId", ParseIntPipe) teamId: number,
|
||||
@Query() queryParams: GetTeamEventTypesQuery_2024_06_14
|
||||
): Promise<GetTeamEventTypesOutput> {
|
||||
const { eventSlug, hostsLimit } = queryParams;
|
||||
const { eventSlug, hostsLimit, sortCreatedAt } = queryParams;
|
||||
|
||||
if (eventSlug) {
|
||||
const eventType = await this.teamsEventTypesService.getTeamEventTypeBySlug(
|
||||
@@ -156,7 +160,7 @@ export class TeamsEventTypesController {
|
||||
};
|
||||
}
|
||||
|
||||
const eventTypes = await this.teamsEventTypesService.getTeamEventTypes(teamId);
|
||||
const eventTypes = await this.teamsEventTypesService.getTeamEventTypes(teamId, sortCreatedAt);
|
||||
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
|
||||
@@ -11,6 +11,8 @@ import { UsersService } from "@/modules/users/services/users.service";
|
||||
import { UserWithProfile } from "@/modules/users/users.repository";
|
||||
import { Injectable, NotFoundException, Logger } from "@nestjs/common";
|
||||
|
||||
import type { SortOrderType } from "@calcom/platform-types";
|
||||
|
||||
import { createEventType, updateEventType } from "@calcom/platform-libraries/event-types";
|
||||
|
||||
@Injectable()
|
||||
@@ -101,8 +103,8 @@ export class TeamsEventTypesService {
|
||||
return eventType;
|
||||
}
|
||||
|
||||
async getTeamEventTypes(teamId: number): Promise<DatabaseTeamEventType[]> {
|
||||
return await this.teamsEventTypesRepository.getTeamEventTypes(teamId);
|
||||
async getTeamEventTypes(teamId: number, sortCreatedAt?: SortOrderType): Promise<DatabaseTeamEventType[]> {
|
||||
return await this.teamsEventTypesRepository.getTeamEventTypes(teamId, sortCreatedAt);
|
||||
}
|
||||
|
||||
async updateTeamEventType(
|
||||
|
||||
@@ -2,6 +2,8 @@ import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
import type { SortOrderType } from "@calcom/platform-types";
|
||||
|
||||
@Injectable()
|
||||
export class TeamsEventTypesRepository {
|
||||
constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
|
||||
@@ -79,11 +81,12 @@ export class TeamsEventTypesRepository {
|
||||
});
|
||||
}
|
||||
|
||||
async getTeamEventTypes(teamId: number) {
|
||||
async getTeamEventTypes(teamId: number, sortCreatedAt?: SortOrderType) {
|
||||
return this.dbRead.prisma.eventType.findMany({
|
||||
where: {
|
||||
teamId,
|
||||
},
|
||||
...(sortCreatedAt && { orderBy: { id: sortCreatedAt } }),
|
||||
include: {
|
||||
users: true,
|
||||
schedule: true,
|
||||
|
||||
Reference in New Issue
Block a user