-
Notifications
You must be signed in to change notification settings - Fork 7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: user schedule management (#13053)
* platform-constants package: list of accepted schedule timezones * feat: schedules endpoint to create schedule with default availability * refactor: rename function * refactor: store userId for availabilities * feat: createSchedule endpoint * feat: get schedules/default * feat: getSchedule by id * feat: get all schedules * feat: delete schedule * feat: update schedule * check user owns schedule * empty test * define returned data on controller level not repository * define returned data on controller level not repository * Revert "define returned data on controller level not repository" This reverts commit 4c292a0. * use luxton * put availabilities out of ee * use guard on controller level * refactor * e2e test schedule creation * remove log * test * default schedule get test * update schedule test * delete schedule test * fix update test * different email for schedules e2e * driveby: fix yarn test * schedule inputs availabilities as array * re-use BaseStrategy class
- Loading branch information
1 parent
9a7772e
commit bea0e89
Showing
25 changed files
with
1,247 additions
and
23 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
329 changes: 329 additions & 0 deletions
329
apps/api/v2/src/ee/schedules/controllers/schedules.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,329 @@ | ||
import { bootstrap } from "@/app"; | ||
import { AppModule } from "@/app.module"; | ||
import { SchedulesRepository } from "@/ee/schedules/schedules.repository"; | ||
import { SchedulesService } from "@/ee/schedules/services/schedules.service"; | ||
import { ScheduleResponse } from "@/ee/schedules/zod/response/response"; | ||
import { AvailabilitiesModule } from "@/modules/availabilities/availabilities.module"; | ||
import { PrismaModule } from "@/modules/prisma/prisma.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 { SchedulesRepositoryFixture } from "test/fixtures/repository/schedules.repository.fixture"; | ||
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture"; | ||
import { withAccessTokenAuth } from "test/utils/withAccessTokenAuth"; | ||
|
||
import { SUCCESS_STATUS } from "@calcom/platform-constants"; | ||
import { ApiSuccessResponse } from "@calcom/platform-types"; | ||
|
||
describe("Schedules Endpoints", () => { | ||
describe("User Authentication", () => { | ||
let app: INestApplication; | ||
|
||
let userRepositoryFixture: UserRepositoryFixture; | ||
let scheduleRepositoryFixture: SchedulesRepositoryFixture; | ||
|
||
const userEmail = "schedules-controller-e2e@api.com"; | ||
let user: User; | ||
|
||
let createdSchedule: ScheduleResponse; | ||
|
||
beforeAll(async () => { | ||
const moduleRef = await withAccessTokenAuth( | ||
userEmail, | ||
Test.createTestingModule({ | ||
imports: [AppModule, PrismaModule, AvailabilitiesModule, UsersModule], | ||
providers: [SchedulesRepository, SchedulesService], | ||
}) | ||
).compile(); | ||
|
||
userRepositoryFixture = new UserRepositoryFixture(moduleRef); | ||
scheduleRepositoryFixture = new SchedulesRepositoryFixture(moduleRef); | ||
user = await userRepositoryFixture.create({ | ||
email: userEmail, | ||
}); | ||
|
||
app = moduleRef.createNestApplication(); | ||
bootstrap(app as NestExpressApplication); | ||
|
||
await app.init(); | ||
}); | ||
|
||
it("should be defined", () => { | ||
expect(userRepositoryFixture).toBeDefined(); | ||
expect(user).toBeDefined(); | ||
}); | ||
|
||
it("should create a default schedule", async () => { | ||
const scheduleName = "schedule-name"; | ||
const scheduleTimeZone = "Europe/Rome"; | ||
|
||
const body = { | ||
name: scheduleName, | ||
timeZone: scheduleTimeZone, | ||
}; | ||
|
||
return request(app.getHttpServer()) | ||
.post("/api/v2/schedules") | ||
.send(body) | ||
.expect(201) | ||
.then(async (response) => { | ||
const responseBody: ApiSuccessResponse<{ schedule: ScheduleResponse }> = response.body; | ||
expect(responseBody.status).toEqual(SUCCESS_STATUS); | ||
expect(responseBody.data).toBeDefined(); | ||
|
||
expect(responseBody.data.schedule).toBeDefined(); | ||
expect(responseBody.data.schedule.id).toBeDefined(); | ||
expect(responseBody.data.schedule.userId).toEqual(user.id); | ||
expect(responseBody.data.schedule.name).toEqual(scheduleName); | ||
expect(responseBody.data.schedule.timeZone).toEqual(scheduleTimeZone); | ||
|
||
expect(responseBody.data.schedule.availability).toBeDefined(); | ||
expect(responseBody.data.schedule.availability?.length).toEqual(1); | ||
const defaultAvailabilityDays = [1, 2, 3, 4, 5]; | ||
const defaultAvailabilityStartTime = "09:00:00"; | ||
const defaultAvailabilityEndTime = "17:00:00"; | ||
|
||
expect(responseBody.data.schedule.availability?.[0]?.days).toEqual(defaultAvailabilityDays); | ||
expect(responseBody.data.schedule.availability?.[0]?.startTime).toEqual( | ||
defaultAvailabilityStartTime | ||
); | ||
expect(responseBody.data.schedule.availability?.[0]?.endTime).toEqual(defaultAvailabilityEndTime); | ||
|
||
const scheduleUser = await userRepositoryFixture.get(responseBody.data.schedule.userId); | ||
expect(scheduleUser?.defaultScheduleId).toEqual(responseBody.data.schedule.id); | ||
await scheduleRepositoryFixture.deleteById(responseBody.data.schedule.id); | ||
await scheduleRepositoryFixture.deleteAvailabilities(responseBody.data.schedule.id); | ||
}); | ||
}); | ||
|
||
it("should create a schedule", async () => { | ||
const scheduleName = "schedule-name"; | ||
const scheduleTimeZone = "Europe/Rome"; | ||
const availabilityDays = [1, 2, 3, 4, 5, 6]; | ||
const availabilityStartTime = "11:00:00"; | ||
const availabilityEndTime = "14:00:00"; | ||
|
||
const body = { | ||
name: scheduleName, | ||
timeZone: scheduleTimeZone, | ||
availabilities: [ | ||
{ | ||
days: availabilityDays, | ||
startTime: availabilityStartTime, | ||
endTime: availabilityEndTime, | ||
}, | ||
], | ||
}; | ||
|
||
return request(app.getHttpServer()) | ||
.post("/api/v2/schedules") | ||
.send(body) | ||
.expect(201) | ||
.then(async (response) => { | ||
const responseBody: ApiSuccessResponse<{ schedule: ScheduleResponse }> = response.body; | ||
expect(responseBody.status).toEqual(SUCCESS_STATUS); | ||
expect(responseBody.data).toBeDefined(); | ||
|
||
expect(responseBody.data.schedule).toBeDefined(); | ||
expect(responseBody.data.schedule.id).toBeDefined(); | ||
expect(responseBody.data.schedule.userId).toEqual(user.id); | ||
expect(responseBody.data.schedule.name).toEqual(scheduleName); | ||
expect(responseBody.data.schedule.timeZone).toEqual(scheduleTimeZone); | ||
|
||
expect(responseBody.data.schedule.availability).toBeDefined(); | ||
expect(responseBody.data.schedule.availability?.length).toEqual(1); | ||
expect(responseBody.data.schedule.availability?.[0]?.days).toEqual(availabilityDays); | ||
expect(responseBody.data.schedule.availability?.[0]?.startTime).toEqual(availabilityStartTime); | ||
expect(responseBody.data.schedule.availability?.[0]?.endTime).toEqual(availabilityEndTime); | ||
|
||
createdSchedule = responseBody.data.schedule; | ||
|
||
const scheduleUser = await userRepositoryFixture.get(responseBody.data.schedule.userId); | ||
expect(scheduleUser?.defaultScheduleId).toEqual(responseBody.data.schedule.id); | ||
}); | ||
}); | ||
|
||
it("should get default schedule", async () => { | ||
return request(app.getHttpServer()) | ||
.get("/api/v2/schedules/default") | ||
.expect(200) | ||
.then((response) => { | ||
const responseBody: ApiSuccessResponse<{ schedule: ScheduleResponse }> = response.body; | ||
expect(responseBody.status).toEqual(SUCCESS_STATUS); | ||
expect(responseBody.data).toBeDefined(); | ||
|
||
expect(responseBody.data.schedule).toBeDefined(); | ||
expect(responseBody.data.schedule.id).toBeDefined(); | ||
expect(responseBody.data.schedule.userId).toEqual(createdSchedule.userId); | ||
expect(responseBody.data.schedule.name).toEqual(createdSchedule.name); | ||
expect(responseBody.data.schedule.timeZone).toEqual(createdSchedule.timeZone); | ||
|
||
expect(responseBody.data.schedule.availability).toBeDefined(); | ||
expect(responseBody.data.schedule.availability?.length).toEqual(1); | ||
|
||
expect(responseBody.data.schedule.availability?.[0]?.days).toEqual( | ||
createdSchedule.availability?.[0]?.days | ||
); | ||
expect(responseBody.data.schedule.availability?.[0]?.startTime).toEqual( | ||
createdSchedule.availability?.[0]?.startTime | ||
); | ||
expect(responseBody.data.schedule.availability?.[0]?.endTime).toEqual( | ||
createdSchedule.availability?.[0]?.endTime | ||
); | ||
}); | ||
}); | ||
|
||
it("should get schedule", async () => { | ||
return request(app.getHttpServer()) | ||
.get(`/api/v2/schedules/${createdSchedule.id}`) | ||
.expect(200) | ||
.then((response) => { | ||
const responseBody: ApiSuccessResponse<{ schedule: ScheduleResponse }> = response.body; | ||
expect(responseBody.status).toEqual(SUCCESS_STATUS); | ||
expect(responseBody.data).toBeDefined(); | ||
|
||
expect(responseBody.data.schedule).toBeDefined(); | ||
expect(responseBody.data.schedule.id).toBeDefined(); | ||
expect(responseBody.data.schedule.userId).toEqual(createdSchedule.userId); | ||
expect(responseBody.data.schedule.name).toEqual(createdSchedule.name); | ||
expect(responseBody.data.schedule.timeZone).toEqual(createdSchedule.timeZone); | ||
|
||
expect(responseBody.data.schedule.availability).toBeDefined(); | ||
expect(responseBody.data.schedule.availability?.length).toEqual(1); | ||
|
||
expect(responseBody.data.schedule.availability?.[0]?.days).toEqual( | ||
createdSchedule.availability?.[0]?.days | ||
); | ||
expect(responseBody.data.schedule.availability?.[0]?.startTime).toEqual( | ||
createdSchedule.availability?.[0]?.startTime | ||
); | ||
expect(responseBody.data.schedule.availability?.[0]?.endTime).toEqual( | ||
createdSchedule.availability?.[0]?.endTime | ||
); | ||
}); | ||
}); | ||
|
||
it("should get schedules", async () => { | ||
return request(app.getHttpServer()) | ||
.get(`/api/v2/schedules`) | ||
.expect(200) | ||
.then((response) => { | ||
const responseBody: ApiSuccessResponse<{ schedules: ScheduleResponse[] }> = response.body; | ||
expect(responseBody.status).toEqual(SUCCESS_STATUS); | ||
expect(responseBody.data).toBeDefined(); | ||
|
||
expect(responseBody.data.schedules).toBeDefined(); | ||
expect(responseBody.data.schedules.length).toEqual(1); | ||
|
||
const fetchedSchedule = responseBody.data.schedules[0]; | ||
expect(fetchedSchedule).toBeDefined(); | ||
expect(fetchedSchedule.userId).toEqual(createdSchedule.userId); | ||
expect(fetchedSchedule.name).toEqual(createdSchedule.name); | ||
expect(fetchedSchedule.timeZone).toEqual(createdSchedule.timeZone); | ||
|
||
expect(fetchedSchedule.availability).toBeDefined(); | ||
expect(fetchedSchedule.availability?.length).toEqual(1); | ||
|
||
expect(fetchedSchedule.availability?.[0]?.days).toEqual(createdSchedule.availability?.[0]?.days); | ||
expect(fetchedSchedule.availability?.[0]?.startTime).toEqual( | ||
createdSchedule.availability?.[0]?.startTime | ||
); | ||
expect(fetchedSchedule.availability?.[0]?.endTime).toEqual( | ||
createdSchedule.availability?.[0]?.endTime | ||
); | ||
}); | ||
}); | ||
|
||
it("should update schedule name", async () => { | ||
const newScheduleName = "new-schedule-name"; | ||
|
||
const body = { | ||
name: newScheduleName, | ||
}; | ||
|
||
return request(app.getHttpServer()) | ||
.put(`/api/v2/schedules/${createdSchedule.id}`) | ||
.send(body) | ||
.expect(200) | ||
.then((response) => { | ||
const responseBody: ApiSuccessResponse<{ schedule: ScheduleResponse }> = response.body; | ||
expect(responseBody.status).toEqual(SUCCESS_STATUS); | ||
expect(responseBody.data).toBeDefined(); | ||
|
||
expect(responseBody.data.schedule).toBeDefined(); | ||
expect(responseBody.data.schedule.id).toBeDefined(); | ||
expect(responseBody.data.schedule.userId).toEqual(createdSchedule.userId); | ||
expect(responseBody.data.schedule.name).toEqual(newScheduleName); | ||
expect(responseBody.data.schedule.timeZone).toEqual(createdSchedule.timeZone); | ||
|
||
expect(responseBody.data.schedule.availability).toBeDefined(); | ||
expect(responseBody.data.schedule.availability?.length).toEqual(1); | ||
|
||
expect(responseBody.data.schedule.availability?.[0]?.days).toEqual( | ||
createdSchedule.availability?.[0]?.days | ||
); | ||
expect(responseBody.data.schedule.availability?.[0]?.startTime).toEqual( | ||
createdSchedule.availability?.[0]?.startTime | ||
); | ||
expect(responseBody.data.schedule.availability?.[0]?.endTime).toEqual( | ||
createdSchedule.availability?.[0]?.endTime | ||
); | ||
|
||
createdSchedule = responseBody.data.schedule; | ||
}); | ||
}); | ||
|
||
it("should update schedule availabilities", async () => { | ||
const newAvailabilityDays = [2, 4]; | ||
const newAvailabilityStartTime = "19:00:00"; | ||
const newAvailabilityEndTime = "20:00:00"; | ||
|
||
const body = { | ||
availabilities: [ | ||
{ | ||
days: newAvailabilityDays, | ||
startTime: newAvailabilityStartTime, | ||
endTime: newAvailabilityEndTime, | ||
}, | ||
], | ||
}; | ||
|
||
return request(app.getHttpServer()) | ||
.put(`/api/v2/schedules/${createdSchedule.id}`) | ||
.send(body) | ||
.expect(200) | ||
.then((response) => { | ||
const responseBody: ApiSuccessResponse<{ schedule: ScheduleResponse }> = response.body; | ||
expect(responseBody.status).toEqual(SUCCESS_STATUS); | ||
expect(responseBody.data).toBeDefined(); | ||
|
||
expect(responseBody.data.schedule).toBeDefined(); | ||
expect(responseBody.data.schedule.id).toBeDefined(); | ||
expect(responseBody.data.schedule.userId).toEqual(createdSchedule.userId); | ||
expect(responseBody.data.schedule.name).toEqual(createdSchedule.name); | ||
expect(responseBody.data.schedule.timeZone).toEqual(createdSchedule.timeZone); | ||
|
||
expect(responseBody.data.schedule.availability).toBeDefined(); | ||
expect(responseBody.data.schedule.availability?.length).toEqual(1); | ||
|
||
expect(responseBody.data.schedule.availability?.[0]?.days).toEqual(newAvailabilityDays); | ||
expect(responseBody.data.schedule.availability?.[0]?.startTime).toEqual(newAvailabilityStartTime); | ||
expect(responseBody.data.schedule.availability?.[0]?.endTime).toEqual(newAvailabilityEndTime); | ||
|
||
createdSchedule = responseBody.data.schedule; | ||
}); | ||
}); | ||
|
||
it("should delete schedule", async () => { | ||
return request(app.getHttpServer()).delete(`/api/v2/schedules/${createdSchedule.id}`).expect(200); | ||
}); | ||
|
||
afterAll(async () => { | ||
await userRepositoryFixture.deleteByEmail(user.email); | ||
await app.close(); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.