diff --git a/src/model/dto/register-user-dto.ts b/src/model/dto/register-user-dto.ts index 4489ddb..72343ee 100644 --- a/src/model/dto/register-user-dto.ts +++ b/src/model/dto/register-user-dto.ts @@ -29,6 +29,9 @@ export class RegisterUserDto extends BaseDto { ) @Prop() password!: string; + @IsOptional() + @Prop() + code!: string; constructor(init?: Partial) { super(); diff --git a/src/model/dto/user-profile-dto.ts b/src/model/dto/user-profile-dto.ts index 9922640..d6df97c 100644 --- a/src/model/dto/user-profile-dto.ts +++ b/src/model/dto/user-profile-dto.ts @@ -7,6 +7,8 @@ export class UserProfile { emailVerified!: boolean username!: string apiKey!: string; + instructions_url!: string | null | undefined; + token!: string; constructor(init?: Partial) { Object.assign(this, init); } diff --git a/src/service/user-management-service.ts b/src/service/user-management-service.ts index b07055a..4910e04 100644 --- a/src/service/user-management-service.ts +++ b/src/service/user-management-service.ts @@ -16,6 +16,7 @@ import { ProjectGroupRoleDto } from "../model/dto/project-group-role-dto"; import { environment } from "../environment/environment"; import { ResetCredentialsDto } from "../model/dto/reset-credentials-dto"; import projectgroupService from "./project-group-service"; +import { ReferralCodeDto } from "../model/dto/referral-code-dto"; export class UserManagementService implements IUserManagement { @@ -103,7 +104,20 @@ export class UserManagementService implements IUserManagement { async registerUser(user: RegisterUserDto): Promise { let userProfile = new UserProfile(); try { - const result: Response = await fetch(environment.registerUserUrl as string, { + let referralCodeDetails: ReferralCodeDto | undefined = undefined; + //Check if referral code is valid + if (user.code && user.code.length > 0) { + let referralCodeQuery = format('SELECT * FROM promo_referrals WHERE UPPER(code) = %L AND is_active = true AND valid_from <= NOW() AND valid_to >= NOW() limit 1', user.code.toUpperCase()); + + const referralCodeResult = await dbClient.query(referralCodeQuery); + if (referralCodeResult.rows.length == 0) { + throw new HttpException(410, "Invalid/Expired referral code"); + } + referralCodeDetails = ReferralCodeDto.from(referralCodeResult.rows[0]); + } + + + const result: Response = await fetch(environment.registerUserUrl, { method: 'post', body: JSON.stringify(user), headers: { 'Content-Type': 'application/json' } @@ -123,12 +137,32 @@ export class UserManagementService implements IUserManagement { const data = await result.json(); userProfile = new UserProfile(data); - //Assign user with default role and permissions - let queryStr = format(`INSERT INTO user_roles (user_id, project_group_id, role_id) - SELECT %L, project_group_id, role_id - FROM roles, project_group - WHERE roles.name = %L AND project_group.name = %L`, userProfile.id, Role.TDEI_MEMBER, DEFAULT_PROJECT_GROUP); - await dbClient.query(queryStr); + //If referral code is valid, then assign the user to the project group associated with referral code with TDEI member role + //Else assign the user to default project group with TDEI member role + if (referralCodeDetails) { + const rolesDetails = await this.getRolesByNames([Role.TDEI_MEMBER]); + const role_id = rolesDetails.get(Role.TDEI_MEMBER); + let addRoleProjectQuery = format(`INSERT INTO user_roles (user_id, project_group_id, role_id) VALUES %L ON CONFLICT ON CONSTRAINT unq_user_role_project_group DO NOTHING`, [[userProfile.id, referralCodeDetails.project_group_id, role_id]]); + await dbClient.query(addRoleProjectQuery); + + //Update the instructions url in the user profile if exists + userProfile.instructions_url = referralCodeDetails.instructions_url; + //Generate the auth token and assign refresh token to user profile + const loginDto = new LoginDto(); + loginDto.username = user.email; + loginDto.password = user.password; + const authResponse = await this.login(loginDto); + userProfile.token = authResponse.refresh_token; + + } + else { + //Assign user with default role and permissions + let queryStr = format(`INSERT INTO user_roles (user_id, project_group_id, role_id) + SELECT %L, project_group_id, role_id + FROM roles, project_group + WHERE roles.name = %L AND project_group.name = %L`, userProfile.id, Role.TDEI_MEMBER, DEFAULT_PROJECT_GROUP); + await dbClient.query(queryStr); + } } catch (error: any) { console.error(error); diff --git a/test/unit/user-management.service.test.ts b/test/unit/user-management.service.test.ts index 7b1b771..bc26311 100644 --- a/test/unit/user-management.service.test.ts +++ b/test/unit/user-management.service.test.ts @@ -13,6 +13,11 @@ import { ForeignKeyException } from "../../src/exceptions/http/http-exceptions"; import { ResetCredentialsDto } from "../../src/model/dto/reset-credentials-dto"; import projectgroupService from "../../src/service/project-group-service"; +beforeEach(() => { + jest.clearAllMocks(); + fetchMock.resetMocks(); +}); + // group test using describe describe("User Management Service Test", () => { @@ -118,6 +123,53 @@ describe("User Management Service Test", () => { expect(getDbSpy).toHaveBeenCalledTimes(1); }); + test("When requested with promo code, Expect to return user profile response on success with instruction url (optional) and token ", async () => { + //Arrange + let newuser = new RegisterUserDto({ + firstName: "firstname", + lastName: "lastname", + email: "email", + phone: "phone", + password: "password", + code: "PROMO123" + }); + fetchMock.mockResolvedValueOnce(Promise.resolve({ + status: 200, + json: () => Promise.resolve({ + firstName: "firstname", + lastName: "lastname", + email: "email", + phone: "phone", + id: "id", + username: "email", + emailVerified: true, + apiKey: "apiKey", + instructions_url: "http://example.com/instructions", + token: "token" + }), + })); + const getDbPromoSpy = jest + .spyOn(dbClient, "query") + .mockResolvedValueOnce({ rows: [{ code: 'PROMO123' }] }); + const getDbRoleByNamesSpy = jest + .spyOn(dbClient, "query") + .mockResolvedValueOnce({ rows: [{ role_id: 'role_id_1', name: Role.TDEI_MEMBER }] }); + const getDbSpy = jest + .spyOn(dbClient, "query") + .mockResolvedValueOnce({}); + const getLoginSpy = jest + .spyOn(userManagementServiceInstance, "login") + .mockResolvedValueOnce({ refresh_token: "refresh_token", access_token: "access_token" }); + //Act + let result = await userManagementServiceInstance.registerUser(newuser); + //Assert + // expect(result.apiKey).toBe("apiKey"); + expect(getDbPromoSpy).toHaveBeenCalled(); + expect(getDbSpy).toHaveBeenCalled(); + expect(getLoginSpy).toHaveBeenCalled(); + expect(getDbRoleByNamesSpy).toHaveBeenCalled(); + }); + test("When user already exists with same email, Expect to throw error", async () => { //Arrange let newuser = new RegisterUserDto({ @@ -136,6 +188,25 @@ describe("User Management Service Test", () => { await expect(userManagementServiceInstance.registerUser(newuser)).rejects.toThrow(Error); }); + test("When promo code is invalid, Expect to throw error", async () => { + //Arrange + let newuser = new RegisterUserDto({ + firstName: "firstname", + lastName: "lastname", + email: "email", + phone: "phone", + password: "password", + code: "INVALIDCODE" + }); + fetchMock.mockResolvedValueOnce(Promise.resolve({ + status: 410, + json: () => Promise.resolve("Invalid/Expired referral code"), + })); + //Act + //Assert + await expect(userManagementServiceInstance.registerUser(newuser)).rejects.toThrow(Error); + }); + test("When error registering, Expect to throw error", async () => { //Arrange let newuser = new RegisterUserDto({