Skip to content

Commit

Permalink
Merge pull request #234 from ReseauEntourage/feat/EN-7598-Referer-Ref…
Browse files Browse the repository at this point in the history
…er-Candidate

feat(Refering): Add refer candidate api endpoint
  • Loading branch information
guillobits authored Nov 27, 2024
2 parents dab413c + e5e6163 commit 780bae3
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 14 deletions.
9 changes: 9 additions & 0 deletions src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,15 @@ export class AuthController {
throw new NotFoundException();
}

await this.authService.sendWelcomeMail({
id: updatedUser.id,
firstName: updatedUser.firstName,
role: updatedUser.role,
zone: updatedUser.zone,
email: updatedUser.email,
});
await this.authService.sendOnboardingJ1BAOMail(updatedUser);
await this.authService.sendOnboardingJ3ProfileCompletionMail(updatedUser);
await this.authService.sendRefererCandidateHasVerifiedAccountMail(
updatedUser.referer,
updatedUser
Expand Down
14 changes: 14 additions & 0 deletions src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,18 @@ export class AuthService {
}
);
}

async sendWelcomeMail(
user: Pick<User, 'id' | 'firstName' | 'role' | 'zone' | 'email'>
) {
return this.mailsService.sendWelcomeMail(user);
}

async sendOnboardingJ1BAOMail(user: User) {
return this.mailsService.sendOnboardingJ1BAOMail(user);
}

async sendOnboardingJ3ProfileCompletionMail(user: User) {
return this.mailsService.sendOnboardingJ3ProfileCompletionMail(user);
}
}
20 changes: 8 additions & 12 deletions src/mails/mails.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ export class MailsService {
});
}

async sendWelcomeMail(user: User) {
async sendWelcomeMail(
user: Pick<User, 'id' | 'firstName' | 'role' | 'zone' | 'email'>
) {
const { candidatesAdminMail } = getAdminMailsFromZone(user.zone);

if (user.role === UserRoles.COACH) {
Expand Down Expand Up @@ -833,33 +835,27 @@ export class MailsService {
refererFirstName: referer.firstName,
candidateFirstName: candidate.firstName,
candidateLastName: candidate.lastName,
loginUrl: `${process.env.FRONTEND_URL}/login`,
loginUrl: `${process.env.FRONT_URL}/login`,
zone: referer.zone,
},
});
}

