From d004815a68e2e53bf137ed16473a0b6454b1926f Mon Sep 17 00:00:00 2001 From: Lauris Skraucis Date: Fri, 26 Apr 2024 11:20:29 +0200 Subject: [PATCH] feat: v2 managed user create default schedule given timeZone (#14727) * schedules service: function to create default user schedule * managed user: create default schedule if timeZone provided * swagger update --- .../schedules/services/schedules.service.ts | 10 + .../oauth-client-users.controller.e2e-spec.ts | 17 + .../oauth-clients/oauth-client.module.ts | 2 + .../services/oauth-clients-users.service.ts | 10 +- apps/api/v2/swagger/documentation.json | 478 +++++++++--------- 5 files changed, 277 insertions(+), 240 deletions(-) diff --git a/apps/api/v2/src/ee/schedules/services/schedules.service.ts b/apps/api/v2/src/ee/schedules/services/schedules.service.ts index d74abd6ceb..c91176e422 100644 --- a/apps/api/v2/src/ee/schedules/services/schedules.service.ts +++ b/apps/api/v2/src/ee/schedules/services/schedules.service.ts @@ -24,6 +24,16 @@ export class SchedulesService { private readonly usersRepository: UsersRepository ) {} + async createUserDefaultSchedule(userId: number, timeZone: string) { + const schedule = { + isDefault: true, + name: "Default schedule", + timeZone, + }; + + return this.createUserSchedule(userId, schedule); + } + async createUserSchedule(userId: number, schedule: CreateScheduleInput) { const availabilities = schedule.availabilities?.length ? schedule.availabilities diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.e2e-spec.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.e2e-spec.ts index 5af5207662..568dfd8db4 100644 --- a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.e2e-spec.ts +++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.e2e-spec.ts @@ -17,6 +17,7 @@ import { PlatformOAuthClient, Team, User } from "@prisma/client"; import * as request from "supertest"; import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture"; import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.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"; @@ -75,10 +76,12 @@ describe("OAuth Client Users Endpoints", () => { let oauthClientRepositoryFixture: OAuthClientRepositoryFixture; let teamRepositoryFixture: TeamRepositoryFixture; let eventTypesRepositoryFixture: EventTypesRepositoryFixture; + let schedulesRepositoryFixture: SchedulesRepositoryFixture; let postResponseData: CreateUserResponse; const userEmail = "oauth-client-user@gmail.com"; + const userTimeZone = "Europe/Rome"; beforeAll(async () => { const moduleRef = await Test.createTestingModule({ @@ -93,6 +96,7 @@ describe("OAuth Client Users Endpoints", () => { userRepositoryFixture = new UserRepositoryFixture(moduleRef); teamRepositoryFixture = new TeamRepositoryFixture(moduleRef); eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef); + schedulesRepositoryFixture = new SchedulesRepositoryFixture(moduleRef); organization = await teamRepositoryFixture.create({ name: "organization" }); oAuthClient = await createOAuthClient(organization.id); @@ -134,6 +138,7 @@ describe("OAuth Client Users Endpoints", () => { it(`/POST`, async () => { const requestBody: CreateManagedUserInput = { email: userEmail, + timeZone: userTimeZone, }; const response = await request(app.getHttpServer()) @@ -158,6 +163,7 @@ describe("OAuth Client Users Endpoints", () => { await userConnectedToOAuth(responseBody.data.user.email); await userHasDefaultEventTypes(responseBody.data.user.id); + await userHasDefaultSchedule(responseBody.data.user.id, responseBody.data.user.defaultScheduleId); }); async function userConnectedToOAuth(userEmail: string) { @@ -181,6 +187,17 @@ describe("OAuth Client Users Endpoints", () => { ).toBeTruthy(); } + 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); + } + 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`) diff --git a/apps/api/v2/src/modules/oauth-clients/oauth-client.module.ts b/apps/api/v2/src/modules/oauth-clients/oauth-client.module.ts index 74a1b554ae..77a651b49a 100644 --- a/apps/api/v2/src/modules/oauth-clients/oauth-client.module.ts +++ b/apps/api/v2/src/modules/oauth-clients/oauth-client.module.ts @@ -1,4 +1,5 @@ import { EventTypesModule } from "@/ee/event-types/event-types.module"; +import { SchedulesModule } from "@/ee/schedules/schedules.module"; import { AuthModule } from "@/modules/auth/auth.module"; import { MembershipsModule } from "@/modules/memberships/memberships.module"; import { OAuthClientUsersController } from "@/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller"; @@ -25,6 +26,7 @@ import { Global, Module } from "@nestjs/common"; MembershipsModule, EventTypesModule, OrganizationsModule, + SchedulesModule, ], providers: [ OAuthClientRepository, diff --git a/apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts b/apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts index d4fd46fb5e..e04b52dcae 100644 --- a/apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts +++ b/apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts @@ -1,4 +1,5 @@ import { EventTypesService } from "@/ee/event-types/services/event-types.service"; +import { SchedulesService } from "@/ee/schedules/services/schedules.service"; import { TokensRepository } from "@/modules/tokens/tokens.repository"; import { CreateManagedUserInput } from "@/modules/users/inputs/create-managed-user.input"; import { UpdateManagedUserInput } from "@/modules/users/inputs/update-managed-user.input"; @@ -14,7 +15,8 @@ export class OAuthClientUsersService { constructor( private readonly userRepository: UsersRepository, private readonly tokensRepository: TokensRepository, - private readonly eventTypesService: EventTypesService + private readonly eventTypesService: EventTypesService, + private readonly schedulesService: SchedulesService ) {} async createOauthClientUser( @@ -62,8 +64,14 @@ export class OAuthClientUsersService { oAuthClientId, user.id ); + await this.eventTypesService.createUserDefaultEventTypes(user.id); + if (body.timeZone) { + const defaultSchedule = await this.schedulesService.createUserDefaultSchedule(user.id, body.timeZone); + user.defaultScheduleId = defaultSchedule.id; + } + return { user, tokens: { diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json index b822b36de2..dabbc49a7d 100644 --- a/apps/api/v2/swagger/documentation.json +++ b/apps/api/v2/swagger/documentation.json @@ -773,155 +773,6 @@ ] } }, - "/v2/gcal/oauth/auth-url": { - "get": { - "operationId": "GcalController_redirect", - "parameters": [ - { - "name": "Authorization", - "required": true, - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GcalAuthUrlOutput" - } - } - } - } - }, - "tags": [ - "Google Calendar" - ] - } - }, - "/v2/gcal/oauth/save": { - "get": { - "operationId": "GcalController_save", - "parameters": [ - { - "name": "state", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "code", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GcalSaveRedirectOutput" - } - } - } - } - }, - "tags": [ - "Google Calendar" - ] - } - }, - "/v2/gcal/check": { - "get": { - "operationId": "GcalController_check", - "parameters": [], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GcalCheckOutput" - } - } - } - } - }, - "tags": [ - "Google Calendar" - ] - } - }, - "/v2/provider/{clientId}": { - "get": { - "operationId": "CalProviderController_verifyClientId", - "parameters": [ - { - "name": "clientId", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProviderVerifyClientOutput" - } - } - } - } - }, - "tags": [ - "Cal provider" - ] - } - }, - "/v2/provider/{clientId}/access-token": { - "get": { - "operationId": "CalProviderController_verifyAccessToken", - "parameters": [ - { - "name": "clientId", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProviderVerifyAccessTokenOutput" - } - } - } - } - }, - "tags": [ - "Cal provider" - ] - } - }, "/v2/schedules": { "post": { "operationId": "SchedulesController_createSchedule", @@ -1089,6 +940,155 @@ ] } }, + "/v2/gcal/oauth/auth-url": { + "get": { + "operationId": "GcalController_redirect", + "parameters": [ + { + "name": "Authorization", + "required": true, + "in": "header", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GcalAuthUrlOutput" + } + } + } + } + }, + "tags": [ + "Google Calendar" + ] + } + }, + "/v2/gcal/oauth/save": { + "get": { + "operationId": "GcalController_save", + "parameters": [ + { + "name": "state", + "required": true, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "code", + "required": true, + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GcalSaveRedirectOutput" + } + } + } + } + }, + "tags": [ + "Google Calendar" + ] + } + }, + "/v2/gcal/check": { + "get": { + "operationId": "GcalController_check", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GcalCheckOutput" + } + } + } + } + }, + "tags": [ + "Google Calendar" + ] + } + }, + "/v2/provider/{clientId}": { + "get": { + "operationId": "CalProviderController_verifyClientId", + "parameters": [ + { + "name": "clientId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProviderVerifyClientOutput" + } + } + } + } + }, + "tags": [ + "Cal provider" + ] + } + }, + "/v2/provider/{clientId}/access-token": { + "get": { + "operationId": "CalProviderController_verifyAccessToken", + "parameters": [ + { + "name": "clientId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProviderVerifyAccessTokenOutput" + } + } + } + } + }, + "tags": [ + "Cal provider" + ] + } + }, "/v2/me": { "get": { "operationId": "MeController_getMe", @@ -2788,96 +2788,6 @@ "data" ] }, - "AuthUrlData": { - "type": "object", - "properties": { - "authUrl": { - "type": "string" - } - }, - "required": [ - "authUrl" - ] - }, - "GcalAuthUrlOutput": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] - }, - "data": { - "$ref": "#/components/schemas/AuthUrlData" - } - }, - "required": [ - "status", - "data" - ] - }, - "GcalSaveRedirectOutput": { - "type": "object", - "properties": { - "url": { - "type": "string" - } - }, - "required": [ - "url" - ] - }, - "GcalCheckOutput": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] - } - }, - "required": [ - "status" - ] - }, - "ProviderVerifyClientOutput": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] - } - }, - "required": [ - "status" - ] - }, - "ProviderVerifyAccessTokenOutput": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] - } - }, - "required": [ - "status" - ] - }, "CreateAvailabilityInput": { "type": "object", "properties": { @@ -3347,6 +3257,96 @@ "status" ] }, + "AuthUrlData": { + "type": "object", + "properties": { + "authUrl": { + "type": "string" + } + }, + "required": [ + "authUrl" + ] + }, + "GcalAuthUrlOutput": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] + }, + "data": { + "$ref": "#/components/schemas/AuthUrlData" + } + }, + "required": [ + "status", + "data" + ] + }, + "GcalSaveRedirectOutput": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": [ + "url" + ] + }, + "GcalCheckOutput": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] + } + }, + "required": [ + "status" + ] + }, + "ProviderVerifyClientOutput": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] + } + }, + "required": [ + "status" + ] + }, + "ProviderVerifyAccessTokenOutput": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] + } + }, + "required": [ + "status" + ] + }, "MeOutput": { "type": "object", "properties": {