diff --git a/apps/api/src/app/organization/e2e/rename-organization.e2e.ts b/apps/api/src/app/organization/e2e/rename-organization.e2e.ts new file mode 100644 index 00000000000..cf7ea613b70 --- /dev/null +++ b/apps/api/src/app/organization/e2e/rename-organization.e2e.ts @@ -0,0 +1,25 @@ +import { OrganizationRepository } from '@novu/dal'; +import { UserSession } from '@novu/testing'; +import { expect } from 'chai'; + +describe('Rename Organization - /organizations (PATCH)', function () { + let session: UserSession; + const organizationRepository = new OrganizationRepository(); + + beforeEach(async () => { + session = new UserSession(); + await session.initialize(); + }); + + it('should rename the organization', async function () { + const payload = { + name: 'Liberty Powers', + }; + + await session.testAgent.patch('/v1/organizations').send(payload); + + const organization = await organizationRepository.findById(session.organization._id); + + expect(organization?.name).to.equal(payload.name); + }); +}); diff --git a/apps/api/src/app/organization/organization.controller.ts b/apps/api/src/app/organization/organization.controller.ts index 57ecca86db3..969bb715599 100644 --- a/apps/api/src/app/organization/organization.controller.ts +++ b/apps/api/src/app/organization/organization.controller.ts @@ -7,6 +7,7 @@ import { Param, Post, Put, + Patch, UseGuards, UseInterceptors, } from '@nestjs/common'; @@ -33,6 +34,8 @@ import { IGetOrganizationsDto } from './dtos/get-organizations.dto'; import { GetMyOrganization } from './usecases/get-my-organization/get-my-organization.usecase'; import { GetMyOrganizationCommand } from './usecases/get-my-organization/get-my-organization.command'; import { IGetMyOrganizationDto } from './dtos/get-my-organization.dto'; +import { RenameOrganizationCommand } from './usecases/rename-organization/rename-organization-command'; +import { RenameOrganization } from './usecases/rename-organization/rename-organization.usecase'; @Controller('/organizations') @UseInterceptors(ClassSerializerInterceptor) @@ -47,7 +50,8 @@ export class OrganizationController { private changeMemberRoleUsecase: ChangeMemberRole, private updateBrandingDetailsUsecase: UpdateBrandingDetails, private getOrganizationsUsecase: GetOrganizations, - private getMyOrganizationUsecase: GetMyOrganization + private getMyOrganizationUsecase: GetMyOrganization, + private renameOrganizationUsecase: RenameOrganization ) {} @Post('/') @@ -154,4 +158,16 @@ export class OrganizationController { }) ); } + + @Patch('/') + @Roles(MemberRoleEnum.ADMIN) + async renameOrganization(@UserSession() user: IJwtPayload, @Body() body: { name: string }) { + return await this.renameOrganizationUsecase.execute( + RenameOrganizationCommand.create({ + name: body.name, + userId: user._id, + id: user.organizationId, + }) + ); + } } diff --git a/apps/api/src/app/organization/usecases/index.ts b/apps/api/src/app/organization/usecases/index.ts index 0aefd178e45..dc5f9f75b8b 100644 --- a/apps/api/src/app/organization/usecases/index.ts +++ b/apps/api/src/app/organization/usecases/index.ts @@ -7,6 +7,7 @@ import { ChangeMemberRole } from './membership/change-member-role/change-member- import { UpdateBrandingDetails } from './update-branding-details/update-branding-details.usecase'; import { GetOrganizations } from './get-organizations/get-organizations.usecase'; import { GetMyOrganization } from './get-my-organization/get-my-organization.usecase'; +import { RenameOrganization } from './rename-organization/rename-organization.usecase'; export const USE_CASES = [ AddMember, @@ -18,4 +19,5 @@ export const USE_CASES = [ UpdateBrandingDetails, GetOrganizations, GetMyOrganization, + RenameOrganization, ]; diff --git a/apps/api/src/app/organization/usecases/rename-organization/rename-organization-command.ts b/apps/api/src/app/organization/usecases/rename-organization/rename-organization-command.ts new file mode 100644 index 00000000000..c960378a5c3 --- /dev/null +++ b/apps/api/src/app/organization/usecases/rename-organization/rename-organization-command.ts @@ -0,0 +1,10 @@ +import { IsDefined, MaxLength } from 'class-validator'; +import { AuthenticatedCommand } from '../../../shared/commands/authenticated.command'; + +export class RenameOrganizationCommand extends AuthenticatedCommand { + @IsDefined() + public readonly id: string; + + @MaxLength(50) + name: string; +} diff --git a/apps/api/src/app/organization/usecases/rename-organization/rename-organization.usecase.ts b/apps/api/src/app/organization/usecases/rename-organization/rename-organization.usecase.ts new file mode 100644 index 00000000000..d499c0a3465 --- /dev/null +++ b/apps/api/src/app/organization/usecases/rename-organization/rename-organization.usecase.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@nestjs/common'; +import { OrganizationRepository } from '@novu/dal'; +import { RenameOrganizationCommand } from './rename-organization-command'; + +@Injectable() +export class RenameOrganization { + constructor(private organizationRepository: OrganizationRepository) {} + + async execute(command: RenameOrganizationCommand) { + const payload = { + name: command.name, + }; + + await this.organizationRepository.renameOrganization(command.id, payload); + + return payload; + } +} diff --git a/libs/dal/src/repositories/organization/organization.repository.ts b/libs/dal/src/repositories/organization/organization.repository.ts index 205b7e9238b..f5ebc711b18 100644 --- a/libs/dal/src/repositories/organization/organization.repository.ts +++ b/libs/dal/src/repositories/organization/organization.repository.ts @@ -35,6 +35,19 @@ export class OrganizationRepository extends BaseRepository< ); } + async renameOrganization(organizationId: string, payload: { name: string }) { + return this.update( + { + _id: organizationId, + }, + { + $set: { + name: payload.name, + }, + } + ); + } + async findPartnerConfigurationDetails(organizationId: string, userId: string, configurationId: string) { const members = await this.memberRepository.findUserActiveMembers(userId);