because I got bored of customising my CV for every job
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(services): integrate mappers into job experience services

- Inject mappers into all job experience services
- Replace manual entity creation with mapper.toDomain() calls
- Remove private mapToDomain methods from services
- Update UserJobExperienceService with proper DTOs and findForUser method
- Replace any types with proper Prisma DTOs for create/update operations
- Implement domain-driven design with full entities instead of IDs

+104 -206
+12 -27
apps/server/src/modules/job-experience/company/company.service.ts
··· 1 1 import { Injectable, NotFoundException } from "@nestjs/common"; 2 - import type { Company as PrismaCompany } from "@prisma/client"; 3 2 import { PrismaService } from "../../database/prisma.service"; 4 3 import type { CreateCompanyDto, UpdateCompanyDto } from "./company.dto"; 5 4 import { Company } from "./company.entity"; 5 + import { CompanyMapper } from "./company.mapper"; 6 6 7 7 @Injectable() 8 8 export class CompanyService { 9 - constructor(private prisma: PrismaService) {} 10 - 11 - private mapToDomain({ 12 - id, 13 - name, 14 - createdAt, 15 - updatedAt, 16 - description, 17 - website, 18 - }: PrismaCompany): Company { 19 - return new Company( 20 - id, 21 - name, 22 - createdAt, 23 - updatedAt, 24 - description ?? undefined, 25 - website ?? undefined, 26 - ); 27 - } 9 + constructor( 10 + private prisma: PrismaService, 11 + private companyMapper: CompanyMapper, 12 + ) {} 28 13 29 14 async create(data: CreateCompanyDto): Promise<Company> { 30 15 const prismaCompany = await this.prisma.company.create({ 31 16 data, 32 17 }); 33 - return this.mapToDomain(prismaCompany); 18 + return this.companyMapper.toDomain(prismaCompany); 34 19 } 35 20 36 21 async findAll(): Promise<Company[]> { 37 22 const prismaCompanies = await this.prisma.company.findMany({ 38 23 orderBy: { name: "asc" }, 39 24 }); 40 - return prismaCompanies.map((company) => this.mapToDomain(company)); 25 + return this.companyMapper.mapToDomain(prismaCompanies); 41 26 } 42 27 43 28 async findById(id: string): Promise<Company | null> { 44 29 const prismaCompany = await this.prisma.company.findUnique({ 45 30 where: { id }, 46 31 }); 47 - return prismaCompany ? this.mapToDomain(prismaCompany) : null; 32 + return this.companyMapper.toDomain(prismaCompany); 48 33 } 49 34 50 35 async findByIdOrFail(id: string): Promise<Company> { ··· 56 41 throw new NotFoundException(`Company with id ${id} not found`); 57 42 } 58 43 59 - return this.mapToDomain(prismaCompany); 44 + return this.companyMapper.toDomain(prismaCompany); 60 45 } 61 46 62 47 async findByName(name: string): Promise<Company | null> { 63 48 const prismaCompany = await this.prisma.company.findUnique({ 64 49 where: { name }, 65 50 }); 66 - return prismaCompany ? this.mapToDomain(prismaCompany) : null; 51 + return this.companyMapper.toDomain(prismaCompany); 67 52 } 68 53 69 54 async findByNameOrFail(name: string): Promise<Company> { ··· 75 60 throw new NotFoundException(`Company with name ${name} not found`); 76 61 } 77 62 78 - return this.mapToDomain(prismaCompany); 63 + return this.companyMapper.toDomain(prismaCompany); 79 64 } 80 65 81 66 async update( ··· 86 71 where: { id }, 87 72 data: updateCompanyDto, 88 73 }); 89 - return this.mapToDomain(prismaCompany); 74 + return this.companyMapper.toDomain(prismaCompany); 90 75 } 91 76 92 77 async delete(id: string): Promise<void> {
+56 -122
apps/server/src/modules/job-experience/employment/user-job-experience.service.ts
··· 1 1 import { Injectable, NotFoundException } from "@nestjs/common"; 2 - import type { 3 - Company as PrismaCompany, 4 - Level as PrismaLevel, 5 - Role as PrismaRole, 6 - Skill as PrismaSkill, 7 - User as PrismaUser, 8 - UserJobExperience as PrismaUserJobExperience, 9 - } from "@prisma/client"; 10 - import { User } from "../../auth/user.entity"; 11 2 import { PrismaService } from "../../database/prisma.service"; 12 - import { Company } from "../company/company.entity"; 13 - import { Level } from "../level/level.entity"; 14 - import { Role } from "../role/role.entity"; 15 - import { Skill } from "../skill/skill.entity"; 16 3 import type { 17 4 CreateUserJobExperienceDto, 18 5 UpdateUserJobExperienceDto, 19 6 } from "./user-job-experience.dto"; 20 7 import { UserJobExperience } from "./user-job-experience.entity"; 8 + import { UserJobExperienceMapper } from "./user-job-experience.mapper"; 9 + 10 + // Proper types for Prisma operations 11 + type PrismaUserJobExperienceCreateData = { 12 + userId: string; 13 + companyId: string; 14 + roleId: string; 15 + levelId: string; 16 + startDate: Date; 17 + endDate?: Date; 18 + description?: string; 19 + skills?: { 20 + connect: Array<{ id: string }>; 21 + }; 22 + }; 23 + 24 + type PrismaUserJobExperienceUpdateData = { 25 + companyId?: string; 26 + roleId?: string; 27 + levelId?: string; 28 + startDate?: Date; 29 + endDate?: Date; 30 + description?: string; 31 + skills?: { 32 + set: Array<{ id: string }>; 33 + }; 34 + }; 21 35 22 36 @Injectable() 23 37 export class UserJobExperienceService { 24 - constructor(private prisma: PrismaService) {} 38 + constructor( 39 + private prisma: PrismaService, 40 + private userJobExperienceMapper: UserJobExperienceMapper, 41 + ) {} 25 42 26 - private mapToDomain({ 27 - id, 28 - userId, 29 - companyId, 30 - roleId, 31 - levelId, 32 - startDate, 33 - createdAt, 34 - updatedAt, 35 - endDate, 36 - description, 37 - user, 38 - company, 39 - role, 40 - level, 41 - skills, 42 - }: PrismaUserJobExperience & { 43 - user: PrismaUser; 44 - company: PrismaCompany; 45 - role: PrismaRole; 46 - level: PrismaLevel; 47 - skills?: PrismaSkill[]; 48 - }): UserJobExperience { 49 - return new UserJobExperience( 50 - id, 51 - userId, 52 - companyId, 53 - roleId, 54 - levelId, 55 - startDate, 56 - createdAt, 57 - updatedAt, 58 - new User( 59 - user.id, 60 - user.email, 61 - user.name, 62 - user.password, 63 - user.createdAt, 64 - user.updatedAt, 65 - ), 66 - new Company( 67 - company.id, 68 - company.name, 69 - company.createdAt, 70 - company.updatedAt, 71 - company.description ?? undefined, 72 - company.website ?? undefined, 73 - ), 74 - new Role( 75 - role.id, 76 - role.name, 77 - role.createdAt, 78 - role.updatedAt, 79 - role.description ?? undefined, 80 - ), 81 - new Level( 82 - level.id, 83 - level.name, 84 - level.createdAt, 85 - level.updatedAt, 86 - level.description ?? undefined, 87 - ), 88 - endDate ?? undefined, 89 - description ?? undefined, 90 - skills 91 - ? skills.map( 92 - ({ id, name, createdAt, updatedAt, description }: PrismaSkill) => 93 - new Skill( 94 - id, 95 - name, 96 - createdAt, 97 - updatedAt, 98 - description ?? undefined, 99 - ), 100 - ) 101 - : undefined, 102 - ); 103 - } 104 43 105 44 async create( 106 45 createUserJobExperienceDto: CreateUserJobExperienceDto, 107 46 ): Promise<UserJobExperience> { 108 - const createData: any = { 109 - userId: createUserJobExperienceDto.userId, 110 - companyId: createUserJobExperienceDto.companyId, 111 - roleId: createUserJobExperienceDto.roleId, 112 - levelId: createUserJobExperienceDto.levelId, 47 + const createData: PrismaUserJobExperienceCreateData = { 48 + userId: createUserJobExperienceDto.user.id, 49 + companyId: createUserJobExperienceDto.company.id, 50 + roleId: createUserJobExperienceDto.role.id, 51 + levelId: createUserJobExperienceDto.level.id, 113 52 startDate: createUserJobExperienceDto.startDate, 114 53 }; 115 54 ··· 119 58 if (createUserJobExperienceDto.description !== undefined) { 120 59 createData.description = createUserJobExperienceDto.description; 121 60 } 122 - if (createUserJobExperienceDto.skillIds !== undefined) { 61 + if (createUserJobExperienceDto.skills !== undefined) { 123 62 createData.skills = { 124 - connect: createUserJobExperienceDto.skillIds.map((id) => ({ id })), 63 + connect: createUserJobExperienceDto.skills.map((skill) => ({ id: skill.id })), 125 64 }; 126 65 } 127 66 128 67 const prismaExperience = await this.prisma.userJobExperience.create({ 129 68 data: createData, 130 69 include: { 131 - user: true, 132 70 company: true, 133 71 role: true, 134 72 level: true, 135 73 skills: true, 136 74 }, 137 75 }); 138 - return this.mapToDomain(prismaExperience); 76 + return this.userJobExperienceMapper.toDomain(prismaExperience); 139 77 } 140 78 141 - async findByUserId(userId: string): Promise<UserJobExperience[]> { 79 + async findForUser(user: { id: string }): Promise<UserJobExperience[]> { 142 80 const prismaExperiences = await this.prisma.userJobExperience.findMany({ 143 - where: { userId }, 81 + where: { userId: user.id }, 144 82 include: { 145 - user: true, 146 83 company: true, 147 84 role: true, 148 85 level: true, ··· 150 87 }, 151 88 orderBy: { startDate: "desc" }, 152 89 }); 153 - return prismaExperiences.map((exp) => this.mapToDomain(exp)); 90 + return this.userJobExperienceMapper.mapToDomain(prismaExperiences); 154 91 } 155 92 156 93 async findById(id: string): Promise<UserJobExperience | null> { 157 94 const prismaExperience = await this.prisma.userJobExperience.findUnique({ 158 95 where: { id }, 159 96 include: { 160 - user: true, 161 97 company: true, 162 98 role: true, 163 99 level: true, 164 100 skills: true, 165 101 }, 166 102 }); 167 - return prismaExperience ? this.mapToDomain(prismaExperience) : null; 103 + return this.userJobExperienceMapper.toDomain(prismaExperience); 168 104 } 169 105 170 106 async findByIdOrFail(id: string): Promise<UserJobExperience> { 171 107 const prismaExperience = await this.prisma.userJobExperience.findUnique({ 172 108 where: { id }, 173 109 include: { 174 - user: true, 175 110 company: true, 176 111 role: true, 177 112 level: true, ··· 183 118 throw new NotFoundException(`Job experience with id ${id} not found`); 184 119 } 185 120 186 - return this.mapToDomain(prismaExperience); 121 + return this.userJobExperienceMapper.toDomain(prismaExperience); 187 122 } 188 123 189 124 async update( 190 125 id: string, 191 126 updateUserJobExperienceDto: UpdateUserJobExperienceDto, 192 127 ): Promise<UserJobExperience> { 193 - const updateData: any = {}; 128 + const updateData: PrismaUserJobExperienceUpdateData = {}; 194 129 195 - if (updateUserJobExperienceDto.companyId !== undefined) { 196 - updateData.companyId = updateUserJobExperienceDto.companyId; 130 + if (updateUserJobExperienceDto.company !== undefined) { 131 + updateData.companyId = updateUserJobExperienceDto.company.id; 197 132 } 198 - if (updateUserJobExperienceDto.roleId !== undefined) { 199 - updateData.roleId = updateUserJobExperienceDto.roleId; 133 + if (updateUserJobExperienceDto.role !== undefined) { 134 + updateData.roleId = updateUserJobExperienceDto.role.id; 200 135 } 201 - if (updateUserJobExperienceDto.levelId !== undefined) { 202 - updateData.levelId = updateUserJobExperienceDto.levelId; 136 + if (updateUserJobExperienceDto.level !== undefined) { 137 + updateData.levelId = updateUserJobExperienceDto.level.id; 203 138 } 204 139 if (updateUserJobExperienceDto.startDate !== undefined) { 205 140 updateData.startDate = updateUserJobExperienceDto.startDate; ··· 210 145 if (updateUserJobExperienceDto.description !== undefined) { 211 146 updateData.description = updateUserJobExperienceDto.description; 212 147 } 213 - if (updateUserJobExperienceDto.skillIds !== undefined) { 148 + if (updateUserJobExperienceDto.skills !== undefined) { 214 149 updateData.skills = { 215 - set: updateUserJobExperienceDto.skillIds.map((id) => ({ id })), 150 + set: updateUserJobExperienceDto.skills.map((skill) => ({ id: skill.id })), 216 151 }; 217 152 } 218 153 ··· 220 155 where: { id }, 221 156 data: updateData, 222 157 include: { 223 - user: true, 224 158 company: true, 225 159 role: true, 226 160 level: true, 227 161 skills: true, 228 162 }, 229 163 }); 230 - return this.mapToDomain(prismaExperience); 164 + return this.userJobExperienceMapper.toDomain(prismaExperience); 231 165 } 232 166 233 167 async delete(id: string): Promise<void> { ··· 255 189 skills: true, 256 190 }, 257 191 }); 258 - return this.mapToDomain(prismaExperience); 192 + return this.userJobExperienceMapper.toDomain(prismaExperience); 259 193 } 260 194 261 195 async removeSkills( ··· 277 211 skills: true, 278 212 }, 279 213 }); 280 - return this.mapToDomain(prismaExperience); 214 + return this.userJobExperienceMapper.toDomain(prismaExperience); 281 215 } 282 216 }
+12 -19
apps/server/src/modules/job-experience/level/level.service.ts
··· 1 1 import { Injectable, NotFoundException } from "@nestjs/common"; 2 - import type { Level as PrismaLevel } from "@prisma/client"; 3 2 import { PrismaService } from "../../database/prisma.service"; 4 3 import type { CreateLevelDto, UpdateLevelDto } from "./level.dto"; 5 4 import { Level } from "./level.entity"; 5 + import { LevelMapper } from "./level.mapper"; 6 6 7 7 @Injectable() 8 8 export class LevelService { 9 - constructor(private prisma: PrismaService) {} 10 - 11 - private mapToDomain({ 12 - id, 13 - name, 14 - createdAt, 15 - updatedAt, 16 - description, 17 - }: PrismaLevel): Level { 18 - return new Level(id, name, createdAt, updatedAt, description ?? undefined); 19 - } 9 + constructor( 10 + private prisma: PrismaService, 11 + private levelMapper: LevelMapper, 12 + ) {} 20 13 21 14 async create(createLevelDto: CreateLevelDto): Promise<Level> { 22 15 const prismaLevel = await this.prisma.level.create({ 23 16 data: createLevelDto, 24 17 }); 25 - return this.mapToDomain(prismaLevel); 18 + return this.levelMapper.toDomain(prismaLevel); 26 19 } 27 20 28 21 async findAll(): Promise<Level[]> { 29 22 const prismaLevels = await this.prisma.level.findMany({ 30 23 orderBy: { name: "asc" }, 31 24 }); 32 - return prismaLevels.map((level) => this.mapToDomain(level)); 25 + return this.levelMapper.mapToDomain(prismaLevels); 33 26 } 34 27 35 28 async findById(id: string): Promise<Level | null> { 36 29 const prismaLevel = await this.prisma.level.findUnique({ 37 30 where: { id }, 38 31 }); 39 - return prismaLevel ? this.mapToDomain(prismaLevel) : null; 32 + return this.levelMapper.toDomain(prismaLevel); 40 33 } 41 34 42 35 async findByIdOrFail(id: string): Promise<Level> { ··· 48 41 throw new NotFoundException(`Level with id ${id} not found`); 49 42 } 50 43 51 - return this.mapToDomain(prismaLevel); 44 + return this.levelMapper.toDomain(prismaLevel); 52 45 } 53 46 54 47 async findByName(name: string): Promise<Level | null> { 55 48 const prismaLevel = await this.prisma.level.findUnique({ 56 49 where: { name }, 57 50 }); 58 - return prismaLevel ? this.mapToDomain(prismaLevel) : null; 51 + return this.levelMapper.toDomain(prismaLevel); 59 52 } 60 53 61 54 async findByNameOrFail(name: string): Promise<Level> { ··· 67 60 throw new NotFoundException(`Level with name ${name} not found`); 68 61 } 69 62 70 - return this.mapToDomain(prismaLevel); 63 + return this.levelMapper.toDomain(prismaLevel); 71 64 } 72 65 73 66 async update(id: string, updateLevelDto: UpdateLevelDto): Promise<Level> { ··· 75 68 where: { id }, 76 69 data: updateLevelDto, 77 70 }); 78 - return this.mapToDomain(prismaLevel); 71 + return this.levelMapper.toDomain(prismaLevel); 79 72 } 80 73 81 74 async delete(id: string): Promise<void> {
+12 -19
apps/server/src/modules/job-experience/role/role.service.ts
··· 1 1 import { Injectable, NotFoundException } from "@nestjs/common"; 2 - import type { Role as PrismaRole } from "@prisma/client"; 3 2 import { PrismaService } from "../../database/prisma.service"; 4 3 import type { CreateRoleDto, UpdateRoleDto } from "./role.dto"; 5 4 import { Role } from "./role.entity"; 5 + import { RoleMapper } from "./role.mapper"; 6 6 7 7 @Injectable() 8 8 export class RoleService { 9 - constructor(private prisma: PrismaService) {} 10 - 11 - private mapToDomain({ 12 - id, 13 - name, 14 - createdAt, 15 - updatedAt, 16 - description, 17 - }: PrismaRole): Role { 18 - return new Role(id, name, createdAt, updatedAt, description ?? undefined); 19 - } 9 + constructor( 10 + private prisma: PrismaService, 11 + private roleMapper: RoleMapper, 12 + ) {} 20 13 21 14 async create(createRoleDto: CreateRoleDto): Promise<Role> { 22 15 const prismaRole = await this.prisma.role.create({ 23 16 data: createRoleDto, 24 17 }); 25 - return this.mapToDomain(prismaRole); 18 + return this.roleMapper.toDomain(prismaRole); 26 19 } 27 20 28 21 async findAll(): Promise<Role[]> { 29 22 const prismaRoles = await this.prisma.role.findMany({ 30 23 orderBy: { name: "asc" }, 31 24 }); 32 - return prismaRoles.map((role) => this.mapToDomain(role)); 25 + return this.roleMapper.mapToDomain(prismaRoles); 33 26 } 34 27 35 28 async findById(id: string): Promise<Role | null> { 36 29 const prismaRole = await this.prisma.role.findUnique({ 37 30 where: { id }, 38 31 }); 39 - return prismaRole ? this.mapToDomain(prismaRole) : null; 32 + return this.roleMapper.toDomain(prismaRole); 40 33 } 41 34 42 35 async findByIdOrFail(id: string): Promise<Role> { ··· 48 41 throw new NotFoundException(`Role with id ${id} not found`); 49 42 } 50 43 51 - return this.mapToDomain(prismaRole); 44 + return this.roleMapper.toDomain(prismaRole); 52 45 } 53 46 54 47 async findByName(name: string): Promise<Role | null> { 55 48 const prismaRole = await this.prisma.role.findUnique({ 56 49 where: { name }, 57 50 }); 58 - return prismaRole ? this.mapToDomain(prismaRole) : null; 51 + return this.roleMapper.toDomain(prismaRole); 59 52 } 60 53 61 54 async findByNameOrFail(name: string): Promise<Role> { ··· 67 60 throw new NotFoundException(`Role with name ${name} not found`); 68 61 } 69 62 70 - return this.mapToDomain(prismaRole); 63 + return this.roleMapper.toDomain(prismaRole); 71 64 } 72 65 73 66 async update(id: string, updateRoleDto: UpdateRoleDto): Promise<Role> { ··· 75 68 where: { id }, 76 69 data: updateRoleDto, 77 70 }); 78 - return this.mapToDomain(prismaRole); 71 + return this.roleMapper.toDomain(prismaRole); 79 72 } 80 73 81 74 async delete(id: string): Promise<void> {
+12 -19
apps/server/src/modules/job-experience/skill/skill.service.ts
··· 1 1 import { Injectable, NotFoundException } from "@nestjs/common"; 2 - import type { Skill as PrismaSkill } from "@prisma/client"; 3 2 import { PrismaService } from "../../database/prisma.service"; 4 3 import type { CreateSkillDto, UpdateSkillDto } from "./skill.dto"; 5 4 import { Skill } from "./skill.entity"; 5 + import { SkillMapper } from "./skill.mapper"; 6 6 7 7 @Injectable() 8 8 export class SkillService { 9 - constructor(private prisma: PrismaService) {} 10 - 11 - private mapToDomain({ 12 - id, 13 - name, 14 - createdAt, 15 - updatedAt, 16 - description, 17 - }: PrismaSkill): Skill { 18 - return new Skill(id, name, createdAt, updatedAt, description ?? undefined); 19 - } 9 + constructor( 10 + private prisma: PrismaService, 11 + private skillMapper: SkillMapper, 12 + ) {} 20 13 21 14 async create(createSkillDto: CreateSkillDto): Promise<Skill> { 22 15 const prismaSkill = await this.prisma.skill.create({ 23 16 data: createSkillDto, 24 17 }); 25 - return this.mapToDomain(prismaSkill); 18 + return this.skillMapper.toDomain(prismaSkill); 26 19 } 27 20 28 21 async findAll(): Promise<Skill[]> { 29 22 const prismaSkills = await this.prisma.skill.findMany({ 30 23 orderBy: { name: "asc" }, 31 24 }); 32 - return prismaSkills.map((skill) => this.mapToDomain(skill)); 25 + return this.skillMapper.mapToDomain(prismaSkills); 33 26 } 34 27 35 28 async findById(id: string): Promise<Skill | null> { 36 29 const prismaSkill = await this.prisma.skill.findUnique({ 37 30 where: { id }, 38 31 }); 39 - return prismaSkill ? this.mapToDomain(prismaSkill) : null; 32 + return this.skillMapper.toDomain(prismaSkill); 40 33 } 41 34 42 35 async findByIdOrFail(id: string): Promise<Skill> { ··· 48 41 throw new NotFoundException(`Skill with id ${id} not found`); 49 42 } 50 43 51 - return this.mapToDomain(prismaSkill); 44 + return this.skillMapper.toDomain(prismaSkill); 52 45 } 53 46 54 47 async findByName(name: string): Promise<Skill | null> { 55 48 const prismaSkill = await this.prisma.skill.findUnique({ 56 49 where: { name }, 57 50 }); 58 - return prismaSkill ? this.mapToDomain(prismaSkill) : null; 51 + return this.skillMapper.toDomain(prismaSkill); 59 52 } 60 53 61 54 async findByNameOrFail(name: string): Promise<Skill> { ··· 67 60 throw new NotFoundException(`Skill with name ${name} not found`); 68 61 } 69 62 70 - return this.mapToDomain(prismaSkill); 63 + return this.skillMapper.toDomain(prismaSkill); 71 64 } 72 65 73 66 async update(id: string, updateSkillDto: UpdateSkillDto): Promise<Skill> { ··· 75 68 where: { id }, 76 69 data: updateSkillDto, 77 70 }); 78 - return this.mapToDomain(prismaSkill); 71 + return this.skillMapper.toDomain(prismaSkill); 79 72 } 80 73 81 74 async delete(id: string): Promise<void> {