// TODO: Call this method after completing refering funnel
// Generate a token with
// const token = await this.authService.generateVerificationToken(candidate);
async sendReferedCandidateFinalizeAccountMail(
// TODO after merge
// referer: UserWithOrganization,
referer: User,
candidate: User,
token: string
) {
await this.queuesService.addToWorkQueue(Jobs.SEND_MAIL, {
toEmail: referer.email,
toEmail: candidate.email,
templateId: MailjetTemplates.REFERED_CANDIDATE_FINALIZE_ACCOUNT,
variables: {
id: candidate.id,
candidateFirstName: candidate.firstName,
refererFirstName: referer.firstName,
refererLastName: referer.lastName,
// organizationName: referer.organization.name,
// TODO after merge
// structure: referer.structure,
finalizeAccountUrl: `${process.env.FRONTEND_URL}/finaliser-compte-oriente?token=${token}`,
organizationName: referer.organization.name,
finalizeAccountUrl: `${process.env.FRONT_URL}/finaliser-compte-oriente?token=${token}`,
zone: candidate.zone,
},
});
Expand Down
101 changes: 101 additions & 0 deletions src/users-creation/dto/create-user-refering.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { ApiProperty, PickType } from '@nestjs/swagger';
import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator';
import { Ambition } from 'src/common/ambitions/models';
import { BusinessLine } from 'src/common/business-lines/models';
import { Department } from 'src/common/locations/locations.types';
import {
CandidateAccommodation,
CandidateResource,
CandidateYesNoNSPPValue,
JobSearchDuration,
Nationality,
StudiesLevel,
WorkingExperience,
YesNoJNSPRValue,
} from 'src/contacts/contacts.types';
import { HelpNeed } from 'src/user-profiles/models';
import { User } from 'src/users/models';
import { Gender, Program } from 'src/users/users.types';

export class CreateUserReferingDto extends PickType(User, [
'firstName',
'lastName',
'email',
'gender',
'phone',
'password',
] as const) {
@ApiProperty()
@IsNumber()
gender: Gender;

@ApiProperty()
@IsString()
program: Program;

@ApiProperty()
@IsString()
birthDate: Date;

@ApiProperty()
@IsString()
department: Department;

@ApiProperty()
@IsString()
@IsOptional()
campaign?: string;

@ApiProperty()
@IsArray()
helpNeeds?: HelpNeed[];

@ApiProperty()
@IsString()
workingRight?: CandidateYesNoNSPPValue;

@ApiProperty()
@IsArray()
@IsOptional()
searchBusinessLines?: BusinessLine[];

@ApiProperty()
@IsArray()
@IsOptional()
searchAmbitions?: Ambition[];

@ApiProperty()
@IsString()
@IsOptional()
nationality?: Nationality;

@ApiProperty()
@IsString()
@IsOptional()
accommodation?: CandidateAccommodation;

@ApiProperty()
@IsString()
@IsOptional()
hasSocialWorker?: YesNoJNSPRValue;

@ApiProperty()
@IsString()
@IsOptional()
resources?: CandidateResource;

@ApiProperty()
@IsString()
@IsOptional()
studiesLevel?: StudiesLevel;

@ApiProperty()
@IsString()
@IsOptional()
workingExperience?: WorkingExperience;

@ApiProperty()
@IsString()
@IsOptional()
jobSearchDuration?: JobSearchDuration;
}
39 changes: 39 additions & 0 deletions src/users-creation/dto/create-user-refering.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* eslint-disable @typescript-eslint/ban-types */
import {
ArgumentMetadata,
BadRequestException,
PipeTransform,
} from '@nestjs/common';
import { plainToInstance } from 'class-transformer';
import { validate } from 'class-validator';
import { CreateUserReferingDto } from './create-user-refering.dto';

export class CreateUserReferingPipe
implements
PipeTransform<CreateUserReferingDto, Promise<CreateUserReferingDto>>
{
private static toValidate(metatype: Function): boolean {
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}

async transform(
value: CreateUserReferingDto,
{ metatype }: ArgumentMetadata
): Promise<CreateUserReferingDto> {
if (!metatype || !CreateUserReferingPipe.toValidate(metatype)) {
return value;
}
const object = plainToInstance(metatype, value);
const errors = await validate(object, {
whitelist: true,
forbidNonWhitelisted: true,
forbidUnknownValues: true,
});

if (errors.length > 0) {
throw new BadRequestException();
}
return value;
}
}
90 changes: 89 additions & 1 deletion src/users-creation/users-creation.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { ApiTags } from '@nestjs/swagger';
import { Throttle } from '@nestjs/throttler';
import { encryptPassword } from 'src/auth/auth.utils';
import { Public } from 'src/auth/guards';
import { Public, UserPayload } from 'src/auth/guards';
import { UserPermissions, UserPermissionsGuard } from 'src/users/guards';
import { User } from 'src/users/models';
import {
Expand All @@ -33,6 +33,8 @@ import {
CreateUserRegistrationDto,
CreateUserRegistrationPipe,
} from './dto';
import { CreateUserReferingDto } from './dto/create-user-refering.dto';
import { CreateUserReferingPipe } from './dto/create-user-refering.pipe';
import { UsersCreationService } from './users-creation.service';

function generateFakePassword() {
Expand Down Expand Up @@ -256,4 +258,90 @@ export class UsersCreationController {
}
}
}

@UserPermissions(Permissions.REFERER)
@UseGuards(UserPermissionsGuard)
@Throttle(10, 60)
@Post('refering')
async createUserRefering(
@Body(new CreateUserReferingPipe())
createUserReferingDto: CreateUserReferingDto,
@UserPayload()
referer: User
) {
if (!isValidPhone(createUserReferingDto.phone)) {
throw new BadRequestException();
}

if (!createUserReferingDto.program) {
throw new BadRequestException();
}

const userRandomPassword = generateFakePassword();
const { hash, salt } = encryptPassword(userRandomPassword);

const zone = getZoneFromDepartment(createUserReferingDto.department);

const userToCreate: Partial<User> = {
refererId: referer.id,
OrganizationId: referer.OrganizationId,
firstName: createUserReferingDto.firstName,
lastName: createUserReferingDto.lastName,
email: createUserReferingDto.email,
role: UserRoles.CANDIDATE,
gender: createUserReferingDto.gender,
phone: createUserReferingDto.phone,
address: null,
adminRole: null,
zone,
password: hash,
salt,
};

try {
const { id: createdUserId } = await this.usersCreationService.createUser(
userToCreate
);

await this.usersCreationService.updateUserProfileByUserId(createdUserId, {
department: createUserReferingDto.department,
helpNeeds: createUserReferingDto.helpNeeds,
searchBusinessLines: createUserReferingDto.searchBusinessLines,
searchAmbitions: createUserReferingDto.searchAmbitions,
});

const createdUser = await this.usersCreationService.findOneUser(
createdUserId
);

await this.usersCreationService.createExternalDBUser(createdUserId, {
program: createUserReferingDto.program,
birthDate: createUserReferingDto.birthDate,
campaign:
createUserReferingDto.program === Programs.THREE_SIXTY
? createUserReferingDto.campaign
: undefined,
workingRight: createUserReferingDto.workingRight,
nationality: createUserReferingDto.nationality,
accommodation: createUserReferingDto.accommodation,
hasSocialWorker: createUserReferingDto.hasSocialWorker,
resources: createUserReferingDto.resources,
studiesLevel: createUserReferingDto.studiesLevel,
workingExperience: createUserReferingDto.workingExperience,
jobSearchDuration: createUserReferingDto.jobSearchDuration,
gender: createUserReferingDto.gender,
});

await this.usersCreationService.sendFinalizeAccountReferedUser(
createdUser,
referer
);

return createdUser;
} catch (err) {
if (((err as Error).name = SequelizeUniqueConstraintError)) {
throw new ConflictException();
}
}
}
}
9 changes: 9 additions & 0 deletions src/users-creation/users-creation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ export class UsersCreationService {
return this.mailsService.sendVerificationMail(user, token);
}

async sendFinalizeAccountReferedUser(candidate: User, referer: User) {
const token = await this.authService.generateVerificationToken(candidate);
return this.mailsService.sendReferedCandidateFinalizeAccountMail(
referer,
candidate,
token
);
}

async sendOnboardingJ3ProfileCompletionMail(user: User) {
return this.mailsService.sendOnboardingJ3ProfileCompletionMail(user);
}
Expand Down
2 changes: 1 addition & 1 deletion src/users/users.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export type Permission = (typeof Permissions)[keyof typeof Permissions];

export const UserPermissions: { [K in UserRole]: Permission | Permission[] } = {
[UserRoles.CANDIDATE]: Permissions.CANDIDATE,
[UserRoles.REFERER]: Permissions.COACH,
[UserRoles.REFERER]: Permissions.REFERER,
[UserRoles.COACH]: [Permissions.COACH, Permissions.RESTRICTED_COACH],
[UserRoles.ADMIN]: Permissions.ADMIN,
};
Expand Down

0 comments on commit 780bae3

Please sign in to comment.