From 9ff10877e7d6787d97a192805615bfcb6236837e Mon Sep 17 00:00:00 2001 From: Morgan <33722304+ThyMinimalDev@users.noreply.github.com> Date: Mon, 15 Jul 2024 13:27:33 +0300 Subject: [PATCH] chore: fix apiv2 org teams doc (#15746) --- .../organizations-schedules.controller.ts | 18 +- .../teams/organizations-teams.controller.ts | 25 +- .../outputs/organization-team.output.ts | 38 ++- apps/api/v2/swagger/documentation.json | 291 +++++++++++++++++- packages/platform/types/api.ts | 5 + 5 files changed, 345 insertions(+), 32 deletions(-) diff --git a/apps/api/v2/src/modules/organizations/controllers/schedules/organizations-schedules.controller.ts b/apps/api/v2/src/modules/organizations/controllers/schedules/organizations-schedules.controller.ts index 92924c9cdf..9eb076e8a8 100644 --- a/apps/api/v2/src/modules/organizations/controllers/schedules/organizations-schedules.controller.ts +++ b/apps/api/v2/src/modules/organizations/controllers/schedules/organizations-schedules.controller.ts @@ -21,8 +21,6 @@ import { Query, } from "@nestjs/common"; import { ApiTags as DocsTags } from "@nestjs/swagger"; -import { Transform } from "class-transformer"; -import { IsNumber, Min, Max, IsOptional } from "class-validator"; import { SUCCESS_STATUS } from "@calcom/platform-constants"; import { @@ -34,21 +32,7 @@ import { UpdateScheduleInput_2024_06_11, UpdateScheduleOutput_2024_06_11, } from "@calcom/platform-types"; - -class SkipTakePagination { - @Transform(({ value }: { value: string }) => value && parseInt(value)) - @IsNumber() - @Min(1) - @Max(250) - @IsOptional() - take?: number; - - @Transform(({ value }: { value: string }) => value && parseInt(value)) - @IsNumber() - @Min(0) - @IsOptional() - skip?: number; -} +import { SkipTakePagination } from "@calcom/platform-types"; @Controller({ path: "/v2/organizations/:orgId", diff --git a/apps/api/v2/src/modules/organizations/controllers/teams/organizations-teams.controller.ts b/apps/api/v2/src/modules/organizations/controllers/teams/organizations-teams.controller.ts index 0b329d1dfa..65a2e18aba 100644 --- a/apps/api/v2/src/modules/organizations/controllers/teams/organizations-teams.controller.ts +++ b/apps/api/v2/src/modules/organizations/controllers/teams/organizations-teams.controller.ts @@ -6,7 +6,11 @@ import { IsOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard"; import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard"; import { IsTeamInOrg } from "@/modules/auth/guards/teams/is-team-in-org.guard"; import { CreateOrgTeamDto } from "@/modules/organizations/inputs/create-organization-team.input"; -import { OrgTeamOutputDto } from "@/modules/organizations/outputs/organization-team.output"; +import { + OrgTeamOutputDto, + OrgTeamOutputResponseDto, + OrgTeamsOutputResponseDto, +} from "@/modules/organizations/outputs/organization-team.output"; import { OrganizationsTeamsService } from "@/modules/organizations/services/organizations-teams.service"; import { Controller, @@ -20,11 +24,11 @@ import { Post, Body, } from "@nestjs/common"; -import { ApiTags as DocsTags } from "@nestjs/swagger"; +import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger"; import { plainToClass } from "class-transformer"; import { SUCCESS_STATUS } from "@calcom/platform-constants"; -import { ApiResponse, SkipTakePagination } from "@calcom/platform-types"; +import { SkipTakePagination } from "@calcom/platform-types"; import { Team } from "@calcom/prisma/client"; @Controller({ @@ -37,12 +41,13 @@ export class OrganizationsTeamsController { constructor(private organizationsTeamsService: OrganizationsTeamsService) {} @Get() + @ApiOperation({ summary: "Get all the teams of an organization." }) @UseGuards() @Roles("ORG_ADMIN") async getAllTeams( @Param("orgId", ParseIntPipe) orgId: number, @Query() queryParams: SkipTakePagination - ): Promise> { + ): Promise { const { skip, take } = queryParams; const teams = await this.organizationsTeamsService.getPaginatedOrgTeams(orgId, skip ?? 0, take ?? 250); return { @@ -54,7 +59,8 @@ export class OrganizationsTeamsController { @UseGuards(IsTeamInOrg) @Roles("TEAM_ADMIN") @Get("/:teamId") - async getTeam(@GetTeam() team: Team): Promise> { + @ApiOperation({ summary: "Get a team of the organization by ID." }) + async getTeam(@GetTeam() team: Team): Promise { return { status: SUCCESS_STATUS, data: plainToClass(OrgTeamOutputDto, team, { strategy: "excludeAll" }), @@ -64,10 +70,11 @@ export class OrganizationsTeamsController { @UseGuards(IsTeamInOrg) @Roles("ORG_ADMIN") @Delete("/:teamId") + @ApiOperation({ summary: "Delete a team of the organization by ID." }) async deleteTeam( @Param("orgId", ParseIntPipe) orgId: number, @Param("teamId", ParseIntPipe) teamId: number - ): Promise> { + ): Promise { const team = await this.organizationsTeamsService.deleteOrgTeam(orgId, teamId); return { status: SUCCESS_STATUS, @@ -78,11 +85,12 @@ export class OrganizationsTeamsController { @UseGuards(IsTeamInOrg) @Roles("ORG_ADMIN") @Patch("/:teamId") + @ApiOperation({ summary: "Update a team of the organization by ID." }) async updateTeam( @Param("orgId", ParseIntPipe) orgId: number, @Param("teamId", ParseIntPipe) teamId: number, @Body() body: CreateOrgTeamDto - ): Promise> { + ): Promise { const team = await this.organizationsTeamsService.updateOrgTeam(orgId, teamId, body); return { status: SUCCESS_STATUS, @@ -93,10 +101,11 @@ export class OrganizationsTeamsController { @Post() @UseGuards() @Roles("ORG_ADMIN") + @ApiOperation({ summary: "Create a team for an organization." }) async createTeam( @Param("orgId", ParseIntPipe) orgId: number, @Body() body: CreateOrgTeamDto - ): Promise> { + ): Promise { const team = await this.organizationsTeamsService.createOrgTeam(orgId, body); return { status: SUCCESS_STATUS, diff --git a/apps/api/v2/src/modules/organizations/outputs/organization-team.output.ts b/apps/api/v2/src/modules/organizations/outputs/organization-team.output.ts index d3cac09e70..32c2134ba5 100644 --- a/apps/api/v2/src/modules/organizations/outputs/organization-team.output.ts +++ b/apps/api/v2/src/modules/organizations/outputs/organization-team.output.ts @@ -1,5 +1,17 @@ -import { Expose } from "class-transformer"; -import { IsBoolean, IsInt, IsOptional, IsString, IsUrl, Length } from "class-validator"; +import { ApiProperty } from "@nestjs/swagger"; +import { Expose, Type } from "class-transformer"; +import { + IsBoolean, + IsEnum, + IsInt, + IsOptional, + IsString, + IsUrl, + Length, + ValidateNested, +} from "class-validator"; + +import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants"; export class OrgTeamOutputDto { @IsInt() @@ -105,3 +117,25 @@ export class OrgTeamOutputDto { @Expose() readonly weekStart?: string = "Sunday"; } + +export class OrgTeamsOutputResponseDto { + @ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] }) + @IsEnum([SUCCESS_STATUS, ERROR_STATUS]) + status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS; + + @Expose() + @ValidateNested() + @Type(() => OrgTeamOutputDto) + data!: OrgTeamOutputDto[]; +} + +export class OrgTeamOutputResponseDto { + @ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] }) + @IsEnum([SUCCESS_STATUS, ERROR_STATUS]) + status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS; + + @Expose() + @ValidateNested() + @Type(() => OrgTeamOutputDto) + data!: OrgTeamOutputDto; +} diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json index 8550aa638d..4c5003de3d 100644 --- a/apps/api/v2/swagger/documentation.json +++ b/apps/api/v2/swagger/documentation.json @@ -33,6 +33,16 @@ "schema": { "type": "string" } + }, + { + "name": "limit", + "required": false, + "in": "query", + "description": "The number of items to return", + "example": 10, + "schema": { + "type": "number" + } } ], "responses": { @@ -417,6 +427,16 @@ "schema": { "type": "string" } + }, + { + "name": "limit", + "required": false, + "in": "query", + "description": "The number of items to return", + "example": 10, + "schema": { + "type": "number" + } } ], "responses": { @@ -809,6 +829,7 @@ "/v2/organizations/{orgId}/teams": { "get": { "operationId": "OrganizationsTeamsController_getAllTeams", + "summary": "Get all the teams of an organization.", "parameters": [ { "name": "orgId", @@ -817,6 +838,26 @@ "schema": { "type": "number" } + }, + { + "name": "take", + "required": false, + "in": "query", + "description": "The number of items to return", + "example": 10, + "schema": { + "type": "number" + } + }, + { + "name": "skip", + "required": false, + "in": "query", + "description": "The number of items to skip", + "example": 0, + "schema": { + "type": "number" + } } ], "responses": { @@ -825,7 +866,7 @@ "content": { "application/json": { "schema": { - "type": "object" + "$ref": "#/components/schemas/OrgTeamsOutputResponseDto" } } } @@ -837,6 +878,7 @@ }, "post": { "operationId": "OrganizationsTeamsController_createTeam", + "summary": "Create a team for an organization.", "parameters": [ { "name": "orgId", @@ -863,7 +905,7 @@ "content": { "application/json": { "schema": { - "type": "object" + "$ref": "#/components/schemas/OrgTeamOutputResponseDto" } } } @@ -877,6 +919,7 @@ "/v2/organizations/{orgId}/teams/{teamId}": { "get": { "operationId": "OrganizationsTeamsController_getTeam", + "summary": "Get a team of the organization by ID.", "parameters": [], "responses": { "200": { @@ -884,7 +927,7 @@ "content": { "application/json": { "schema": { - "type": "object" + "$ref": "#/components/schemas/OrgTeamOutputResponseDto" } } } @@ -896,6 +939,7 @@ }, "delete": { "operationId": "OrganizationsTeamsController_deleteTeam", + "summary": "Delete a team of the organization by ID.", "parameters": [ { "name": "orgId", @@ -920,7 +964,7 @@ "content": { "application/json": { "schema": { - "type": "object" + "$ref": "#/components/schemas/OrgTeamOutputResponseDto" } } } @@ -932,6 +976,7 @@ }, "patch": { "operationId": "OrganizationsTeamsController_updateTeam", + "summary": "Update a team of the organization by ID.", "parameters": [ { "name": "orgId", @@ -966,7 +1011,7 @@ "content": { "application/json": { "schema": { - "type": "object" + "$ref": "#/components/schemas/OrgTeamOutputResponseDto" } } } @@ -988,6 +1033,26 @@ "schema": { "type": "number" } + }, + { + "name": "take", + "required": false, + "in": "query", + "description": "The number of items to return", + "example": 10, + "schema": { + "type": "number" + } + }, + { + "name": "skip", + "required": false, + "in": "query", + "description": "The number of items to skip", + "example": 0, + "schema": { + "type": "number" + } } ], "responses": { @@ -1902,6 +1967,46 @@ ] } }, + "/v2/bookings/{bookingUid}/mark-no-show": { + "post": { + "operationId": "BookingsController_markNoShow", + "parameters": [ + { + "name": "bookingUid", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MarkNoShowInput" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MarkNoShowOutput" + } + } + } + } + }, + "tags": [ + "Bookings" + ] + } + }, "/v2/bookings/recurring": { "post": { "operationId": "BookingsController_createRecurringBooking", @@ -3700,6 +3805,125 @@ "data" ] }, + "OrgTeamOutputDto": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "parentId": { + "type": "number" + }, + "name": { + "type": "string", + "minLength": 1 + }, + "slug": { + "type": "string" + }, + "logoUrl": { + "type": "string" + }, + "calVideoLogo": { + "type": "string" + }, + "appLogo": { + "type": "string" + }, + "appIconLogo": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "hideBranding": { + "type": "boolean" + }, + "isOrganization": { + "type": "boolean" + }, + "isPrivate": { + "type": "boolean" + }, + "hideBookATeamMember": { + "type": "boolean", + "default": false + }, + "metadata": { + "type": "string" + }, + "theme": { + "type": "string" + }, + "brandColor": { + "type": "string" + }, + "darkBrandColor": { + "type": "string" + }, + "bannerUrl": { + "type": "string" + }, + "timeFormat": { + "type": "number" + }, + "timeZone": { + "type": "string", + "default": "Europe/London" + }, + "weekStart": { + "type": "string", + "default": "Sunday" + } + }, + "required": [ + "id", + "name" + ] + }, + "OrgTeamsOutputResponseDto": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OrgTeamOutputDto" + } + } + }, + "required": [ + "status", + "data" + ] + }, + "OrgTeamOutputResponseDto": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] + }, + "data": { + "$ref": "#/components/schemas/OrgTeamOutputDto" + } + }, + "required": [ + "status", + "data" + ] + }, "CreateOrgTeamDto": { "type": "object", "properties": { @@ -5461,6 +5685,63 @@ "seatReferenceUid" ] }, + "MarkNoShowInput": { + "type": "object", + "properties": { + "noShowHost": { + "type": "boolean" + }, + "attendees": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Attendee" + } + } + } + }, + "HandleMarkNoShowData": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "noShowHost": { + "type": "boolean" + }, + "messageKey": { + "type": "string" + }, + "attendees": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Attendee" + } + } + }, + "required": [ + "message" + ] + }, + "MarkNoShowOutput": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] + }, + "data": { + "$ref": "#/components/schemas/HandleMarkNoShowData" + } + }, + "required": [ + "status", + "data" + ] + }, "ReserveSlotInput": { "type": "object", "properties": {} diff --git a/packages/platform/types/api.ts b/packages/platform/types/api.ts index 0b4302264e..f18909605c 100644 --- a/packages/platform/types/api.ts +++ b/packages/platform/types/api.ts @@ -1,3 +1,4 @@ +import { ApiProperty } from "@nestjs/swagger"; import { Transform } from "class-transformer"; import { IsNumber, Min, Max, IsOptional } from "class-validator"; import type { Response as BaseResponse } from "express"; @@ -41,6 +42,7 @@ export type ApiResponse = T extends undefined export type ApiResponseMaybeRedirect = ApiResponse | ApiRedirectResponseType; export class Pagination { + @ApiProperty({ required: false, description: "The number of items to return", example: 10 }) @Transform(({ value }: { value: string }) => value && parseInt(value)) @IsNumber() @Min(1) @@ -49,6 +51,7 @@ export class Pagination { limit?: number; @Transform(({ value }: { value: string }) => value && parseInt(value)) + @ApiProperty({ required: false, description: "The number of items to skip", example: 0 }) @IsNumber() @Min(0) @Max(100) @@ -57,6 +60,7 @@ export class Pagination { } export class SkipTakePagination { + @ApiProperty({ required: false, description: "The number of items to return", example: 10 }) @Transform(({ value }: { value: string }) => value && parseInt(value)) @IsNumber() @Min(1) @@ -64,6 +68,7 @@ export class SkipTakePagination { @IsOptional() take?: number; + @ApiProperty({ required: false, description: "The number of items to skip", example: 0 }) @Transform(({ value }: { value: string }) => value && parseInt(value)) @IsNumber() @Min(0)