Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor auth + onboarding flow #318

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apps/server/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ model Employee {
firstName String @db.VarChar(255)
lastName String @db.VarChar(255)
email String @unique @db.VarChar(255)
signatureLink String @db.VarChar(255)
signatureLink String? @db.VarChar(255)
scope EmployeeScope @default(BASE_USER)
pswdHash String? @db.VarChar(255)
createdAt DateTime @default(now())
Expand All @@ -75,8 +75,8 @@ model Employee {

originatedForms FormInstance[]

position Position @relation(fields: [positionId], references: [id])
positionId String @db.Uuid
position Position? @relation(fields: [positionId], references: [id])
positionId String? @db.Uuid

signerEmployeeAssignedGroups AssignedGroup[] @relation("signerEmployee")
signerEmployeeListAssignedGroups AssignedGroup[] @relation("signerEmployeeList")
Expand Down
20 changes: 7 additions & 13 deletions apps/server/src/app.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { EmployeesService } from './employees/employees.service';
import { jwtDecode } from 'jwt-decode';
import { RegisterEmployeeDto } from './auth/dto/register-employee.dto';
import { CreateEmployeeDto } from './employees/dto/create-employee.dto';
import { EmployeeScope } from '@prisma/client';

@Controller()
export class AppController {
Expand Down Expand Up @@ -130,23 +131,16 @@ export class AppController {
@Body(new ValidationPipe({ transform: true }))
registerEmployeeDto: RegisterEmployeeDto,
) {
const { positionName, departmentName, ...employeeDto } =
registerEmployeeDto;

const createEmployeeDtoInstance: CreateEmployeeDto = {
firstName: employeeDto.firstName,
lastName: employeeDto.lastName,
email: employeeDto.email,
password: employeeDto.password,
signatureLink: employeeDto.signatureLink,
positionId: '',
scope: employeeDto.scope,
firstName: registerEmployeeDto.firstName,
lastName: registerEmployeeDto.lastName,
email: registerEmployeeDto.email,
password: registerEmployeeDto.password,
scope: EmployeeScope.BASE_USER,
};

const newEmployee = await this.authService.register(
const newEmployee = await this.employeeService.create(
createEmployeeDtoInstance,
positionName,
departmentName,
);

return new EmployeeEntity(newEmployee);
Expand Down
35 changes: 2 additions & 33 deletions apps/server/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,12 @@ import { EmployeesService } from '../employees/employees.service';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
import { EmployeeEntity } from '../employees/entities/employee.entity';
import { CreateEmployeeDto } from '../employees/dto/create-employee.dto';
import { DepartmentsService } from '../departments/departments.service';
import { PositionsService } from '../positions/positions.service';
import { Department, EmployeeScope, Position } from '@prisma/client';
import { EmployeeScope } from '@prisma/client';

@Injectable()
export class AuthService {
constructor(
private employeesService: EmployeesService,
private departmentsService: DepartmentsService,
private positionsService: PositionsService,
private jwtService: JwtService,
) {}

Expand Down Expand Up @@ -65,7 +60,7 @@ export class AuthService {
lastName: user.lastName,
sub: user.id,
positionId: user.positionId,
departmentId: user.position.departmentId,
departmentId: user.position?.departmentId,
scope: user.scope,
};

Expand All @@ -85,30 +80,4 @@ export class AuthService {
refreshToken,
};
}

/**
* Register a new employee.
* @param createEmployeeDto the employee's data
* @param positionName the employee's position name
* @param departmentName the employee's department name
* @returns the new employee
*/
async register(
createEmployeeDto: CreateEmployeeDto,
positionName: string,
departmentName: string,
) {
const department: Department =
await this.departmentsService.findOrCreateOneByName(departmentName);

const position: Position =
await this.positionsService.findOrCreateOneByNameInDepartment(
positionName,
department.id,
);

createEmployeeDto.positionId = position.id;
const newEmployee = await this.employeesService.create(createEmployeeDto);
return newEmployee;
}
}
20 changes: 0 additions & 20 deletions apps/server/src/auth/dto/register-employee.dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ApiProperty } from '@nestjs/swagger';
import { EmployeeScope } from '@prisma/client';
import { IsNotEmpty, IsString, MinLength, IsEmail } from 'class-validator';

export class RegisterEmployeeDto {
Expand All @@ -23,23 +22,4 @@ export class RegisterEmployeeDto {
@MinLength(5)
@ApiProperty()
password: string;

@IsString()
@IsNotEmpty()
@ApiProperty()
positionName: string;

@IsString()
@IsNotEmpty()
@ApiProperty()
departmentName: string;

@IsString()
@IsNotEmpty()
@ApiProperty()
signatureLink: string;

@IsNotEmpty()
@ApiProperty({ enum: EmployeeScope })
scope: EmployeeScope;
}
8 changes: 1 addition & 7 deletions apps/server/src/employees/dto/create-employee.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ export class CreateEmployeeDto {
lastName: string;

@IsUUID()
@IsNotEmpty()
@ApiProperty()
positionId: string;
positionId?: string;

@IsEmail()
@IsNotEmpty()
Expand All @@ -35,11 +34,6 @@ export class CreateEmployeeDto {
@ApiProperty()
password: string;

@IsString()
@IsNotEmpty()
@ApiProperty()
signatureLink: string;

@IsString()
@IsNotEmpty()
@ApiProperty({ enum: EmployeeScope })
Expand Down
14 changes: 14 additions & 0 deletions apps/server/src/employees/dto/onboard-employee.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNotEmpty } from 'class-validator';

export class OnboardEmployeeDto {
@IsString()
@IsNotEmpty()
@ApiProperty()
signatureLink: string;

@IsString()
@IsNotEmpty()
@ApiProperty()
positionId: string;
}
21 changes: 21 additions & 0 deletions apps/server/src/employees/employees.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { UserEntity } from '../auth/entities/user.entity';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { LoggerServiceImpl } from '../logger/logger.service';
import { AdminAuthGuard } from '../auth/guards/admin-auth.guard';
import { OnboardEmployeeDto } from './dto/onboard-employee.dto';

@ApiTags('employees')
@Controller('employees')
Expand Down Expand Up @@ -61,6 +62,26 @@ export class EmployeesController {
return new EmployeeEntity(newEmployee);
}

@Patch('/onboarding')
@UseGuards(JwtAuthGuard)
@ApiCreatedResponse({ type: EmployeeEntity })
@ApiForbiddenResponse({ description: AppErrorMessage.FORBIDDEN })
@ApiUnprocessableEntityResponse({
description: AppErrorMessage.UNPROCESSABLE_ENTITY,
})
@ApiBadRequestResponse({ description: AppErrorMessage.UNPROCESSABLE_ENTITY })
async onboardEmployee(
@AuthUser() currentUser: UserEntity,
@Body(new ValidationPipe({ transform: true }))
onboardEmployeeDto: OnboardEmployeeDto,
) {
const onboardedEmployee = await this.employeesService.update(
currentUser.id,
onboardEmployeeDto,
);
return new EmployeeEntity(onboardedEmployee);
}

@Get()
@UseGuards(JwtAuthGuard)
@ApiOkResponse({ type: [EmployeeEntity] })
Expand Down
1 change: 1 addition & 0 deletions apps/server/src/employees/employees.errors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export enum EmployeeErrorMessage {
EMPLOYEE_NOT_FOUND = 'Employee could not be found with this email',
EMPLOYEE_NOT_FOUND_CLIENT = 'Employee could not be found',
EMPLOYEE_NOT_ONBOARDED = 'Employee has not been onboarded',
}
2 changes: 0 additions & 2 deletions apps/server/src/employees/employees.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ export class EmployeesService {
firstName: createEmployeeDto.firstName,
lastName: createEmployeeDto.lastName,
email: createEmployeeDto.email,
positionId: createEmployeeDto.positionId,
signatureLink: createEmployeeDto.signatureLink,
pswdHash: await bcrypt.hash(
createEmployeeDto.password,
await bcrypt.genSalt(),
Expand Down
6 changes: 3 additions & 3 deletions apps/server/src/employees/entities/employee.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ export class EmployeeBaseEntity implements Employee {
lastName: string;

@Exclude()
positionId: string;
positionId: string | null;

@ApiProperty()
email: string;

@ApiProperty()
signatureLink: string;
signatureLink: string | null;

@ApiProperty({ enum: EmployeeScope })
scope: EmployeeScope;
Expand All @@ -44,7 +44,7 @@ export class EmployeeBaseEntity implements Employee {

export class EmployeeEntity extends EmployeeBaseEntity {
@ApiProperty()
position: PositionBaseEntity;
position: PositionBaseEntity | null;

constructor(partial: Partial<EmployeeEntity>) {
super(partial);
Expand Down
12 changes: 12 additions & 0 deletions apps/server/src/form-instances/form-instances.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,12 @@ export class FormInstancesService {
throw new NotFoundException(EmployeeErrorMessage.EMPLOYEE_NOT_FOUND);
}

if (!employee.positionId || !employee.position) {
throw new BadRequestException(
EmployeeErrorMessage.EMPLOYEE_NOT_ONBOARDED,
);
}

const formInstances = await this.prisma.formInstance.findMany({
where: {
assignedGroups: {
Expand Down Expand Up @@ -537,6 +543,12 @@ export class FormInstancesService {
where: { id: currentUser.id },
});

if (!employee.positionId) {
throw new BadRequestException(
EmployeeErrorMessage.EMPLOYEE_NOT_ONBOARDED,
);
}

const position = await this.prisma.position.findFirstOrThrow({
where: { id: employee.positionId },
});
Expand Down
2 changes: 1 addition & 1 deletion apps/web/openapi-ts.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default {
path: './src/client',
},
plugins: [
'@hey-api/client-fetch',
'@hey-api/client-axios',
'@tanstack/react-query',
'@hey-api/schemas',
{
Expand Down
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@emotion/react": "11.10.5",
"@emotion/styled": "11.10.5",
"@fontsource/hanken-grotesk": "4.5.2",
"@hey-api/client-axios": "0.6.1",
"@tanstack/react-query": "5.0.5",
"@trpc/client": "10.38.0",
"@trpc/server": "10.38.0",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/authConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,5 @@ export const loginRequest = {

export const graphConfig = {
graphMeEndpoint:
'https://graph.microsoft.com/v1.0/me?$select=id,displayName,department,jobTitle,givenName,surname,userPrincipalName',
'https://graph.microsoft.com/v1.0/me?$select=id,displayName,department,jobTitle,givenName,surname,userPrincipalName,mail',
};
Loading