From 47bf46a141ff56247c257f5a21615108caeb5b88 Mon Sep 17 00:00:00 2001 From: Khaybee Date: Fri, 28 Feb 2025 17:17:29 +0100 Subject: [PATCH 1/3] fix: Ensure user creation runs in a transaction --- src/modules/auth/auth.service.ts | 119 ++++++++++-------- src/modules/auth/tests/auth.service.spec.ts | 15 ++- .../organisations/organisations.service.ts | 22 ++-- src/modules/otp/otp.service.ts | 12 +- src/modules/user/user.service.ts | 23 ++-- 5 files changed, 110 insertions(+), 81 deletions(-) diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 7b270c554..0009ebf25 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -20,7 +20,7 @@ import { TokenPayload } from 'google-auth-library'; import { UpdateProfileDto } from '@modules/profile/dto/update-profile.dto'; import { RequestSigninTokenDto } from './dto/request-signin-token.dto'; import { OtpDto } from '@modules/otp/dto/otp.dto'; - +import { DataSource, EntityManager } from 'typeorm'; @Injectable() export default class AuthenticationService { constructor( @@ -29,74 +29,83 @@ export default class AuthenticationService { private otpService: OtpService, private emailService: EmailService, private organisationService: OrganisationsService, - private profileService: ProfileService + private profileService: ProfileService, + private dataSource: DataSource ) {} async createNewUser(createUserDto: CreateUserDTO) { - const userExists = await this.userService.getUserRecord({ - identifier: createUserDto.email, - identifierType: 'email', - }); + const result = await this.dataSource.transaction(async (manager: EntityManager) => { + const userExists = await this.userService.getUserRecord( + { + identifier: createUserDto.email, + identifierType: 'email', + }, + manager + ); - if (userExists) { - throw new CustomHttpException(SYS_MSG.USER_ACCOUNT_EXIST, HttpStatus.BAD_REQUEST); - } + if (userExists) { + throw new CustomHttpException(SYS_MSG.USER_ACCOUNT_EXIST, HttpStatus.BAD_REQUEST); + } - await this.userService.createUser(createUserDto); + const user = await this.userService.createUser(createUserDto, manager); - const user = await this.userService.getUserRecord({ identifier: createUserDto.email, identifierType: 'email' }); + if (!user) { + throw new CustomHttpException(SYS_MSG.FAILED_TO_CREATE_USER, HttpStatus.BAD_REQUEST); + } + const newOrganisationPayload = { + name: `${user.first_name}'s Organisation`, + description: '', + email: user.email, + industry: '', + type: '', + country: '', + address: '', + state: '', + }; - if (!user) { - throw new CustomHttpException(SYS_MSG.FAILED_TO_CREATE_USER, HttpStatus.BAD_REQUEST); - } - const newOrganisationPayload = { - name: `${user.first_name}'s Organisation`, - description: '', - email: user.email, - industry: '', - type: '', - country: '', - address: '', - state: '', - }; + const newOrganisation = await this.organisationService.create(newOrganisationPayload, user.id, manager); - const newOrganisation = await this.organisationService.create(newOrganisationPayload, user.id); + const userOrganisations = await this.organisationService.getAllUserOrganisations(user.id, manager); - const userOranisations = await this.organisationService.getAllUserOrganisations(user.id); - const isSuperAdmin = userOranisations.map(instance => instance.user_role).includes('super-admin'); - const token = (await this.otpService.createOtp(user.id)).token; + const isSuperAdmin = userOrganisations.map(instance => instance.user_role).includes('super-admin'); - const access_token = this.jwtService.sign({ - id: user.id, - sub: user.id, - email: user.email, - }); + const token = (await this.otpService.createOtp(user.id, manager)).token; - const responsePayload = { - user: { + const access_token = this.jwtService.sign({ id: user.id, - first_name: user.first_name, - last_name: user.last_name, + sub: user.id, email: user.email, - avatar_url: user.profile.profile_pic_url, - is_superadmin: isSuperAdmin, - }, - oranisations: userOranisations, - }; - - // send welcome mail - await this.emailService.sendUserConfirmationMail( - user.email, - user.first_name, - `${process.env.FRONTEND_URL}/confirm-email`, - token - ); + }); + const responsePayload = { + user: { + id: user.id, + first_name: user.first_name, + last_name: user.last_name, + email: user.email, + avatar_url: user.profile.profile_pic_url, + is_superadmin: isSuperAdmin, + }, + organisations: userOrganisations, + }; + return { + message: SYS_MSG.USER_CREATED_SUCCESSFULLY, + access_token, + data: responsePayload, + }; + }); + try { + // send welcome mail + await this.emailService.sendUserConfirmationMail( + result.data.user.email, + result.data.user.first_name, + `${process.env.FRONTEND_URL}/confirm-email`, + result.access_token + ); + } catch (emailError) { + console.error('Error sending confirmation email:', emailError); + } - return { - message: SYS_MSG.USER_CREATED_SUCCESSFULLY, - access_token, - data: responsePayload, - }; + return result; } async forgotPassword(dto: ForgotPasswordDto): Promise<{ message: string } | null> { diff --git a/src/modules/auth/tests/auth.service.spec.ts b/src/modules/auth/tests/auth.service.spec.ts index bc7697e59..723a5c06e 100644 --- a/src/modules/auth/tests/auth.service.spec.ts +++ b/src/modules/auth/tests/auth.service.spec.ts @@ -19,19 +19,24 @@ import { LoginDto } from '../dto/login.dto'; import UserResponseDTO from '@modules/user/dto/user-response.dto'; import { Otp } from '@modules/otp/entities/otp.entity'; import { Verify2FADto } from '../dto/verify-2fa.dto'; - +import { DataSource, EntityManager } from 'typeorm'; jest.mock('speakeasy'); describe('AuthenticationService', () => { let service: AuthenticationService; let userServiceMock: jest.Mocked; let profileServiceMock: jest.Mocked; + let dataSourceMock: jest.Mocked; let jwtServiceMock: jest.Mocked; let otpServiceMock: jest.Mocked; let emailServiceMock: jest.Mocked; let organisationServiceMock: jest.Mocked; beforeEach(async () => { + dataSourceMock = { + transaction: jest.fn().mockImplementation(async cb => cb({} as EntityManager)), + manager: {} as EntityManager, + } as unknown as jest.Mocked; const module: TestingModule = await Test.createTestingModule({ providers: [ AuthenticationService, @@ -77,6 +82,10 @@ describe('AuthenticationService', () => { sendEmail: jest.fn(), }, }, + { + provide: DataSource, + useValue: dataSourceMock, + }, ], }).compile(); @@ -123,7 +132,7 @@ describe('AuthenticationService', () => { it('should create a new user successfully', async () => { userServiceMock.getUserRecord.mockResolvedValueOnce(null); - userServiceMock.createUser.mockResolvedValueOnce(undefined); + userServiceMock.createUser.mockResolvedValueOnce(mockUser as User); userServiceMock.getUserRecord.mockResolvedValueOnce({ id: '1', @@ -175,7 +184,7 @@ describe('AuthenticationService', () => { is_superadmin: false, avatar_url: 'some_url', }, - oranisations: [ + organisations: [ { organisation_id: 'e12973d1-cbc3-45f8-ba13-14991e4490fa', name: "John's Organisation", diff --git a/src/modules/organisations/organisations.service.ts b/src/modules/organisations/organisations.service.ts index e29d7f5c8..c401ecd34 100644 --- a/src/modules/organisations/organisations.service.ts +++ b/src/modules/organisations/organisations.service.ts @@ -8,7 +8,7 @@ import { NotFoundException, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { Repository, EntityManager } from 'typeorm'; import { UpdateOrganisationDto } from './dto/update-organisation.dto'; import { Organisation } from './entities/organisations.entity'; import { OrganisationMapper } from './mapper/organisation.mapper'; @@ -70,30 +70,35 @@ export class OrganisationsService { return { status_code: HttpStatus.CREATED, messge: 'Organisation created', data: query }; } - async create(createOrganisationDto: CreateOrganisationType, userId: string) { + async create(createOrganisationDto: CreateOrganisationType, userId: string, manager?: EntityManager) { + const userRepo = manager ? manager.getRepository(User) : this.userRepository; + const repo = manager ? manager.getRepository(Organisation) : this.organisationRepository; + const orgUserRoleRepo = manager ? manager.getRepository(OrganisationUserRole) : this.organisationUserRole; if (createOrganisationDto.email) { const emailFound = await this.emailExists(createOrganisationDto.email); if (emailFound) throw new ConflictException('Organisation with this email already exists'); } - const owner = await this.userRepository.findOne({ + const owner = await userRepo.findOne({ where: { id: userId }, }); - const vendorRole = await this.roleRepository.findOne({ where: { name: 'admin' } }); + const vendorRole = await (manager ? manager.getRepository(Role) : this.roleRepository).findOne({ + where: { name: 'admin' }, + }); const organisationInstance = new Organisation(); Object.assign(organisationInstance, createOrganisationDto); organisationInstance.owner = owner; organisationInstance.members = [owner]; - const newOrganisation = await this.organisationRepository.save(organisationInstance); + const newOrganisation = await repo.save(organisationInstance); const adminRole = new OrganisationUserRole(); adminRole.userId = owner.id; adminRole.organisationId = newOrganisation.id; adminRole.roleId = vendorRole.id; - await this.organisationUserRole.save(adminRole); + await orgUserRoleRepo.save(adminRole); const mappedResponse = OrganisationMapper.mapToResponseFormat(newOrganisation); @@ -148,8 +153,9 @@ export class OrganisationsService { }; } - async getAllUserOrganisations(userId: string) { - const user = await this.userRepository.findOne({ where: { id: userId } }); + async getAllUserOrganisations(userId: string, manager?: EntityManager) { + const repo = manager ? manager.getRepository(User) : this.userRepository; + const user = await repo.findOne({ where: { id: userId } }); if (!user) { throw new CustomHttpException('Invalid Request', HttpStatus.BAD_REQUEST); diff --git a/src/modules/otp/otp.service.ts b/src/modules/otp/otp.service.ts index 149dd4b30..299e59de7 100644 --- a/src/modules/otp/otp.service.ts +++ b/src/modules/otp/otp.service.ts @@ -1,6 +1,6 @@ import { HttpStatus, Injectable, NotAcceptableException, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { Repository, EntityManager } from 'typeorm'; import { Otp } from './entities/otp.entity'; import { User } from '@modules/user/entities/user.entity'; import { generateSixDigitToken } from '@utils/generate-token'; @@ -15,9 +15,11 @@ export class OtpService { private userRepository: Repository ) {} - async createOtp(userId: string): Promise { + async createOtp(userId: string, manager?: EntityManager): Promise { try { - const user = await this.userRepository.findOne({ where: { id: userId } }); + const repo = manager ? manager.getRepository(User) : this.userRepository; + const otpRepo = manager ? manager.getRepository(Otp) : this.otpRepository; + const user = await repo.findOne({ where: { id: userId } }); if (!user) { throw new NotFoundException('User not found'); @@ -26,8 +28,8 @@ export class OtpService { const token = generateSixDigitToken(); const expiry = new Date(Date.now() + 5 * 60 * 1000); - const otp = this.otpRepository.create({ token, expiry, user, user_id: userId }); - await this.otpRepository.save(otp); + const otp = otpRepo.create({ token, expiry, user, user_id: userId }); + await otpRepo.save(otp); return otp; } catch (error) { diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index 5d7918a52..53c917c2f 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -8,7 +8,7 @@ import { StreamableFile, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { EntityManager, Repository } from 'typeorm'; import { Profile } from '../profile/entities/profile.entity'; import { DeactivateAccountDto } from './dto/deactivate-account.dto'; import { UpdateUserDto } from './dto/update-user-dto'; @@ -38,13 +38,14 @@ export default class UserService { private profileRepository: Repository ) {} - async createUser(createUserPayload: CreateNewUserOptions): Promise { + async createUser(createUserPayload: CreateNewUserOptions, manager?: EntityManager): Promise { + const repo = manager ? manager.getRepository(User) : this.userRepository; const profile = await this.profileRepository.save({ email: createUserPayload.email, username: '' }); const newUser = new User(); Object.assign(newUser, createUserPayload); newUser.is_active = true; newUser.profile = profile; - return await this.userRepository.save(newUser); + return await repo.save(newUser); } async updateUserRecord(userUpdateOptions: UpdateUserRecordOption) { @@ -78,28 +79,30 @@ export default class UserService { return this.userRepository.save(newUser); } - private async getUserByEmail(email: string) { - const user: UserResponseDTO = await this.userRepository.findOne({ + private async getUserByEmail(email: string, manager?: EntityManager) { + const repo = manager ? manager.getRepository(User) : this.userRepository; + const user: UserResponseDTO = await repo.findOne({ where: { email: email }, relations: ['profile', 'owned_organisations'], }); return user; } - private async getUserById(identifier: string) { - const user: UserResponseDTO = await this.userRepository.findOne({ + private async getUserById(identifier: string, manager?: EntityManager) { + const repo = manager ? manager.getRepository(User) : this.userRepository; + const user: UserResponseDTO = await repo.findOne({ where: { id: identifier }, relations: ['profile', 'owned_organisations'], }); return user; } - async getUserRecord(identifierOptions: UserIdentifierOptionsType) { + async getUserRecord(identifierOptions: UserIdentifierOptionsType, manager?: EntityManager) { const { identifier, identifierType } = identifierOptions; const GetRecord = { - id: async () => this.getUserById(String(identifier)), - email: async () => this.getUserByEmail(String(identifier)), + id: async () => this.getUserById(String(identifier), manager), + email: async () => this.getUserByEmail(String(identifier), manager), }; return await GetRecord[identifierType](); From 5e34c7d3ab082334727d6fde97a690227502f1af Mon Sep 17 00:00:00 2001 From: Khaybee Date: Sat, 1 Mar 2025 00:38:31 +0100 Subject: [PATCH 2/3] fix: modified user creation service function --- src/modules/auth/auth.service.ts | 36 ++++++++++----- .../dto/create-organisation-options.ts | 4 +- .../interfaces/OrganisationInterface.ts | 2 + .../organisations/organisations.controller.ts | 6 ++- .../organisations/organisations.service.ts | 45 ++++++++++++------- .../tests/organisations.service.spec.ts | 17 ++++--- src/modules/user/user.service.ts | 16 +++---- src/shared/helpers/createRecordGeneric.ts | 13 ++++++ .../inteceptors/response.interceptor.ts | 1 + 9 files changed, 94 insertions(+), 46 deletions(-) create mode 100644 src/shared/helpers/createRecordGeneric.ts diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 0009ebf25..7f749e132 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -21,6 +21,7 @@ import { UpdateProfileDto } from '@modules/profile/dto/update-profile.dto'; import { RequestSigninTokenDto } from './dto/request-signin-token.dto'; import { OtpDto } from '@modules/otp/dto/otp.dto'; import { DataSource, EntityManager } from 'typeorm'; +import { CreateOrganisationRecordOptions } from '@modules/organisations/dto/create-organisation-options'; @Injectable() export default class AuthenticationService { constructor( @@ -35,18 +36,17 @@ export default class AuthenticationService { async createNewUser(createUserDto: CreateUserDTO) { const result = await this.dataSource.transaction(async (manager: EntityManager) => { - const userExists = await this.userService.getUserRecord( - { - identifier: createUserDto.email, - identifierType: 'email', - }, - manager - ); + const userExists = await this.userService.getUserRecord({ + identifier: createUserDto.email, + identifierType: 'email', + }); + console.log('userExists', userExists); if (userExists) { throw new CustomHttpException(SYS_MSG.USER_ACCOUNT_EXIST, HttpStatus.BAD_REQUEST); } + console.log('createUserDto', createUserDto); const user = await this.userService.createUser(createUserDto, manager); if (!user) { @@ -63,9 +63,19 @@ export default class AuthenticationService { state: '', }; - const newOrganisation = await this.organisationService.create(newOrganisationPayload, user.id, manager); + const createOrganisationPayload: CreateOrganisationRecordOptions = { + createPayload: newOrganisationPayload, + dbTransaction: { + useTransaction: true, + transactionManager: manager, + }, + }; + + const newOrganisation = await this.organisationService.create(createOrganisationPayload); + console.log('newOrganisation', newOrganisation); - const userOrganisations = await this.organisationService.getAllUserOrganisations(user.id, manager); + const userOrganisations = await this.organisationService.getAllUserOrganisations(user.id); + console.log('userOrganisations', userOrganisations); const isSuperAdmin = userOrganisations.map(instance => instance.user_role).includes('super-admin'); @@ -383,7 +393,13 @@ export default class AuthenticationService { state: '', }; - await this.organisationService.create(newOrganisationPaload, newUser.id); + const createOrganisationPayload: CreateOrganisationRecordOptions = { + createPayload: newOrganisationPaload, + dbTransaction: { + useTransaction: false, + }, + }; + await this.organisationService.create(createOrganisationPayload); const userOranisations = await this.organisationService.getAllUserOrganisations(newUser.id); const isSuperAdmin = userOranisations.map(instance => instance.user_role).includes('super-admin'); diff --git a/src/modules/organisations/dto/create-organisation-options.ts b/src/modules/organisations/dto/create-organisation-options.ts index fa1609979..2cca017e1 100644 --- a/src/modules/organisations/dto/create-organisation-options.ts +++ b/src/modules/organisations/dto/create-organisation-options.ts @@ -1,5 +1,5 @@ import { OrganisationInterface } from '../interfaces/OrganisationInterface'; - +import { CreateRecordGeneric } from '@shared/helpers/createRecordGeneric'; type CreateOrganisationType = Partial; - +export type CreateOrganisationRecordOptions = CreateRecordGeneric; export default CreateOrganisationType; diff --git a/src/modules/organisations/interfaces/OrganisationInterface.ts b/src/modules/organisations/interfaces/OrganisationInterface.ts index 49a6a83f2..bcd6e8355 100644 --- a/src/modules/organisations/interfaces/OrganisationInterface.ts +++ b/src/modules/organisations/interfaces/OrganisationInterface.ts @@ -14,4 +14,6 @@ export interface OrganisationInterface { address: string; state: string; + + userId: string; } diff --git a/src/modules/organisations/organisations.controller.ts b/src/modules/organisations/organisations.controller.ts index 739e6648a..f98593826 100644 --- a/src/modules/organisations/organisations.controller.ts +++ b/src/modules/organisations/organisations.controller.ts @@ -38,7 +38,11 @@ export class OrganisationsController { @Post('/') async create(@Body() createOrganisationDto: OrganisationRequestDto, @Req() req) { const user = req['user']; - return this.organisationsService.createOrganisation(createOrganisationDto, user.sub); + const payload = { + ...createOrganisationDto, + userId: user.sub, + }; + return this.organisationsService.createOrganisation(payload); } @UseGuards(OwnershipGuard) diff --git a/src/modules/organisations/organisations.service.ts b/src/modules/organisations/organisations.service.ts index c401ecd34..8943e3222 100644 --- a/src/modules/organisations/organisations.service.ts +++ b/src/modules/organisations/organisations.service.ts @@ -12,7 +12,7 @@ import { Repository, EntityManager } from 'typeorm'; import { UpdateOrganisationDto } from './dto/update-organisation.dto'; import { Organisation } from './entities/organisations.entity'; import { OrganisationMapper } from './mapper/organisation.mapper'; -import CreateOrganisationType from './dto/create-organisation-options'; +import CreateOrganisationType, { CreateOrganisationRecordOptions } from './dto/create-organisation-options'; import { OrganisationMemberMapper } from './mapper/org-members.mapper'; import { UpdateMemberRoleDto } from './dto/update-organisation-role.dto'; import { AddMemberDto } from './dto/add-member.dto'; @@ -65,30 +65,41 @@ export class OrganisationsService { return { status_code: HttpStatus.OK, message: 'members retrieved successfully', data }; } - async createOrganisation(createOrganisationDto: CreateOrganisationType, userId: string) { - const query = await this.create(createOrganisationDto, userId); + async createOrganisation(createOrganisationDto: CreateOrganisationType) { + const createPayload: CreateOrganisationRecordOptions = { + createPayload: createOrganisationDto, + dbTransaction: { + useTransaction: false, + }, + }; + const query = await this.create(createPayload); return { status_code: HttpStatus.CREATED, messge: 'Organisation created', data: query }; } - async create(createOrganisationDto: CreateOrganisationType, userId: string, manager?: EntityManager) { - const userRepo = manager ? manager.getRepository(User) : this.userRepository; - const repo = manager ? manager.getRepository(Organisation) : this.organisationRepository; - const orgUserRoleRepo = manager ? manager.getRepository(OrganisationUserRole) : this.organisationUserRole; - if (createOrganisationDto.email) { - const emailFound = await this.emailExists(createOrganisationDto.email); + async create(createOrganisationDto: CreateOrganisationRecordOptions) { + const { createPayload, dbTransaction } = createOrganisationDto; + + const repo = dbTransaction.useTransaction + ? dbTransaction.transactionManager.getRepository(Organisation) + : this.organisationRepository; + const orgUserRoleRepo = dbTransaction.useTransaction + ? dbTransaction.transactionManager.getRepository(OrganisationUserRole) + : this.organisationUserRole; + if (createPayload.email) { + const emailFound = await this.emailExists(createPayload.email); if (emailFound) throw new ConflictException('Organisation with this email already exists'); } - const owner = await userRepo.findOne({ - where: { id: userId }, + const owner = await this.userRepository.findOne({ + where: { id: createPayload.userId }, }); - const vendorRole = await (manager ? manager.getRepository(Role) : this.roleRepository).findOne({ + const vendorRole = await this.roleRepository.findOne({ where: { name: 'admin' }, }); const organisationInstance = new Organisation(); - Object.assign(organisationInstance, createOrganisationDto); + Object.assign(organisationInstance, createPayload); organisationInstance.owner = owner; organisationInstance.members = [owner]; const newOrganisation = await repo.save(organisationInstance); @@ -153,13 +164,13 @@ export class OrganisationsService { }; } - async getAllUserOrganisations(userId: string, manager?: EntityManager) { - const repo = manager ? manager.getRepository(User) : this.userRepository; - const user = await repo.findOne({ where: { id: userId } }); + async getAllUserOrganisations(userId: string) { + const user = await this.userRepository.findOne({ where: { id: userId } }); if (!user) { - throw new CustomHttpException('Invalid Request', HttpStatus.BAD_REQUEST); + return []; } + const userOrganisations = ( await this.organisationUserRole.find({ where: { userId }, diff --git a/src/modules/organisations/tests/organisations.service.spec.ts b/src/modules/organisations/tests/organisations.service.spec.ts index 809eb8135..deddecbd5 100644 --- a/src/modules/organisations/tests/organisations.service.spec.ts +++ b/src/modules/organisations/tests/organisations.service.spec.ts @@ -5,15 +5,12 @@ import { User } from '../../user/entities/user.entity'; import { Organisation } from '../entities/organisations.entity'; import { getRepositoryToken } from '@nestjs/typeorm'; import UserService from '../../user/user.service'; -import { - ForbiddenException, - NotFoundException, -} from '@nestjs/common'; +import { ForbiddenException, NotFoundException } from '@nestjs/common'; import { Profile } from '../../profile/entities/profile.entity'; import { OrganisationUserRole } from '../../../modules/role/entities/organisation-user-role.entity'; import { Role } from '../../../modules/role/entities/role.entity'; import { CustomHttpException } from '@shared/helpers/custom-http-filter'; - +import { CreateOrganisationRecordOptions } from '../dto/create-organisation-options'; describe('OrganisationsService', () => { let service: OrganisationsService; let userRepository: Repository; @@ -90,8 +87,14 @@ describe('OrganisationsService', () => { }); describe('create', () => { it('should create a new organisation', async () => { - const createOrganisationDto = { name: 'Test Org', email: 'test@example.com' }; const userId = 'user-id'; + const createOrganisationDto = { name: 'Test Org', email: 'test@example.com' }; + const createOrganisationPayload: CreateOrganisationRecordOptions = { + createPayload: { ...createOrganisationDto, userId }, + dbTransaction: { + useTransaction: false, + }, + }; const user = { id: userId }; const superAdminRole = { id: 'role-id', name: 'super_admin', description: '', permissions: [] }; const newOrganisation = { ...createOrganisationDto, id: 'org-id', owner: user }; @@ -108,7 +111,7 @@ describe('OrganisationsService', () => { jest.spyOn(organisationRepository, 'save').mockResolvedValue(newOrganisation as Organisation); jest.spyOn(organisationUserRole, 'save').mockResolvedValue(adminReponse); - const result = await service.create(createOrganisationDto, userId); + const result = await service.create(createOrganisationPayload); expect(result).toEqual( expect.objectContaining({ diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index 53c917c2f..0705b85e6 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -79,30 +79,28 @@ export default class UserService { return this.userRepository.save(newUser); } - private async getUserByEmail(email: string, manager?: EntityManager) { - const repo = manager ? manager.getRepository(User) : this.userRepository; - const user: UserResponseDTO = await repo.findOne({ + private async getUserByEmail(email: string) { + const user: UserResponseDTO = await this.userRepository.findOne({ where: { email: email }, relations: ['profile', 'owned_organisations'], }); return user; } - private async getUserById(identifier: string, manager?: EntityManager) { - const repo = manager ? manager.getRepository(User) : this.userRepository; - const user: UserResponseDTO = await repo.findOne({ + private async getUserById(identifier: string) { + const user: UserResponseDTO = await this.userRepository.findOne({ where: { id: identifier }, relations: ['profile', 'owned_organisations'], }); return user; } - async getUserRecord(identifierOptions: UserIdentifierOptionsType, manager?: EntityManager) { + async getUserRecord(identifierOptions: UserIdentifierOptionsType) { const { identifier, identifierType } = identifierOptions; const GetRecord = { - id: async () => this.getUserById(String(identifier), manager), - email: async () => this.getUserByEmail(String(identifier), manager), + id: async () => this.getUserById(String(identifier)), + email: async () => this.getUserByEmail(String(identifier)), }; return await GetRecord[identifierType](); diff --git a/src/shared/helpers/createRecordGeneric.ts b/src/shared/helpers/createRecordGeneric.ts new file mode 100644 index 000000000..1e4e1dfb2 --- /dev/null +++ b/src/shared/helpers/createRecordGeneric.ts @@ -0,0 +1,13 @@ +import { EntityManager } from 'typeorm'; + +export interface CreateRecordGeneric { + createPayload: RecordType; + dbTransaction: + | { + useTransaction: false; + } + | { + useTransaction: true; + transactionManager: EntityManager; + }; +} diff --git a/src/shared/inteceptors/response.interceptor.ts b/src/shared/inteceptors/response.interceptor.ts index 52ee1f74b..537a45a48 100644 --- a/src/shared/inteceptors/response.interceptor.ts +++ b/src/shared/inteceptors/response.interceptor.ts @@ -41,6 +41,7 @@ export class ResponseInterceptor implements NestInterceptor { response.setHeader('Content-Type', 'application/json'); if (typeof res === 'object') { const { message, ...data } = res; + console.log('response', res); return { status_code, From 3d149c8d706f3d4df3c50a39f638b8ae7047738e Mon Sep 17 00:00:00 2001 From: Khaybee Date: Sat, 1 Mar 2025 00:51:55 +0100 Subject: [PATCH 3/3] fix: fixed conflict from merge --- src/modules/auth/auth.service.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index cc13c1719..05f153123 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -73,11 +73,8 @@ export default class AuthenticationService { const newOrganisation = await this.organisationService.create(createOrganisationPayload); - const userOrganisations = await this.organisationService.getAllUserOrganisations(user.id); - + const userOrganisations = await this.organisationService.getAllUserOrganisations(user.id, 1, 10); const isSuperAdmin = userOrganisations.map(instance => instance.user_role).includes('super-admin'); - const userOranisations = await this.organisationService.getAllUserOrganisations(user.id, 1, 10); - const token = (await this.otpService.createOtp(user.id, manager)).token;