Skip to content

Commit

Permalink
Merge branch 'main' into release-new-embed-versions
Browse files Browse the repository at this point in the history
  • Loading branch information
sean-brydon committed May 10, 2024
2 parents 31747fc + a136511 commit 4c1b00d
Show file tree
Hide file tree
Showing 53 changed files with 1,667 additions and 589 deletions.
15 changes: 6 additions & 9 deletions apps/api/v2/src/ee/gcal/gcal.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,12 @@ import {
} from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { ApiTags as DocsTags } from "@nestjs/swagger";
import { Prisma } from "@prisma/client";
import { Request } from "express";
import { google } from "googleapis";
import { z } from "zod";

import {
APPS_READ,
GOOGLE_CALENDAR_ID,
GOOGLE_CALENDAR_TYPE,
SUCCESS_STATUS,
} from "@calcom/platform-constants";
import { APPS_READ, GOOGLE_CALENDAR_TYPE, SUCCESS_STATUS } from "@calcom/platform-constants";

const CALENDAR_SCOPES = [
"https://www.googleapis.com/auth/calendar.readonly",
Expand Down Expand Up @@ -106,10 +102,11 @@ export class GcalController {

const oAuth2Client = await this.gcalService.getOAuthClient(this.redirectUri);
const token = await oAuth2Client.getToken(parsedCode);
const key = token.res?.data;
// Google oAuth Credentials are stored in token.tokens
const key = token.tokens;
const credential = await this.credentialRepository.createAppCredential(
GOOGLE_CALENDAR_TYPE,
key,
key as Prisma.InputJsonValue,
ownerId
);

Expand All @@ -129,7 +126,7 @@ export class GcalController {
primaryCal.id,
credential.id,
ownerId,
GOOGLE_CALENDAR_ID
GOOGLE_CALENDAR_TYPE
);
}

Expand Down
2 changes: 1 addition & 1 deletion apps/api/v2/src/filters/http-exception.filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class HttpExceptionFilter implements ExceptionFilter<HttpException> {
status: ERROR_STATUS,
timestamp: new Date().toISOString(),
path: request.url,
error: { code: exception.name, message: exception.message },
error: { code: exception.name, message: exception.message, details: exception.getResponse() },
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export class BillingController {

@Get("/:teamId/check")
@UseGuards(NextAuthGuard, OrganizationRolesGuard)
@Roles(["OWNER", "ADMIN"])
@Roles(["OWNER", "ADMIN", "MEMBER"])
async checkTeamBilling(
@Param("teamId") teamId: number
): Promise<ApiResponse<CheckPlatformBillingResponseDto>> {
Expand Down
4 changes: 2 additions & 2 deletions apps/api/v2/src/modules/billing/services/billing.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ export class BillingService {
quantity: 1,
},
],
success_url: `${this.webAppUrl}/settings/platform/oauth-clients`,
cancel_url: `${this.webAppUrl}/settings/platform/oauth-clients`,
success_url: `${this.webAppUrl}/settings/platform/`,
cancel_url: `${this.webAppUrl}/settings/platform/`,
mode: "subscription",
metadata: {
teamId: teamId.toString(),
Expand Down
1 change: 1 addition & 0 deletions apps/api/v2/src/modules/billing/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export enum PlatformPlan {
STARTER = "STARTER",
ESSENTIALS = "ESSENTIALS",
SCALE = "SCALE",
ENTERPRISE = "ENTERPRISE",
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ describe("OAuth Client Users Endpoints", () => {
const requestBody: CreateManagedUserInput = {
email: userEmail,
timeZone: userTimeZone,
weekStart: "Monday",
timeFormat: 24,
};

const response = await request(app.getHttpServer())
Expand All @@ -158,6 +160,9 @@ describe("OAuth Client Users Endpoints", () => {
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseBody.data.user.email).toEqual(getOAuthUserEmail(oAuthClient.id, requestBody.email));
expect(responseBody.data.user.timeZone).toEqual(requestBody.timeZone);
expect(responseBody.data.user.weekStart).toEqual(requestBody.weekStart);
expect(responseBody.data.user.timeFormat).toEqual(requestBody.timeFormat);
expect(responseBody.data.accessToken).toBeDefined();
expect(responseBody.data.refreshToken).toBeDefined();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@ import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { NextAuthGuard } from "@/modules/auth/guards/next-auth/next-auth.guard";
import { OrganizationRolesGuard } from "@/modules/auth/guards/organization-roles/organization-roles.guard";
import { GetManagedUsersOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/get-managed-users.output";
import { ManagedUserOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/managed-user.output";
import { CreateOAuthClientResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/CreateOAuthClientResponse.dto";
import { GetOAuthClientResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientResponse.dto";
import { GetOAuthClientsResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientsResponse.dto";
import { UpdateOAuthClientInput } from "@/modules/oauth-clients/inputs/update-oauth-client.input";
import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
import { UserWithProfile } from "@/modules/users/users.repository";
import { UsersRepository } from "@/modules/users/users.repository";
import {
Body,
Controller,
Query,
Get,
Post,
Patch,
Expand All @@ -32,9 +36,11 @@ import {
ApiCreatedResponse as DocsCreatedResponse,
} from "@nestjs/swagger";
import { MembershipRole } from "@prisma/client";
import { User } from "@prisma/client";

import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { CreateOAuthClientInput } from "@calcom/platform-types";
import { Pagination } from "@calcom/platform-types";

const AUTH_DOCUMENTATION = `⚠️ First, this endpoint requires \`Cookie: next-auth.session-token=eyJhbGciOiJ\` header. Log into Cal web app using owner of organization that was created after visiting \`/settings/organizations/new\`, refresh swagger docs, and the cookie will be added to requests automatically to pass the NextAuthGuard.
Second, make sure that the logged in user has organizationId set to pass the OrganizationRolesGuard guard.`;
Expand All @@ -51,6 +57,7 @@ export class OAuthClientsController {

constructor(
private readonly oauthClientRepository: OAuthClientRepository,
private readonly userRepository: UsersRepository,
private readonly teamsRepository: OrganizationsRepository
) {}

Expand Down Expand Up @@ -109,6 +116,24 @@ export class OAuthClientsController {
return { status: SUCCESS_STATUS, data: client };
}

@Get("/:clientId/managed-users")
@HttpCode(HttpStatus.OK)
@Roles([MembershipRole.ADMIN, MembershipRole.OWNER, MembershipRole.MEMBER])
@DocsOperation({ description: AUTH_DOCUMENTATION })
async getOAuthClientManagedUsersById(
@Param("clientId") clientId: string,
@Query() queryParams: Pagination
): Promise<GetManagedUsersOutput> {
const { offset, limit } = queryParams;
const existingManagedUsers = await this.userRepository.findManagedUsersByOAuthClientId(
clientId,
offset ?? 0,
limit ?? 50
);

return { status: SUCCESS_STATUS, data: existingManagedUsers.map((user) => this.getResponseUser(user)) };
}

@Patch("/:clientId")
@HttpCode(HttpStatus.OK)
@Roles([MembershipRole.ADMIN, MembershipRole.OWNER])
Expand All @@ -131,4 +156,17 @@ export class OAuthClientsController {
const client = await this.oauthClientRepository.deleteOAuthClient(clientId);
return { status: SUCCESS_STATUS, data: client };
}

private getResponseUser(user: User): ManagedUserOutput {
return {
id: user.id,
email: user.email,
username: user.username,
timeZone: user.timeZone,
weekStart: user.weekStart,
createdDate: user.createdDate,
timeFormat: user.timeFormat,
defaultScheduleId: user.defaultScheduleId,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ export class OAuthClientUsersService {
},
},
isPlatformManaged,
timeFormat: body.timeFormat,
weekStart: body.weekStart,
timeZone: body.timeZone,
})
)[0];
await this.userRepository.addToOAuthClient(user.id, oAuthClientId);
Expand Down
22 changes: 11 additions & 11 deletions apps/api/v2/src/modules/users/inputs/create-managed-user.input.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { IsTimeFormat } from "@/modules/users/inputs/validators/is-time-format";
import { IsWeekStart } from "@/modules/users/inputs/validators/is-week-start";
import { ApiProperty } from "@nestjs/swagger";
import { IsNumber, IsOptional, IsTimeZone, IsString, Validate } from "class-validator";
import { IsOptional, IsTimeZone, IsString } from "class-validator";

export type WeekDay = "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday";
export type TimeFormat = 12 | 24;
export class CreateManagedUserInput {
@IsString()
@ApiProperty({ example: "alice@example.com" })
Expand All @@ -12,17 +12,17 @@ export class CreateManagedUserInput {
@IsOptional()
name?: string;

@IsNumber()
@IsOptional()
@Validate(IsTimeFormat)
@ApiProperty({ example: 12 })
timeFormat?: number;
@ApiProperty({ example: 12, enum: [12, 24], description: "Must be 12 or 24" })
timeFormat?: TimeFormat;

@IsString()
@IsOptional()
@Validate(IsWeekStart)
@ApiProperty({ example: "Sunday" })
weekStart?: string;
@IsString()
@ApiProperty({
example: "Monday",
enum: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
})
weekStart?: WeekDay;

@IsTimeZone()
@IsOptional()
Expand Down
20 changes: 11 additions & 9 deletions apps/api/v2/src/modules/users/inputs/update-managed-user.input.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IsTimeFormat } from "@/modules/users/inputs/validators/is-time-format";
import { IsWeekStart } from "@/modules/users/inputs/validators/is-week-start";
import { IsNumber, IsOptional, IsString, IsTimeZone, Validate } from "class-validator";
import { TimeFormat, WeekDay } from "@/modules/users/inputs/create-managed-user.input";
import { ApiProperty } from "@nestjs/swagger";
import { IsNumber, IsOptional, IsString, IsTimeZone } from "class-validator";

export class UpdateManagedUserInput {
@IsString()
Expand All @@ -11,19 +11,21 @@ export class UpdateManagedUserInput {
@IsOptional()
name?: string;

@IsNumber()
@IsOptional()
@Validate(IsTimeFormat)
timeFormat?: number;
@ApiProperty({ example: 12, enum: [12, 24], description: "Must be 12 or 24" })
timeFormat?: TimeFormat;

@IsNumber()
@IsOptional()
defaultScheduleId?: number;

@IsString()
@IsOptional()
@Validate(IsWeekStart)
weekStart?: string;
@IsString()
@ApiProperty({
example: "Monday",
enum: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
})
weekStart?: WeekDay;

@IsTimeZone()
@IsOptional()
Expand Down
12 changes: 0 additions & 12 deletions apps/api/v2/src/modules/users/inputs/validators/is-time-format.ts

This file was deleted.

23 changes: 0 additions & 23 deletions apps/api/v2/src/modules/users/inputs/validators/is-week-start.ts

This file was deleted.

2 changes: 1 addition & 1 deletion apps/api/v2/src/modules/users/users.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export class UsersRepository {

formatInput(userInput: CreateManagedUserInput | UpdateManagedUserInput) {
if (userInput.weekStart) {
userInput.weekStart = capitalize(userInput.weekStart);
userInput.weekStart = userInput.weekStart;
}

if (userInput.timeZone) {
Expand Down
46 changes: 38 additions & 8 deletions apps/api/v2/swagger/documentation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1651,11 +1651,25 @@
},
"timeFormat": {
"type": "number",
"example": 12
"example": 12,
"enum": [
12,
24
],
"description": "Must be 12 or 24"
},
"weekStart": {
"type": "string",
"example": "Sunday"
"example": "Monday",
"enum": [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
]
},
"timeZone": {
"type": "string",
Expand Down Expand Up @@ -1731,21 +1745,37 @@
"UpdateManagedUserInput": {
"type": "object",
"properties": {
"timeFormat": {
"type": "number",
"example": 12,
"enum": [
12,
24
],
"description": "Must be 12 or 24"
},
"weekStart": {
"type": "string",
"example": "Monday",
"enum": [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
]
},
"email": {
"type": "string"
},
"name": {
"type": "string"
},
"timeFormat": {
"type": "number"
},
"defaultScheduleId": {
"type": "number"
},
"weekStart": {
"type": "string"
},
"timeZone": {
"type": "string"
}
Expand Down
2 changes: 2 additions & 0 deletions apps/api/v2/test/setEnvVars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const env: Partial<Omit<Environment, "NODE_ENV">> = {
JWT_SECRET: "XF+Hws3A5g2eyWA5uGYYVJ74X+wrCWJ8oWo6kAfU6O8=",
LOG_LEVEL: "trace",
REDIS_URL: "redis://localhost:9199",
STRIPE_API_KEY: "sk_test_51J4",
STRIPE_WEBHOOK_SECRET: "whsec_51J4",
};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
Expand Down

0 comments on commit 4c1b00d

Please sign in to comment.