* chore: Upgrade prisma to 6.7.0 * Build fixes * type fixes Signed-off-by: Omar López <zomars@me.com> * Update schema.prisma * Patching * Revert "Update schema.prisma" This reverts commit 47d8618bf89ef4d007b30084df766f17281e21a1. * Revert "Patching" This reverts commit a1d2e3040e71690a44d4324db95d73b4d68c6adb. * Revert schema changes Signed-off-by: Omar López <zomars@me.com> * WIP Signed-off-by: Omar López <zomars@me.com> * Update getPublicEvent.ts * Update imports Signed-off-by: Omar López <zomars@me.com> * Update gitignore Signed-off-by: Omar López <zomars@me.com> * update remaining imports Signed-off-by: Omar López <zomars@me.com> * Delete .cursor/config.json * Discard changes to packages/features/eventtypes/lib/getPublicEvent.ts * Update _get.ts * Update user.ts * Update .gitignore * update * Update WorkflowStepContainer.tsx * Update next-auth-custom-adapter.ts * Update getPublicEvent.ts * Update workflow.ts * Update next-auth-custom-adapter.ts * Update next-auth-options.ts * Update bookingScenario.ts * fix missing imports * upgrades prismock Signed-off-by: Omar López <zomars@me.com> * patches prismock Signed-off-by: Omar López <zomars@me.com> * Update reschedule.test.ts * Update prisma.ts * patch prismock Signed-off-by: Omar López <zomars@me.com> * fix enums imports Signed-off-by: Omar López <zomars@me.com> * Revert "Update prisma.ts" This reverts commit 64edcf8db54171ff4456c209d563b5d431d99619. * Revert "patch prismock" This reverts commit e95819113dc9d88e7130947aa120cd42710977c8. * fix patch * Fix test that overrun the boundary, it shouldn't test too much * Move prisma import to changeSMSLockState * Bring back broken test without illegal imports * Merge with main and fix filter hosts by same round robin host * Fixed buildDryRunBooking fn tests * Fix and move ooo create or update handler test * Fix packages/features/eventtypes/lib/isCurrentlyAvailable.test.ts * Fix packages/trpc/server/routers/viewer/organizations/listMembers.handler.test.ts * Mock @calcom/prisma * Fix: verify-email.test.ts * fix: Moved WebhookService test and fixed default import mock * Fix: Added missing prisma mock, handleNewBooking uses that of course * We're not testing createContext here * fix: Prisma mock fix for listMembers.test.ts * More fixes to broken testcases * Forgot to remove borked test * Prevent the need to mock a lot of dependencies by moving out buildBaseWhereCondition to its own file * Temporarily skip getCalendarEvents, needs a rewrite * Fix: turns out you can access protected in testcases * fix further mocks * Added packages/features/insights/server/buildBaseWhereCondition.ts, types * Always great to have a mock and then not use it * And one less again. * fix: confirm.handler.test, didn't mock prisma * fix: Address minor nit by @eunjae & fix ImpersonationProvider test * Updated isPrismaAvailableCheck that doesn't crash on import * fix: Get Prisma directly from the client, it usually involves the Validator and does not need 'local' inclusion * Add zod-prisma-types without the generator enabled (commented out) * Uncomment and see what happens * Change method of import as imports did not work in Input Schemas * Remove custom 'zod' booking model, it does not belong with Prisma * Fix all other global Model imports * Rewrite most schema includes AND remove barrel file * Add bookingCreateBodySchema to features/bookings * Flurry of type fixes for compatibility with new zod gen * Refactor out the custom prisma type createEventTypeInput * Work around nullable eventTypeLocations * HandlePayment type fix * More fixes, final fix remaining is CompleteEventType * Should fix a bunch more booking related type errors * Missed one * Some props missing from BookingCreateBodySchema * Fix location type in handleChildrenEventTypes * Little bit hacky imo but it works * Final type error \o/ * Forgot to include Prisma * Do not include zod-utils in booker/types * Oops, was already including Booker/types * Fix membership type, also disallow updating createdAt/updatedAt, make part of patch/post * Fix api v1 type errors * Fix EventTypeDescription typings * Remove getParserWithGeneric, use userBodySchema with UserSchema * use centralized timeZoneSchema * Implement feedback by @zomars * Couple of WIP pushes * Fix tests * Type fixes in `handleChildrenEventTypes` test * Try and parse metadata before use * Change zod-prisma-types configuration for optimal performance * Fix prisma validator error in `prisma/selects/credential` * Disable seperate relations model, hits a bug * Import absolute - this makes rollup work in @platform/libraries * Attempt at removing resolutions override * Refactor using `Prisma.validator` to `satisfies` * Build atoms using @calcom/prisma/client * Build atoms using @calcom/prisma/client * fixes * Update eventTypeSelect.ts * Adjust `eventTypeMetaDataSchemaWithUntypedApps` from `unknown` to `record(any)` * `EventTypeDescription` rely on `descriptionAsSafeHTML` instead of `description` * Add `seatsPerTimeSlot` to event type public select * Fix typing in `users-public-view` getServerSide props * Add missing `schedulingType` to prop * chore: bump platform libraries * Function return type is illegal, not sure how this passed eslint (#21567) * Merged with main * Update updateTokenObject.ts * Update handleResponse.ts * Update index.ts * Update handleChildrenEventTypes.ts * Update booking-idempotency-key.ts * Update WebhookService.test.ts * Update events.test.ts * Update queued-response.test.ts * Update events.test.ts * Update getRoutedUrl.test.ts * fix: type checks Signed-off-by: Omar López <zomars@me.com> * fixes Signed-off-by: Omar López <zomars@me.com> * chore: bump platform libraries * Update yarn.lock * more fixes Signed-off-by: Omar López <zomars@me.com> * fixes Signed-off-by: Omar López <zomars@me.com> * biuld fixes * chore: bump platform libraries * Update conferencing.repository.ts * Update conferencing.repository.ts * Update getCalendarsEvents.test.ts * Update vite.config.js * chore: bump platform libraries * Update users.ts * Discard changes to docs/api-reference/v2/openapi.json * Update vite.config.ts * updated platform libraries * Update get.handler.test.ts * Update get.handler.test.ts * Update schema.prisma * Discard changes to docs/api-reference/v2/openapi.json * Update next-auth-custom-adapter.ts * Update team.ts * Flurry of type fixes * Fix majority of insight related type errors * Type fixes for unlink of account * Make user nullable again * Fixed a bunch of unit tests and one type error * Attempted mock fix * Attempted fix for Attribute type * Ensure default import becomes prisma, but not direct usage * Import default as prisma in prisma.module * Add attributeOption to attribute type * Fix calcom/prisma mock * Refactor Prisma client imports to @calcom/prisma/client Updated all imports from '@prisma/client' to '@calcom/prisma/client' across tests and repository files for consistency and to use the correct Prisma client package. This change improves maintainability and ensures the correct client is referenced throughout the codebase. * Undo removal of max-warnings=0 to get main to merge * Remove unit tests for e2e fixtures, provide new prisma mock * Mock @calcom/prisma in event manager * Mock @calcom/prisma in event manager * Add correct format even with --no-verify * Mock prisma in CalendarManager * Add mock for permission-check.service * Better injection in PrismaApiKeyRepository imports * More mock fixes :) * Fix listMembers.handler.test * Fix User import * Appropriately adjust all types to be imported as types, there were a lot of types imported as normal deps * Why was this a thing? * Strictly speaking; Not using prismock anymore * Ditched patch file for prismock * Fix output.service.ts platform type imports, need concrete for plainToClass * Better typing and tests for unlinkConnectedAccount.handler * Small type fix * Disable calendar cache tests as they are dependent on prismock * chore: bump platform lib * getRoutedUrl test remove of unused import * Extract select to external const on getEventTypesFromDB * Direct select of userSelect from selects/user * fix type error from merging 23653 * Fixed integration tests by removing hardcoded values that were possible due to mocking, but as its now directly hitting the db no longer * fix: vite config atoms prisma client type location * revert: example app prisma client * revert: example app prisma client * bump platform libs * fix: use class instead of type for DI of PlatformBookingsService * update platform libs * remove unused variable * chore: generate prisma client for api v2 * fix: api v2 e2e * fix: atoms e2e * fix: atoms e2e * fix: atoms e2e * fix: api v2 e2e * fix: tsconfig apiv2 enums * publish libraries * Simplify check for existence teamId --------- Signed-off-by: Omar López <zomars@me.com> Co-authored-by: Alex van Andel <me@alexvanandel.com> Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com> Co-authored-by: supalarry <laurisskraucis@gmail.com> Co-authored-by: cal.com <morgan@cal.com> Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com> Co-authored-by: Benny Joo <sldisek783@gmail.com>
933 lines
39 KiB
TypeScript
933 lines
39 KiB
TypeScript
import { bootstrap } from "@/app";
|
|
import { AppModule } from "@/app.module";
|
|
import { DEFAULT_EVENT_TYPES } from "@/ee/event-types/event-types_2024_04_15/constants/constants";
|
|
import { HttpExceptionFilter } from "@/filters/http-exception.filter";
|
|
import { PrismaExceptionFilter } from "@/filters/prisma-exception.filter";
|
|
import { Locales } from "@/lib/enums/locales";
|
|
import { CreateManagedUserOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/create-managed-user.output";
|
|
import { GetManagedUserOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/get-managed-user.output";
|
|
import { GetManagedUsersOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/get-managed-users.output";
|
|
import { KeysResponseDto } from "@/modules/oauth-clients/controllers/oauth-flow/responses/KeysResponse.dto";
|
|
import { OAuthClientUsersService } from "@/modules/oauth-clients/services/oauth-clients-users.service";
|
|
import { CreateManagedUserInput } from "@/modules/users/inputs/create-managed-user.input";
|
|
import { UpdateManagedUserInput } from "@/modules/users/inputs/update-managed-user.input";
|
|
import { UsersModule } from "@/modules/users/users.module";
|
|
import { INestApplication } from "@nestjs/common";
|
|
import { JwtService } from "@nestjs/jwt";
|
|
import { NestExpressApplication } from "@nestjs/platform-express";
|
|
import { Test } from "@nestjs/testing";
|
|
import * as request from "supertest";
|
|
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
|
|
import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture";
|
|
import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
|
|
import { ProfileRepositoryFixture } from "test/fixtures/repository/profiles.repository.fixture";
|
|
import { SchedulesRepositoryFixture } from "test/fixtures/repository/schedules.repository.fixture";
|
|
import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
|
|
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
|
|
import { randomString } from "test/utils/randomString";
|
|
|
|
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
|
import { slugify } from "@calcom/platform-libraries";
|
|
import type { ApiSuccessResponse } from "@calcom/platform-types";
|
|
import type { PlatformOAuthClient, Team, User } from "@calcom/prisma/client";
|
|
|
|
const CLIENT_REDIRECT_URI = "http://localhost:4321";
|
|
|
|
describe("OAuth Client Users Endpoints", () => {
|
|
describe("Not authenticated", () => {
|
|
let app: INestApplication;
|
|
|
|
beforeAll(async () => {
|
|
const moduleRef = await Test.createTestingModule({
|
|
providers: [PrismaExceptionFilter, HttpExceptionFilter],
|
|
imports: [AppModule, UsersModule],
|
|
}).compile();
|
|
|
|
app = moduleRef.createNestApplication();
|
|
bootstrap(app as NestExpressApplication);
|
|
await app.init();
|
|
});
|
|
|
|
describe("secret header not set", () => {
|
|
it(`/POST`, () => {
|
|
return request(app.getHttpServer())
|
|
.post("/api/v2/oauth-clients/100/users")
|
|
.send({ email: "bob@gmail.com" })
|
|
.expect(401);
|
|
});
|
|
});
|
|
|
|
describe("Bearer access token not set", () => {
|
|
it(`/GET/:id`, () => {
|
|
return request(app.getHttpServer()).get("/api/v2/oauth-clients/100/users/200").expect(401);
|
|
});
|
|
it(`/PUT/:id`, () => {
|
|
return request(app.getHttpServer()).patch("/api/v2/oauth-clients/100/users/200").expect(401);
|
|
});
|
|
it(`/DELETE/:id`, () => {
|
|
return request(app.getHttpServer()).delete("/api/v2/oauth-clients/100/users/200").expect(401);
|
|
});
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await app.close();
|
|
});
|
|
});
|
|
|
|
describe("User Authenticated", () => {
|
|
let app: INestApplication;
|
|
|
|
let oAuthClient: PlatformOAuthClient;
|
|
let oAuthClientEventTypesDisabled: PlatformOAuthClient;
|
|
let organization: Team;
|
|
let userRepositoryFixture: UserRepositoryFixture;
|
|
let oauthClientRepositoryFixture: OAuthClientRepositoryFixture;
|
|
let teamRepositoryFixture: TeamRepositoryFixture;
|
|
let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
|
|
let schedulesRepositoryFixture: SchedulesRepositoryFixture;
|
|
let profilesRepositoryFixture: ProfileRepositoryFixture;
|
|
let membershipsRepositoryFixture: MembershipRepositoryFixture;
|
|
|
|
let postResponseData: CreateManagedUserOutput["data"];
|
|
let postResponseData2: CreateManagedUserOutput["data"];
|
|
|
|
const platformAdminEmail = `oauth-client-users-admin-${randomString()}@api.com`;
|
|
let platformAdmin: User;
|
|
|
|
const userEmail = `oauth-client-users-user-${randomString(5)}@api.com`;
|
|
const userTimeZone = "Europe/Rome";
|
|
const userEmailTwo = `oauth-client-users-user-2-${randomString(5)}@api.com`;
|
|
const userTimeZoneTwo = "Europe/Rome";
|
|
let postResponseDataTwo: CreateManagedUserOutput["data"];
|
|
|
|
const userEmail2 = `oauth-client-users-user2-${randomString()}@api.com`;
|
|
const userTimeZone2 = "America/New_York";
|
|
|
|
beforeAll(async () => {
|
|
const moduleRef = await Test.createTestingModule({
|
|
providers: [PrismaExceptionFilter, HttpExceptionFilter],
|
|
imports: [AppModule, UsersModule],
|
|
}).compile();
|
|
|
|
app = moduleRef.createNestApplication();
|
|
bootstrap(app as NestExpressApplication);
|
|
|
|
oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef);
|
|
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
|
|
teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
|
|
eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef);
|
|
schedulesRepositoryFixture = new SchedulesRepositoryFixture(moduleRef);
|
|
profilesRepositoryFixture = new ProfileRepositoryFixture(moduleRef);
|
|
membershipsRepositoryFixture = new MembershipRepositoryFixture(moduleRef);
|
|
|
|
platformAdmin = await userRepositoryFixture.create({ email: platformAdminEmail });
|
|
|
|
organization = await teamRepositoryFixture.create({
|
|
name: `oauth-client-users-organization-${randomString()}`,
|
|
isPlatform: true,
|
|
isOrganization: true,
|
|
});
|
|
oAuthClient = await createOAuthClient(organization.id);
|
|
oAuthClientEventTypesDisabled = await createOAuthClient(organization.id, false);
|
|
|
|
await profilesRepositoryFixture.create({
|
|
uid: "asd1qwwqeqw-asddsadasd",
|
|
username: platformAdminEmail,
|
|
organization: { connect: { id: organization.id } },
|
|
user: {
|
|
connect: { id: platformAdmin.id },
|
|
},
|
|
});
|
|
|
|
await membershipsRepositoryFixture.create({
|
|
role: "OWNER",
|
|
user: { connect: { id: platformAdmin.id } },
|
|
team: { connect: { id: organization.id } },
|
|
accepted: true,
|
|
});
|
|
|
|
await app.init();
|
|
});
|
|
|
|
async function createOAuthClient(organizationId: number, areDefaultEventTypesEnabled?: boolean) {
|
|
const data = {
|
|
logo: "logo-url",
|
|
name: "name",
|
|
redirectUris: [CLIENT_REDIRECT_URI],
|
|
permissions: 32,
|
|
areDefaultEventTypesEnabled,
|
|
};
|
|
const secret = "secret";
|
|
|
|
const client = await oauthClientRepositoryFixture.create(organizationId, data, secret);
|
|
return client;
|
|
}
|
|
|
|
it("should be defined", () => {
|
|
expect(oauthClientRepositoryFixture).toBeDefined();
|
|
expect(userRepositoryFixture).toBeDefined();
|
|
expect(oAuthClient).toBeDefined();
|
|
});
|
|
|
|
it(`should fail /POST with incorrect timeZone`, async () => {
|
|
const requestBody: CreateManagedUserInput = {
|
|
email: userEmail,
|
|
timeZone: "incorrect-time-zone",
|
|
name: "Alice Smith",
|
|
avatarUrl: "https://cal.com/api/avatar/2b735186-b01b-46d3-87da-019b8f61776b.png",
|
|
};
|
|
|
|
await request(app.getHttpServer())
|
|
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.send(requestBody)
|
|
.expect(400);
|
|
});
|
|
|
|
it(`should fail /POST with incorrect timeFormat`, async () => {
|
|
const requestBody = {
|
|
email: userEmail,
|
|
timeZone: userTimeZone,
|
|
name: "Alice Smith",
|
|
timeFormat: 100,
|
|
};
|
|
|
|
await request(app.getHttpServer())
|
|
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.send(requestBody)
|
|
.expect(400);
|
|
});
|
|
|
|
it(`/POST with default event types`, async () => {
|
|
const requestBody: CreateManagedUserInput = {
|
|
email: userEmail,
|
|
timeZone: userTimeZone,
|
|
weekStart: "Monday",
|
|
timeFormat: 24,
|
|
locale: Locales.FR,
|
|
name: "Alice Smith",
|
|
avatarUrl: "https://cal.com/api/avatar/2b735186-b01b-46d3-87da-019b8f61776b.png",
|
|
bio: "I am a bio",
|
|
metadata: {
|
|
key: "value",
|
|
},
|
|
};
|
|
|
|
const response = await request(app.getHttpServer())
|
|
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.send(requestBody)
|
|
.expect(201);
|
|
|
|
const responseBody: CreateManagedUserOutput = response.body;
|
|
|
|
postResponseData = responseBody.data;
|
|
|
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
|
expect(responseBody.data).toBeDefined();
|
|
expect(responseBody.data.user.email).toEqual(
|
|
OAuthClientUsersService.getOAuthUserEmail(oAuthClient.id, requestBody.email)
|
|
);
|
|
expect(responseBody.data.user.timeZone).toEqual(requestBody.timeZone);
|
|
expect(responseBody.data.user.name).toEqual(requestBody.name);
|
|
expect(responseBody.data.user.weekStart).toEqual(requestBody.weekStart);
|
|
expect(responseBody.data.user.timeFormat).toEqual(requestBody.timeFormat);
|
|
expect(responseBody.data.user.locale).toEqual(requestBody.locale);
|
|
expect(responseBody.data.user.avatarUrl).toEqual(requestBody.avatarUrl);
|
|
expect(responseBody.data.user.bio).toEqual(requestBody.bio);
|
|
expect(responseBody.data.user.metadata).toEqual(requestBody.metadata);
|
|
const [emailUser, emailDomain] = responseBody.data.user.email.split("@");
|
|
const [domainName, TLD] = emailDomain.split(".");
|
|
expect(responseBody.data.user.username).toEqual(slugify(`${emailUser}-${domainName}-${TLD}`));
|
|
|
|
const { accessToken, refreshToken, accessTokenExpiresAt, refreshTokenExpiresAt } = response.body.data;
|
|
expect(accessToken).toBeDefined();
|
|
expect(refreshToken).toBeDefined();
|
|
expect(accessTokenExpiresAt).toBeDefined();
|
|
expect(refreshTokenExpiresAt).toBeDefined();
|
|
|
|
const jwtService = app.get(JwtService);
|
|
const decodedAccessToken = jwtService.decode(accessToken);
|
|
const decodedRefreshToken = jwtService.decode(refreshToken);
|
|
|
|
expect(decodedAccessToken.clientId).toBe(oAuthClient.id);
|
|
expect(decodedAccessToken.ownerId).toBeDefined();
|
|
expect(decodedAccessToken.type).toBe("access_token");
|
|
expect(decodedAccessToken.expiresAt).toBe(new Date(accessTokenExpiresAt).valueOf());
|
|
expect(decodedAccessToken.iat).toBeGreaterThan(0);
|
|
|
|
expect(decodedRefreshToken.clientId).toBe(oAuthClient.id);
|
|
expect(decodedRefreshToken.ownerId).toBeDefined();
|
|
expect(decodedRefreshToken.type).toBe("refresh_token");
|
|
expect(decodedRefreshToken.expiresAt).toBe(new Date(refreshTokenExpiresAt).valueOf());
|
|
expect(decodedRefreshToken.iat).toBeGreaterThan(0);
|
|
|
|
await userConnectedToOAuth(oAuthClient.id, responseBody.data.user.email, 1);
|
|
await userHasDefaultEventTypes(responseBody.data.user.id);
|
|
await userHasDefaultSchedule(responseBody.data.user.id, responseBody.data.user.defaultScheduleId);
|
|
await userHasOnlyOneSchedule(responseBody.data.user.id);
|
|
});
|
|
|
|
it(`/POST with default event types`, async () => {
|
|
const requestBody: CreateManagedUserInput = {
|
|
email: userEmailTwo,
|
|
timeZone: userTimeZoneTwo,
|
|
weekStart: "Monday",
|
|
timeFormat: 24,
|
|
locale: Locales.FR,
|
|
name: "Bob Smith",
|
|
avatarUrl: "https://cal.com/api/avatar/2b735186-b01b-46d3-87da-019b8f61776b.png",
|
|
bio: "I am a bio",
|
|
metadata: {
|
|
key: "value",
|
|
},
|
|
};
|
|
|
|
const response = await request(app.getHttpServer())
|
|
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.send(requestBody)
|
|
.expect(201);
|
|
|
|
const responseBody: CreateManagedUserOutput = response.body;
|
|
|
|
postResponseDataTwo = responseBody.data;
|
|
|
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
|
expect(responseBody.data).toBeDefined();
|
|
expect(responseBody.data.user.email).toEqual(
|
|
OAuthClientUsersService.getOAuthUserEmail(oAuthClient.id, requestBody.email)
|
|
);
|
|
expect(responseBody.data.user.timeZone).toEqual(requestBody.timeZone);
|
|
expect(responseBody.data.user.name).toEqual(requestBody.name);
|
|
expect(responseBody.data.user.weekStart).toEqual(requestBody.weekStart);
|
|
expect(responseBody.data.user.timeFormat).toEqual(requestBody.timeFormat);
|
|
expect(responseBody.data.user.locale).toEqual(requestBody.locale);
|
|
expect(responseBody.data.user.avatarUrl).toEqual(requestBody.avatarUrl);
|
|
|
|
const { accessToken, refreshToken, accessTokenExpiresAt, refreshTokenExpiresAt } = response.body.data;
|
|
expect(accessToken).toBeDefined();
|
|
expect(refreshToken).toBeDefined();
|
|
expect(accessTokenExpiresAt).toBeDefined();
|
|
expect(refreshTokenExpiresAt).toBeDefined();
|
|
|
|
const jwtService = app.get(JwtService);
|
|
const decodedAccessToken = jwtService.decode(accessToken);
|
|
const decodedRefreshToken = jwtService.decode(refreshToken);
|
|
|
|
expect(decodedAccessToken.clientId).toBe(oAuthClient.id);
|
|
expect(decodedAccessToken.ownerId).toBeDefined();
|
|
expect(decodedAccessToken.type).toBe("access_token");
|
|
expect(decodedAccessToken.expiresAt).toBe(new Date(accessTokenExpiresAt).valueOf());
|
|
expect(decodedAccessToken.iat).toBeGreaterThan(0);
|
|
|
|
expect(decodedRefreshToken.clientId).toBe(oAuthClient.id);
|
|
expect(decodedRefreshToken.ownerId).toBeDefined();
|
|
expect(decodedRefreshToken.type).toBe("refresh_token");
|
|
expect(decodedRefreshToken.expiresAt).toBe(new Date(refreshTokenExpiresAt).valueOf());
|
|
expect(decodedRefreshToken.iat).toBeGreaterThan(0);
|
|
|
|
await userConnectedToOAuth(oAuthClient.id, responseBody.data.user.email, 2);
|
|
await userHasDefaultEventTypes(responseBody.data.user.id);
|
|
await userHasDefaultSchedule(responseBody.data.user.id, responseBody.data.user.defaultScheduleId);
|
|
await userHasOnlyOneSchedule(responseBody.data.user.id);
|
|
});
|
|
|
|
it(`/POST without default event types`, async () => {
|
|
const requestBody: CreateManagedUserInput = {
|
|
email: userEmail2,
|
|
timeZone: userTimeZone2,
|
|
weekStart: "Monday",
|
|
timeFormat: 24,
|
|
locale: Locales.FR,
|
|
name: "Alice Smith",
|
|
avatarUrl: "https://cal.com/api/avatar/2b735186-b01b-46d3-87da-019b8f61776b.png",
|
|
bio: "I am a bio",
|
|
metadata: {
|
|
key: "value",
|
|
},
|
|
};
|
|
|
|
const response = await request(app.getHttpServer())
|
|
.post(`/api/v2/oauth-clients/${oAuthClientEventTypesDisabled.id}/users`)
|
|
.set("x-cal-secret-key", oAuthClientEventTypesDisabled.secret)
|
|
.send(requestBody)
|
|
.expect(201);
|
|
|
|
const responseBody: CreateManagedUserOutput = response.body;
|
|
|
|
postResponseData2 = responseBody.data;
|
|
|
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
|
expect(responseBody.data).toBeDefined();
|
|
expect(responseBody.data.user.email).toEqual(
|
|
OAuthClientUsersService.getOAuthUserEmail(oAuthClientEventTypesDisabled.id, requestBody.email)
|
|
);
|
|
expect(responseBody.data.user.timeZone).toEqual(requestBody.timeZone);
|
|
expect(responseBody.data.user.name).toEqual(requestBody.name);
|
|
expect(responseBody.data.user.weekStart).toEqual(requestBody.weekStart);
|
|
expect(responseBody.data.user.timeFormat).toEqual(requestBody.timeFormat);
|
|
expect(responseBody.data.user.locale).toEqual(requestBody.locale);
|
|
expect(responseBody.data.user.avatarUrl).toEqual(requestBody.avatarUrl);
|
|
|
|
const { accessToken, refreshToken, accessTokenExpiresAt, refreshTokenExpiresAt } = response.body.data;
|
|
expect(accessToken).toBeDefined();
|
|
expect(refreshToken).toBeDefined();
|
|
expect(accessTokenExpiresAt).toBeDefined();
|
|
expect(refreshTokenExpiresAt).toBeDefined();
|
|
|
|
const jwtService = app.get(JwtService);
|
|
const decodedAccessToken = jwtService.decode(accessToken);
|
|
const decodedRefreshToken = jwtService.decode(refreshToken);
|
|
|
|
expect(decodedAccessToken.clientId).toBe(oAuthClientEventTypesDisabled.id);
|
|
expect(decodedAccessToken.ownerId).toBeDefined();
|
|
expect(decodedAccessToken.type).toBe("access_token");
|
|
expect(decodedAccessToken.expiresAt).toBe(new Date(accessTokenExpiresAt).valueOf());
|
|
expect(decodedAccessToken.iat).toBeGreaterThan(0);
|
|
|
|
expect(decodedRefreshToken.clientId).toBe(oAuthClientEventTypesDisabled.id);
|
|
expect(decodedRefreshToken.ownerId).toBeDefined();
|
|
expect(decodedRefreshToken.type).toBe("refresh_token");
|
|
expect(decodedRefreshToken.expiresAt).toBe(new Date(refreshTokenExpiresAt).valueOf());
|
|
expect(decodedRefreshToken.iat).toBeGreaterThan(0);
|
|
|
|
await userConnectedToOAuth(oAuthClientEventTypesDisabled.id, responseBody.data.user.email, 1);
|
|
await userDoesNotHaveDefaultEventTypes(responseBody.data.user.id);
|
|
await userHasDefaultSchedule(responseBody.data.user.id, responseBody.data.user.defaultScheduleId);
|
|
await userHasOnlyOneSchedule(responseBody.data.user.id);
|
|
});
|
|
|
|
async function userConnectedToOAuth(oAuthClientId: string, userEmail: string, usersCount: number) {
|
|
const oAuthUsers = await oauthClientRepositoryFixture.getUsers(oAuthClientId);
|
|
const newOAuthUser = oAuthUsers?.find((user) => user.email === userEmail);
|
|
|
|
expect(oAuthUsers?.length).toEqual(usersCount);
|
|
expect(newOAuthUser?.email).toEqual(userEmail);
|
|
}
|
|
|
|
async function userHasDefaultEventTypes(userId: number) {
|
|
const defaultEventTypes = await eventTypesRepositoryFixture.getAllUserEventTypes(userId);
|
|
|
|
// note(Lauris): to determine count see default event types created in EventTypesService.createUserDefaultEventTypes
|
|
expect(defaultEventTypes?.length).toEqual(4);
|
|
expect(
|
|
defaultEventTypes?.find((eventType) => eventType.slug === DEFAULT_EVENT_TYPES.thirtyMinutes.slug)
|
|
).toBeTruthy();
|
|
expect(
|
|
defaultEventTypes?.find((eventType) => eventType.slug === DEFAULT_EVENT_TYPES.sixtyMinutes.slug)
|
|
).toBeTruthy();
|
|
expect(
|
|
defaultEventTypes?.find((eventType) => eventType.slug === DEFAULT_EVENT_TYPES.thirtyMinutesVideo.slug)
|
|
).toBeTruthy();
|
|
expect(
|
|
defaultEventTypes?.find((eventType) => eventType.slug === DEFAULT_EVENT_TYPES.sixtyMinutesVideo.slug)
|
|
).toBeTruthy();
|
|
}
|
|
|
|
async function userDoesNotHaveDefaultEventTypes(userId: number) {
|
|
const defaultEventTypes = await eventTypesRepositoryFixture.getAllUserEventTypes(userId);
|
|
expect(defaultEventTypes?.length).toEqual(0);
|
|
}
|
|
|
|
async function userHasDefaultSchedule(userId: number, scheduleId: number | null) {
|
|
expect(scheduleId).toBeDefined();
|
|
expect(scheduleId).not.toBeNull();
|
|
|
|
const user = await userRepositoryFixture.get(userId);
|
|
expect(user?.defaultScheduleId).toEqual(scheduleId);
|
|
|
|
const schedule = scheduleId ? await schedulesRepositoryFixture.getById(scheduleId) : null;
|
|
expect(schedule?.userId).toEqual(userId);
|
|
}
|
|
|
|
async function userHasOnlyOneSchedule(userId: number) {
|
|
const schedules = await schedulesRepositoryFixture.getByUserId(userId);
|
|
expect(schedules?.length).toEqual(1);
|
|
}
|
|
|
|
it(`should fail /POST using already used managed user email`, async () => {
|
|
const requestBody: CreateManagedUserInput = {
|
|
email: userEmail,
|
|
timeZone: userTimeZone,
|
|
weekStart: "Monday",
|
|
timeFormat: 24,
|
|
locale: Locales.FR,
|
|
name: "Alice Smith",
|
|
avatarUrl: "https://cal.com/api/avatar/2b735186-b01b-46d3-87da-019b8f61776b.png",
|
|
};
|
|
|
|
const response = await request(app.getHttpServer())
|
|
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.send(requestBody)
|
|
.expect(409);
|
|
|
|
const responseBody: CreateManagedUserOutput = response.body;
|
|
const error = responseBody.error;
|
|
expect(error).toBeDefined();
|
|
expect(error?.message).toEqual(
|
|
`User with the provided e-mail already exists. Existing user ID=${postResponseData.user.id}`
|
|
);
|
|
});
|
|
|
|
it(`/GET: return list of managed users`, async () => {
|
|
const response = await request(app.getHttpServer())
|
|
.get(`/api/v2/oauth-clients/${oAuthClient.id}/users?limit=10&offset=0`)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.set("Origin", `${CLIENT_REDIRECT_URI}`)
|
|
.expect(200);
|
|
|
|
const responseBody: GetManagedUsersOutput = response.body;
|
|
|
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
|
expect(responseBody.data).toBeDefined();
|
|
expect(responseBody.data?.length).toBeGreaterThan(0);
|
|
expect(responseBody.data[0].email).toEqual(postResponseData.user.email);
|
|
expect(responseBody.data[0].name).toEqual(postResponseData.user.name);
|
|
expect(responseBody.data?.length).toEqual(2);
|
|
const userOne = responseBody.data.find((user) => user.email === postResponseData.user.email);
|
|
const userTwo = responseBody.data.find((user) => user.email === postResponseDataTwo.user.email);
|
|
expect(userOne?.email).toEqual(postResponseData.user.email);
|
|
expect(userOne?.name).toEqual(postResponseData.user.name);
|
|
expect(userTwo?.email).toEqual(postResponseDataTwo.user.email);
|
|
expect(userTwo?.name).toEqual(postResponseDataTwo.user.name);
|
|
expect(userOne?.bio).toEqual(postResponseData.user.bio);
|
|
expect(userOne?.metadata).toEqual(postResponseData.user.metadata);
|
|
expect(userTwo?.bio).toEqual(postResponseDataTwo.user.bio);
|
|
expect(userTwo?.metadata).toEqual(postResponseDataTwo.user.metadata);
|
|
});
|
|
|
|
it(`/GET: managed user by original email`, async () => {
|
|
const response = await request(app.getHttpServer())
|
|
.get(`/api/v2/oauth-clients/${oAuthClient.id}/users?limit=10&offset=0&emails=${userEmail}`)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.set("Origin", `${CLIENT_REDIRECT_URI}`)
|
|
.expect(200);
|
|
|
|
const responseBody: GetManagedUsersOutput = response.body;
|
|
|
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
|
expect(responseBody.data).toBeDefined();
|
|
expect(responseBody.data?.length).toEqual(1);
|
|
const userOne = responseBody.data.find((user) => user.email === postResponseData.user.email);
|
|
expect(userOne?.email).toEqual(postResponseData.user.email);
|
|
expect(userOne?.name).toEqual(postResponseData.user.name);
|
|
expect(userOne?.bio).toEqual(postResponseData.user.bio);
|
|
expect(userOne?.metadata).toEqual(postResponseData.user.metadata);
|
|
});
|
|
|
|
it(`/GET: managed users by original emails`, async () => {
|
|
const response = await request(app.getHttpServer())
|
|
.get(
|
|
`/api/v2/oauth-clients/${oAuthClient.id}/users?limit=10&offset=0&emails=${userEmail},${userEmailTwo}`
|
|
)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.set("Origin", `${CLIENT_REDIRECT_URI}`)
|
|
.expect(200);
|
|
|
|
const responseBody: GetManagedUsersOutput = response.body;
|
|
|
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
|
expect(responseBody.data).toBeDefined();
|
|
expect(responseBody.data?.length).toEqual(2);
|
|
const userOne = responseBody.data.find((user) => user.email === postResponseData.user.email);
|
|
const userTwo = responseBody.data.find((user) => user.email === postResponseDataTwo.user.email);
|
|
expect(userOne?.email).toEqual(postResponseData.user.email);
|
|
expect(userOne?.name).toEqual(postResponseData.user.name);
|
|
expect(userTwo?.email).toEqual(postResponseDataTwo.user.email);
|
|
expect(userTwo?.name).toEqual(postResponseDataTwo.user.name);
|
|
expect(userOne?.bio).toEqual(postResponseData.user.bio);
|
|
expect(userOne?.metadata).toEqual(postResponseData.user.metadata);
|
|
expect(userTwo?.bio).toEqual(postResponseDataTwo.user.bio);
|
|
expect(userTwo?.metadata).toEqual(postResponseDataTwo.user.metadata);
|
|
});
|
|
|
|
it(`/GET: managed user by oAuth email`, async () => {
|
|
const response = await request(app.getHttpServer())
|
|
// note(Lauris): we use encodeURIComponent because email stored on our side includes "+" which without encoding becomes an empty space.
|
|
.get(
|
|
`/api/v2/oauth-clients/${oAuthClient.id}/users?limit=10&offset=0&emails=${encodeURIComponent(
|
|
postResponseData.user.email
|
|
)}`
|
|
)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.set("Origin", `${CLIENT_REDIRECT_URI}`)
|
|
.expect(200);
|
|
|
|
const responseBody: GetManagedUsersOutput = response.body;
|
|
|
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
|
expect(responseBody.data).toBeDefined();
|
|
expect(responseBody.data?.length).toEqual(1);
|
|
const userOne = responseBody.data.find((user) => user.email === postResponseData.user.email);
|
|
expect(userOne?.email).toEqual(postResponseData.user.email);
|
|
expect(userOne?.name).toEqual(postResponseData.user.name);
|
|
expect(userOne?.bio).toEqual(postResponseData.user.bio);
|
|
expect(userOne?.metadata).toEqual(postResponseData.user.metadata);
|
|
});
|
|
|
|
it(`should error /GET if managed user email is invalid`, async () => {
|
|
const invalidEmail = "invalid-email";
|
|
const response = await request(app.getHttpServer())
|
|
.get(`/api/v2/oauth-clients/${oAuthClient.id}/users?limit=10&offset=0&emails=${invalidEmail}`)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.set("Origin", `${CLIENT_REDIRECT_URI}`)
|
|
.expect(400);
|
|
|
|
expect(response.body?.error?.message).toEqual(`Invalid email ${invalidEmail}`);
|
|
});
|
|
|
|
it(`/GET: managed users by oAuth emails`, async () => {
|
|
const response = await request(app.getHttpServer())
|
|
// note(Lauris): we use encodeURIComponent because email stored on our side includes "+" which without encoding becomes an empty space.
|
|
.get(
|
|
`/api/v2/oauth-clients/${oAuthClient.id}/users?limit=10&offset=0&emails=${encodeURIComponent(
|
|
postResponseData.user.email
|
|
)},${encodeURIComponent(postResponseDataTwo.user.email)}`
|
|
)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.set("Origin", `${CLIENT_REDIRECT_URI}`)
|
|
.expect(200);
|
|
|
|
const responseBody: GetManagedUsersOutput = response.body;
|
|
|
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
|
expect(responseBody.data).toBeDefined();
|
|
expect(responseBody.data?.length).toEqual(2);
|
|
const userOne = responseBody.data.find((user) => user.email === postResponseData.user.email);
|
|
const userTwo = responseBody.data.find((user) => user.email === postResponseDataTwo.user.email);
|
|
expect(userOne?.email).toEqual(postResponseData.user.email);
|
|
expect(userOne?.name).toEqual(postResponseData.user.name);
|
|
expect(userTwo?.email).toEqual(postResponseDataTwo.user.email);
|
|
expect(userTwo?.name).toEqual(postResponseDataTwo.user.name);
|
|
expect(userOne?.bio).toEqual(postResponseData.user.bio);
|
|
expect(userOne?.metadata).toEqual(postResponseData.user.metadata);
|
|
expect(userTwo?.bio).toEqual(postResponseDataTwo.user.bio);
|
|
expect(userTwo?.metadata).toEqual(postResponseDataTwo.user.metadata);
|
|
});
|
|
|
|
it(`/GET/:id`, async () => {
|
|
const response = await request(app.getHttpServer())
|
|
.get(`/api/v2/oauth-clients/${oAuthClient.id}/users/${postResponseData.user.id}`)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.set("Origin", `${CLIENT_REDIRECT_URI}`)
|
|
.expect(200);
|
|
|
|
const responseBody: GetManagedUserOutput = response.body;
|
|
|
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
|
expect(responseBody.data).toBeDefined();
|
|
expect(responseBody.data.email).toEqual(
|
|
OAuthClientUsersService.getOAuthUserEmail(oAuthClient.id, userEmail)
|
|
);
|
|
expect(responseBody.data.name).toEqual(postResponseData.user.name);
|
|
expect(responseBody.data.bio).toEqual(postResponseData.user.bio);
|
|
expect(responseBody.data.metadata).toEqual(postResponseData.user.metadata);
|
|
});
|
|
|
|
it(`/PUT/:id`, async () => {
|
|
const userUpdatedEmail = "pineapple-pizza@gmail.com";
|
|
const body: UpdateManagedUserInput = { email: userUpdatedEmail, locale: Locales.PT_BR };
|
|
|
|
const response = await request(app.getHttpServer())
|
|
.patch(`/api/v2/oauth-clients/${oAuthClient.id}/users/${postResponseData.user.id}`)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.set("Origin", `${CLIENT_REDIRECT_URI}`)
|
|
.send(body)
|
|
.expect(200);
|
|
|
|
const responseBody: ApiSuccessResponse<Omit<User, "password">> = response.body;
|
|
|
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
|
expect(responseBody.data).toBeDefined();
|
|
expect(responseBody.data.email).toEqual(
|
|
OAuthClientUsersService.getOAuthUserEmail(oAuthClient.id, userUpdatedEmail)
|
|
);
|
|
expect(responseBody.data.name).toEqual(postResponseData.user.name);
|
|
expect(responseBody.data.bio).toEqual(postResponseData.user.bio);
|
|
expect(responseBody.data.metadata).toEqual(postResponseData.user.metadata);
|
|
const [emailUser, emailDomain] = responseBody.data.email.split("@");
|
|
const [domainName, TLD] = emailDomain.split(".");
|
|
expect(responseBody.data.username).toEqual(slugify(`${emailUser}-${domainName}-${TLD}`));
|
|
expect(responseBody.data.locale).toEqual(Locales.PT_BR);
|
|
});
|
|
|
|
it("should force refresh tokens", async () => {
|
|
const response = await request(app.getHttpServer())
|
|
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users/${postResponseData.user.id}/force-refresh`)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.expect(200);
|
|
|
|
const responseBody: KeysResponseDto = response.body;
|
|
|
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
|
expect(responseBody.data).toBeDefined();
|
|
expect(responseBody.data.accessToken).toBeDefined();
|
|
expect(responseBody.data.accessTokenExpiresAt).toBeDefined();
|
|
|
|
const { accessToken, refreshToken, accessTokenExpiresAt, refreshTokenExpiresAt } = response.body.data;
|
|
expect(accessToken).toBeDefined();
|
|
expect(refreshToken).toBeDefined();
|
|
expect(accessTokenExpiresAt).toBeDefined();
|
|
expect(refreshTokenExpiresAt).toBeDefined();
|
|
|
|
const jwtService = app.get(JwtService);
|
|
const decodedAccessToken = jwtService.decode(accessToken);
|
|
const decodedRefreshToken = jwtService.decode(refreshToken);
|
|
|
|
expect(decodedAccessToken.clientId).toBe(oAuthClient.id);
|
|
expect(decodedAccessToken.ownerId).toBe(postResponseData.user.id);
|
|
expect(decodedAccessToken.type).toBe("access_token");
|
|
expect(decodedAccessToken.expiresAt).toBe(new Date(accessTokenExpiresAt).valueOf());
|
|
expect(decodedAccessToken.iat).toBeGreaterThan(0);
|
|
|
|
expect(decodedRefreshToken.clientId).toBe(oAuthClient.id);
|
|
expect(decodedRefreshToken.ownerId).toBe(postResponseData.user.id);
|
|
expect(decodedRefreshToken.type).toBe("refresh_token");
|
|
expect(decodedRefreshToken.expiresAt).toBe(new Date(refreshTokenExpiresAt).valueOf());
|
|
expect(decodedRefreshToken.iat).toBeGreaterThan(0);
|
|
});
|
|
|
|
it(`/DELETE/:id`, () => {
|
|
return request(app.getHttpServer())
|
|
.delete(`/api/v2/oauth-clients/${oAuthClient.id}/users/${postResponseData.user.id}`)
|
|
.set("x-cal-secret-key", oAuthClient.secret)
|
|
.set("Origin", `${CLIENT_REDIRECT_URI}`)
|
|
.expect(200);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await oauthClientRepositoryFixture.delete(oAuthClient.id);
|
|
await oauthClientRepositoryFixture.delete(oAuthClientEventTypesDisabled.id);
|
|
await teamRepositoryFixture.delete(organization.id);
|
|
try {
|
|
await userRepositoryFixture.delete(postResponseData.user.id);
|
|
} catch (e) {
|
|
// User might have been deleted by the test
|
|
}
|
|
try {
|
|
await userRepositoryFixture.delete(postResponseData2.user.id);
|
|
} catch (e) {
|
|
// User might have been deleted by the test
|
|
}
|
|
try {
|
|
await userRepositoryFixture.delete(platformAdmin.id);
|
|
} catch (e) {
|
|
// User might have been deleted by the test
|
|
}
|
|
await app.close();
|
|
});
|
|
});
|
|
|
|
describe("User team even-types", () => {
|
|
let app: INestApplication;
|
|
|
|
let oAuthClient1: PlatformOAuthClient;
|
|
let oAuthClient2: PlatformOAuthClient;
|
|
|
|
let organization: Team;
|
|
let team1: Team;
|
|
let team2: Team;
|
|
let owner: User;
|
|
|
|
let userRepositoryFixture: UserRepositoryFixture;
|
|
let oauthClientRepositoryFixture: OAuthClientRepositoryFixture;
|
|
let teamRepositoryFixture: TeamRepositoryFixture;
|
|
let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
|
|
let profileRepositoryFixture: ProfileRepositoryFixture;
|
|
let membershipsRepositoryFixture: MembershipRepositoryFixture;
|
|
let postResponseData: CreateManagedUserOutput["data"];
|
|
|
|
const userEmail = `oauth-client-users-user-${randomString(5)}@api.com`;
|
|
const userTimeZone = "Europe/Rome";
|
|
|
|
beforeAll(async () => {
|
|
const moduleRef = await Test.createTestingModule({
|
|
providers: [PrismaExceptionFilter, HttpExceptionFilter],
|
|
imports: [AppModule, UsersModule],
|
|
}).compile();
|
|
|
|
app = moduleRef.createNestApplication();
|
|
bootstrap(app as NestExpressApplication);
|
|
|
|
oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef);
|
|
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
|
|
teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
|
|
eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef);
|
|
profileRepositoryFixture = new ProfileRepositoryFixture(moduleRef);
|
|
membershipsRepositoryFixture = new MembershipRepositoryFixture(moduleRef);
|
|
organization = await teamRepositoryFixture.create({
|
|
name: `oauth-client-users-organization-${randomString()}`,
|
|
isPlatform: true,
|
|
isOrganization: true,
|
|
});
|
|
|
|
owner = await userRepositoryFixture.create({
|
|
email: `oauth-client-users-admin-${randomString()}@api.com`,
|
|
username: `oauth-client-users-admin-${randomString()}@api.com`,
|
|
organization: { connect: { id: organization.id } },
|
|
});
|
|
|
|
await profileRepositoryFixture.create({
|
|
uid: `usr-${owner.id}`,
|
|
username: `oauth-client-users-admin-${randomString()}@api.com`,
|
|
organization: {
|
|
connect: {
|
|
id: organization.id,
|
|
},
|
|
},
|
|
user: {
|
|
connect: {
|
|
id: owner.id,
|
|
},
|
|
},
|
|
});
|
|
|
|
await membershipsRepositoryFixture.create({
|
|
role: "OWNER",
|
|
user: { connect: { id: owner.id } },
|
|
team: { connect: { id: organization.id } },
|
|
accepted: true,
|
|
});
|
|
|
|
oAuthClient1 = await createOAuthClient(organization.id);
|
|
oAuthClient2 = await createOAuthClient(organization.id);
|
|
|
|
team1 = await teamRepositoryFixture.create({
|
|
name: "Testy org team",
|
|
isOrganization: false,
|
|
parent: { connect: { id: organization.id } },
|
|
createdByOAuthClient: { connect: { id: oAuthClient1.id } },
|
|
});
|
|
|
|
team2 = await teamRepositoryFixture.create({
|
|
name: "Testy org team 2",
|
|
isOrganization: false,
|
|
parent: { connect: { id: organization.id } },
|
|
createdByOAuthClient: { connect: { id: oAuthClient2.id } },
|
|
});
|
|
|
|
// note(Lauris): team1 team event-types
|
|
await eventTypesRepositoryFixture.createTeamEventType({
|
|
schedulingType: "COLLECTIVE",
|
|
team: {
|
|
connect: { id: team1.id },
|
|
},
|
|
title: "Collective Event Type",
|
|
slug: "collective-event-type",
|
|
length: 30,
|
|
assignAllTeamMembers: true,
|
|
bookingFields: [],
|
|
locations: [],
|
|
});
|
|
|
|
await eventTypesRepositoryFixture.createTeamEventType({
|
|
schedulingType: "MANAGED",
|
|
team: {
|
|
connect: { id: team1.id },
|
|
},
|
|
title: "Managed Event Type",
|
|
slug: "managed-event-type",
|
|
length: 60,
|
|
assignAllTeamMembers: true,
|
|
bookingFields: [],
|
|
locations: [],
|
|
});
|
|
|
|
// note(Lauris): team2 team event-types
|
|
await eventTypesRepositoryFixture.createTeamEventType({
|
|
schedulingType: "COLLECTIVE",
|
|
team: {
|
|
connect: { id: team2.id },
|
|
},
|
|
title: "Collective Event Type team 2",
|
|
slug: "collective-event-type-team-2",
|
|
length: 30,
|
|
assignAllTeamMembers: true,
|
|
bookingFields: [],
|
|
locations: [],
|
|
});
|
|
|
|
await eventTypesRepositoryFixture.createTeamEventType({
|
|
schedulingType: "MANAGED",
|
|
team: {
|
|
connect: { id: team2.id },
|
|
},
|
|
title: "Managed Event Type team 2",
|
|
slug: "managed-event-type-team-2",
|
|
length: 60,
|
|
assignAllTeamMembers: true,
|
|
bookingFields: [],
|
|
locations: [],
|
|
});
|
|
|
|
await app.init();
|
|
});
|
|
|
|
async function createOAuthClient(organizationId: number) {
|
|
const data = {
|
|
logo: "logo-url",
|
|
name: "name",
|
|
redirectUris: [CLIENT_REDIRECT_URI],
|
|
permissions: 32,
|
|
};
|
|
const secret = "secret";
|
|
|
|
const client = await oauthClientRepositoryFixture.create(organizationId, data, secret);
|
|
return client;
|
|
}
|
|
|
|
it("should be defined", () => {
|
|
expect(oauthClientRepositoryFixture).toBeDefined();
|
|
expect(userRepositoryFixture).toBeDefined();
|
|
expect(oAuthClient1).toBeDefined();
|
|
});
|
|
|
|
it(`should create managed user and update team event-types`, async () => {
|
|
const requestBody: CreateManagedUserInput = {
|
|
email: userEmail,
|
|
timeZone: userTimeZone,
|
|
weekStart: "Monday",
|
|
timeFormat: 24,
|
|
locale: Locales.FR,
|
|
name: "Alice Smith",
|
|
avatarUrl: "https://cal.com/api/avatar/2b735186-b01b-46d3-87da-019b8f61776b.png",
|
|
};
|
|
|
|
const response = await request(app.getHttpServer())
|
|
.post(`/api/v2/oauth-clients/${oAuthClient1.id}/users`)
|
|
.set("x-cal-secret-key", oAuthClient1.secret)
|
|
.send(requestBody)
|
|
.expect(201);
|
|
|
|
const responseBody: CreateManagedUserOutput = response.body;
|
|
postResponseData = responseBody.data;
|
|
|
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
|
expect(responseBody.data).toBeDefined();
|
|
|
|
await teamHasCorrectEventTypes(team1.id);
|
|
expect(responseBody.data.user.name).toEqual(requestBody.name);
|
|
});
|
|
|
|
async function teamHasCorrectEventTypes(teamId: number) {
|
|
const eventTypes = await eventTypesRepositoryFixture.getAllTeamEventTypes(teamId);
|
|
expect(eventTypes?.length).toEqual(2);
|
|
}
|
|
|
|
afterAll(async () => {
|
|
await oauthClientRepositoryFixture.delete(oAuthClient1.id);
|
|
await teamRepositoryFixture.delete(organization.id);
|
|
await userRepositoryFixture.delete(owner.id);
|
|
try {
|
|
await userRepositoryFixture.delete(postResponseData.user.id);
|
|
} catch (e) {
|
|
// User might have been deleted by the test
|
|
}
|
|
await app.close();
|
|
});
|
|
});
|
|
});
|