-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into feat/automatic-no-show
- Loading branch information
Showing
51 changed files
with
2,441 additions
and
219 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
apps/api/v2/src/modules/conferencing/conferencing.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { ConferencingController } from "@/modules/conferencing/controllers/conferencing.controller"; | ||
import { ConferencingRepository } from "@/modules/conferencing/repositories/conferencing.respository"; | ||
import { ConferencingService } from "@/modules/conferencing/services/conferencing.service"; | ||
import { GoogleMeetService } from "@/modules/conferencing/services/google-meet.service"; | ||
import { CredentialsRepository } from "@/modules/credentials/credentials.repository"; | ||
import { PrismaModule } from "@/modules/prisma/prisma.module"; | ||
import { UsersRepository } from "@/modules/users/users.repository"; | ||
import { Module } from "@nestjs/common"; | ||
|
||
@Module({ | ||
imports: [PrismaModule], | ||
providers: [ | ||
ConferencingService, | ||
ConferencingRepository, | ||
GoogleMeetService, | ||
CredentialsRepository, | ||
UsersRepository, | ||
], | ||
exports: [], | ||
controllers: [ConferencingController], | ||
}) | ||
export class ConferencingModule {} |
137 changes: 137 additions & 0 deletions
137
apps/api/v2/src/modules/conferencing/controllers/conferencing.controller.e2e-spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import { bootstrap } from "@/app"; | ||
import { AppModule } from "@/app.module"; | ||
import { ConferencingAppsOutputDto } from "@/modules/conferencing/outputs/get-conferencing-apps.output"; | ||
import { PrismaModule } from "@/modules/prisma/prisma.module"; | ||
import { TokensModule } from "@/modules/tokens/tokens.module"; | ||
import { UsersModule } from "@/modules/users/users.module"; | ||
import { INestApplication } from "@nestjs/common"; | ||
import { NestExpressApplication } from "@nestjs/platform-express"; | ||
import { Test } from "@nestjs/testing"; | ||
import { User } from "@prisma/client"; | ||
import * as request from "supertest"; | ||
import { CredentialsRepositoryFixture } from "test/fixtures/repository/credentials.repository.fixture"; | ||
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture"; | ||
import { withApiAuth } from "test/utils/withApiAuth"; | ||
|
||
import { | ||
ERROR_STATUS, | ||
GOOGLE_CALENDAR_ID, | ||
GOOGLE_CALENDAR_TYPE, | ||
GOOGLE_MEET, | ||
GOOGLE_MEET_TYPE, | ||
SUCCESS_STATUS, | ||
} from "@calcom/platform-constants"; | ||
import { ApiErrorResponse, ApiSuccessResponse } from "@calcom/platform-types"; | ||
|
||
describe("Conferencing Endpoints", () => { | ||
describe("conferencing controller e2e tests", () => { | ||
let app: INestApplication; | ||
|
||
let userRepositoryFixture: UserRepositoryFixture; | ||
let credentialsRepositoryFixture: CredentialsRepositoryFixture; | ||
|
||
const userEmail = "conferencing-controller-user-e2e@api.com"; | ||
let user: User; | ||
|
||
beforeAll(async () => { | ||
const moduleRef = await withApiAuth( | ||
userEmail, | ||
Test.createTestingModule({ | ||
imports: [AppModule, PrismaModule, UsersModule, TokensModule], | ||
}) | ||
).compile(); | ||
|
||
userRepositoryFixture = new UserRepositoryFixture(moduleRef); | ||
credentialsRepositoryFixture = new CredentialsRepositoryFixture(moduleRef); | ||
|
||
user = await userRepositoryFixture.create({ | ||
email: userEmail, | ||
username: userEmail, | ||
}); | ||
|
||
app = moduleRef.createNestApplication(); | ||
bootstrap(app as NestExpressApplication); | ||
|
||
await app.init(); | ||
}); | ||
|
||
it("should get all the conferencing apps of the auth user", async () => { | ||
return request(app.getHttpServer()) | ||
.get(`/v2/conferencing`) | ||
.expect(200) | ||
.then((response) => { | ||
const responseBody: ApiSuccessResponse<ConferencingAppsOutputDto[]> = response.body; | ||
expect(responseBody.status).toEqual(SUCCESS_STATUS); | ||
expect(responseBody.data).toEqual([]); | ||
}); | ||
}); | ||
|
||
it("should fail to connect google meet if google calendar is not connected ", async () => { | ||
return request(app.getHttpServer()) | ||
.post(`/v2/conferencing/google-meet/connect`) | ||
.expect(400) | ||
.then(async () => { | ||
await credentialsRepositoryFixture.create(GOOGLE_CALENDAR_TYPE, {}, user.id, GOOGLE_CALENDAR_ID); | ||
}); | ||
}); | ||
|
||
it("should connect google meet if google calendar is connected ", async () => { | ||
return request(app.getHttpServer()) | ||
.post(`/v2/conferencing/google-meet/connect`) | ||
.expect(200) | ||
.then((response) => { | ||
const responseBody: ApiSuccessResponse<ConferencingAppsOutputDto[]> = response.body; | ||
expect(responseBody.status).toEqual(SUCCESS_STATUS); | ||
}); | ||
}); | ||
|
||
it("should set google meet as default conferencing app", async () => { | ||
return request(app.getHttpServer()) | ||
.post(`/v2/conferencing/google-meet/default`) | ||
.expect(200) | ||
.then(async () => { | ||
const updatedUser = await userRepositoryFixture.get(user.id); | ||
|
||
expect(updatedUser).toBeDefined(); | ||
|
||
if (updatedUser) { | ||
const metadata = updatedUser.metadata as { defaultConferencingApp?: { appSlug?: string } }; | ||
expect(metadata?.defaultConferencingApp?.appSlug).toEqual(GOOGLE_MEET); | ||
} | ||
}); | ||
}); | ||
|
||
it("should get all the conferencing apps of the auth user, and contain google meet", async () => { | ||
return request(app.getHttpServer()) | ||
.get(`/v2/conferencing`) | ||
.expect(200) | ||
.then((response) => { | ||
const responseBody: ApiSuccessResponse<ConferencingAppsOutputDto[]> = response.body; | ||
expect(responseBody.status).toEqual(SUCCESS_STATUS); | ||
const googleMeet = responseBody.data.find((app) => app.type === GOOGLE_MEET_TYPE); | ||
expect(googleMeet?.userId).toEqual(user.id); | ||
}); | ||
}); | ||
|
||
it("should disconnect google meet", async () => { | ||
return request(app.getHttpServer()).delete(`/v2/conferencing/google-meet/disconnect`).expect(200); | ||
}); | ||
|
||
it("should get all the conferencing apps of the auth user, and not contain google meet", async () => { | ||
return request(app.getHttpServer()) | ||
.get(`/v2/conferencing`) | ||
.expect(200) | ||
.then((response) => { | ||
const responseBody: ApiSuccessResponse<ConferencingAppsOutputDto[]> = response.body; | ||
expect(responseBody.status).toEqual(SUCCESS_STATUS); | ||
const googleMeet = responseBody.data.find((app) => app.type === GOOGLE_MEET_TYPE); | ||
expect(googleMeet).toBeUndefined(); | ||
}); | ||
}); | ||
|
||
afterAll(async () => { | ||
await userRepositoryFixture.deleteByEmail(user.email); | ||
await app.close(); | ||
}); | ||
}); | ||
}); |
121 changes: 121 additions & 0 deletions
121
apps/api/v2/src/modules/conferencing/controllers/conferencing.controller.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import { API_VERSIONS_VALUES } from "@/lib/api-versions"; | ||
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator"; | ||
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard"; | ||
import { | ||
ConferencingAppsOutputResponseDto, | ||
ConferencingAppOutputResponseDto, | ||
ConferencingAppsOutputDto, | ||
} from "@/modules/conferencing/outputs/get-conferencing-apps.output"; | ||
import { SetDefaultConferencingAppOutputResponseDto } from "@/modules/conferencing/outputs/set-default-conferencing-app.output"; | ||
import { ConferencingService } from "@/modules/conferencing/services/conferencing.service"; | ||
import { GoogleMeetService } from "@/modules/conferencing/services/google-meet.service"; | ||
import { | ||
Controller, | ||
Get, | ||
HttpCode, | ||
HttpStatus, | ||
Logger, | ||
UseGuards, | ||
Post, | ||
Param, | ||
BadRequestException, | ||
Delete, | ||
} from "@nestjs/common"; | ||
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger"; | ||
import { plainToInstance } from "class-transformer"; | ||
|
||
import { CONFERENCING_APPS, GOOGLE_MEET, SUCCESS_STATUS } from "@calcom/platform-constants"; | ||
|
||
@Controller({ | ||
path: "/v2/conferencing", | ||
version: API_VERSIONS_VALUES, | ||
}) | ||
@DocsTags("Platform / Conferencing") | ||
export class ConferencingController { | ||
private readonly logger = new Logger("Platform Gcal Provider"); | ||
|
||
constructor( | ||
private readonly conferencingService: ConferencingService, | ||
private readonly googleMeetService: GoogleMeetService | ||
) {} | ||
|
||
@Post("/:app/connect") | ||
@HttpCode(HttpStatus.OK) | ||
@UseGuards(ApiAuthGuard) | ||
@ApiOperation({ summary: "Connect your conferencing application" }) | ||
async connect( | ||
@GetUser("id") userId: number, | ||
@Param("app") app: string | ||
): Promise<ConferencingAppOutputResponseDto> { | ||
switch (app) { | ||
case GOOGLE_MEET: | ||
const credential = await this.googleMeetService.connectGoogleMeetApp(userId); | ||
|
||
return { status: SUCCESS_STATUS, data: plainToInstance(ConferencingAppsOutputDto, credential) }; | ||
|
||
default: | ||
throw new BadRequestException( | ||
"Invalid conferencing app, available apps are: ", | ||
CONFERENCING_APPS.join(", ") | ||
); | ||
} | ||
} | ||
|
||
@Get("/") | ||
@HttpCode(HttpStatus.OK) | ||
@UseGuards(ApiAuthGuard) | ||
@ApiOperation({ summary: "List your conferencing applications" }) | ||
async listConferencingApps(@GetUser("id") userId: number): Promise<ConferencingAppsOutputResponseDto> { | ||
const conferencingApps = await this.conferencingService.getConferencingApps(userId); | ||
|
||
const data = conferencingApps.map((conferencingApps) => | ||
plainToInstance(ConferencingAppsOutputDto, conferencingApps) | ||
); | ||
|
||
return { status: SUCCESS_STATUS, data }; | ||
} | ||
|
||
@Post("/:app/default") | ||
@HttpCode(HttpStatus.OK) | ||
@UseGuards(ApiAuthGuard) | ||
@ApiOperation({ summary: "Set your default conferencing application" }) | ||
async default( | ||
@GetUser("id") userId: number, | ||
@Param("app") app: string | ||
): Promise<SetDefaultConferencingAppOutputResponseDto> { | ||
switch (app) { | ||
case GOOGLE_MEET: | ||
await this.googleMeetService.setDefault(userId); | ||
|
||
return { status: SUCCESS_STATUS }; | ||
|
||
default: | ||
throw new BadRequestException( | ||
"Invalid conferencing app, available apps are: ", | ||
CONFERENCING_APPS.join(", ") | ||
); | ||
} | ||
} | ||
|
||
@Delete("/:app/disconnect") | ||
@HttpCode(HttpStatus.OK) | ||
@UseGuards(ApiAuthGuard) | ||
@ApiOperation({ summary: "Disconnect your conferencing application" }) | ||
async disconnect( | ||
@GetUser("id") userId: number, | ||
@Param("app") app: string | ||
): Promise<ConferencingAppOutputResponseDto> { | ||
switch (app) { | ||
case GOOGLE_MEET: | ||
const credential = await this.googleMeetService.disconnectGoogleMeetApp(userId); | ||
|
||
return { status: SUCCESS_STATUS, data: plainToInstance(ConferencingAppsOutputDto, credential) }; | ||
|
||
default: | ||
throw new BadRequestException( | ||
"Invalid conferencing app, available apps are: ", | ||
CONFERENCING_APPS.join(", ") | ||
); | ||
} | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
apps/api/v2/src/modules/conferencing/outputs/get-conferencing-apps.output.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { ApiProperty } from "@nestjs/swagger"; | ||
import { Expose, Type } from "class-transformer"; | ||
import { IsString, ValidateNested, IsEnum, IsNumber, IsOptional, IsBoolean } from "class-validator"; | ||
|
||
import { ERROR_STATUS, GOOGLE_MEET_TYPE, SUCCESS_STATUS } from "@calcom/platform-constants"; | ||
|
||
export class ConferencingAppsOutputDto { | ||
@Expose() | ||
@IsNumber() | ||
@ApiProperty({ description: "Id of the conferencing app credentials" }) | ||
id!: number; | ||
|
||
@ApiProperty({ example: GOOGLE_MEET_TYPE, description: "Type of conferencing app" }) | ||
@Expose() | ||
@IsString() | ||
type!: string; | ||
|
||
@ApiProperty({ description: "Id of the user associated to the conferencing app" }) | ||
@Expose() | ||
@IsNumber() | ||
userId!: number; | ||
|
||
@ApiProperty({ example: true, description: "Whether if the connection is working or not." }) | ||
@Expose() | ||
@IsBoolean() | ||
@IsOptional() | ||
invalid?: boolean | null; | ||
} | ||
|
||
export class ConferencingAppsOutputResponseDto { | ||
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] }) | ||
@IsEnum([SUCCESS_STATUS, ERROR_STATUS]) | ||
status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS; | ||
|
||
@Expose() | ||
@ValidateNested() | ||
@Type(() => ConferencingAppsOutputDto) | ||
data!: ConferencingAppsOutputDto[]; | ||
} | ||
|
||
export class ConferencingAppOutputResponseDto { | ||
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] }) | ||
@IsEnum([SUCCESS_STATUS, ERROR_STATUS]) | ||
status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS; | ||
|
||
@Expose() | ||
@ValidateNested() | ||
@Type(() => ConferencingAppsOutputDto) | ||
data!: ConferencingAppsOutputDto; | ||
} |
10 changes: 10 additions & 0 deletions
10
apps/api/v2/src/modules/conferencing/outputs/set-default-conferencing-app.output.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { ApiProperty } from "@nestjs/swagger"; | ||
import { IsEnum } from "class-validator"; | ||
|
||
import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants"; | ||
|
||
export class SetDefaultConferencingAppOutputResponseDto { | ||
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] }) | ||
@IsEnum([SUCCESS_STATUS, ERROR_STATUS]) | ||
status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS; | ||
} |
25 changes: 25 additions & 0 deletions
25
apps/api/v2/src/modules/conferencing/repositories/conferencing.respository.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service"; | ||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service"; | ||
import { Injectable } from "@nestjs/common"; | ||
|
||
import { GOOGLE_MEET_TYPE } from "@calcom/platform-constants"; | ||
|
||
@Injectable() | ||
export class ConferencingRepository { | ||
constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {} | ||
|
||
async findConferencingApps(userId: number) { | ||
return this.dbRead.prisma.credential.findMany({ | ||
where: { | ||
userId, | ||
type: { endsWith: "_video" }, | ||
}, | ||
}); | ||
} | ||
|
||
async findGoogleMeet(userId: number) { | ||
return this.dbRead.prisma.credential.findFirst({ | ||
where: { userId, type: GOOGLE_MEET_TYPE }, | ||
}); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
apps/api/v2/src/modules/conferencing/services/conferencing.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { ConferencingRepository } from "@/modules/conferencing/repositories/conferencing.respository"; | ||
import { Logger } from "@nestjs/common"; | ||
import { Injectable } from "@nestjs/common"; | ||
|
||
@Injectable() | ||
export class ConferencingService { | ||
private logger = new Logger("ConferencingService"); | ||
|
||
constructor(private readonly conferencingRepository: ConferencingRepository) {} | ||
|
||
async getConferencingApps(userId: number) { | ||
return this.conferencingRepository.findConferencingApps(userId); | ||
} | ||
} |
Oops, something went wrong.