diff --git a/cv/src/app.controller.spec.ts b/cv/src/app.controller.spec.ts index d0dd8fd..ab1e762 100644 --- a/cv/src/app.controller.spec.ts +++ b/cv/src/app.controller.spec.ts @@ -2,23 +2,27 @@ import { Test, TestingModule } from "@nestjs/testing"; import { AppController } from "@/app.controller"; import { AppService } from "@/app.service"; import { SkillsService } from "@/skills/skills.service"; +import { ExperiencesService } from "@/experiences/experiences.service"; import { SkillDto } from "@/skills/skills.types"; +import { ExperienceDto, ExperienceType } from "@/experiences/experiences.types"; describe("AppController", () => { let appController: AppController; let appService: AppService; let skillsService: SkillsService; + let experiencesService: ExperiencesService; beforeEach(async () => { const app: TestingModule = await Test.createTestingModule({ controllers: [AppController], - providers: [AppService, SkillsService], + providers: [AppService, SkillsService, ExperiencesService], }).compile(); appController = app.get(AppController); appService = app.get(AppService); skillsService = app.get(SkillsService); + experiencesService = app.get(ExperiencesService); }); describe("root", () => { @@ -46,5 +50,22 @@ describe("AppController", () => { expect(skillsService.getMany).toBeCalled(); }); }); + + describe("getExperience", () => { + it("Should return an Array of ExperienceDtos", () => { + const result: ExperienceDto[] = ExperienceDto.asDto([{ + name: "experienceName", + city: "experienceCity", + jobTitle: "experienceJobTitle", + startDate: new Date(), + endDate: new Date(), + description: "experienceDescription", + skills: [] + }]); + + jest.spyOn(experiencesService, "getMany").mockImplementation(() => result) + expect(appController.getExperiences()).toBe(result); + expect(experiencesService.getMany).toBeCalled(); + }); }); }); diff --git a/cv/src/app.controller.ts b/cv/src/app.controller.ts index cd657fb..0cfdba5 100644 --- a/cv/src/app.controller.ts +++ b/cv/src/app.controller.ts @@ -4,9 +4,10 @@ import { ApiExcludeEndpoint, ApiExtraModels, ApiOkResponse, ApiTags, getSchemaPa import { SkillsService } from "@/skills/skills.service"; import { ExperiencesService } from "@/experiences/experiences.service"; import { SkillDto } from "@/skills/skills.types"; +import { ExperienceDto } from "@/experiences/experiences.types"; @Controller() -@ApiExtraModels(SkillDto) +@ApiExtraModels(SkillDto, ExperienceDto) export class AppController { constructor(private readonly appService: AppService, private readonly skillsService: SkillsService, private readonly experiencesService: ExperiencesService) { } @@ -30,4 +31,18 @@ export class AppController { getSkills(): ReadonlyArray { return this.skillsService.getMany(); } + + @Get("experiences") + @ApiTags("Experience") + @ApiOkResponse({ + description: "Returns a list of previous work experiences", + schema: { + items: { + $ref: getSchemaPath(ExperienceDto), + }, + }, + }) + getExperiences(): ReadonlyArray { + return this.experiencesService.getMany(); + } } diff --git a/cv/src/experiences/experiences.service.spec.ts b/cv/src/experiences/experiences.service.spec.ts index e4a688c..3f8eeac 100644 --- a/cv/src/experiences/experiences.service.spec.ts +++ b/cv/src/experiences/experiences.service.spec.ts @@ -1,12 +1,13 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ExperiencesService } from './experiences.service'; +import { SkillsService } from "@/skills/skills.service"; describe('ExperiencesService', () => { let service: ExperiencesService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [ExperiencesService], + providers: [ExperiencesService, SkillsService], }).compile(); service = module.get(ExperiencesService); diff --git a/cv/src/experiences/experiences.service.ts b/cv/src/experiences/experiences.service.ts index 4144a40..55cbee5 100644 --- a/cv/src/experiences/experiences.service.ts +++ b/cv/src/experiences/experiences.service.ts @@ -1,10 +1,35 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable } from "@nestjs/common"; +import { ExperienceDto, ExperienceType } from "@/experiences/experiences.types"; +import { SkillsService } from "@/skills/skills.service"; +import { experiences } from "@/experiences/experiences"; +import { SkillDto } from "@/skills/skills.types"; @Injectable() export class ExperiencesService { - private readonly - constructor() { + private readonly experiences: ReadonlyArray; + constructor( + private readonly skillsService: SkillsService + ) { + this.experiences = ExperienceDto.asDto(this.linkSkills(experiences)) + .sort((experienceA, experienceB) => experienceA.startDate > experienceB.startDate ? 1 : -1); } + getMany(filter?: Partial>) { + const filtersValues = Object.entries(filter ?? {}).map(([key, filterValue]) => ([key, new RegExp(filterValue, "i")])) as [keyof Omit, RegExp][]; + if (!filter || filtersValues.length === 0) { + return this.experiences; + } + + return this.experiences.filter((experience) => filtersValues.every(([key, filterValue]) => filterValue.test(experience[key]))); + } + + private linkSkills(experiences: ExperienceType[]): Array }> { + return experiences.map((experience) => { + return { + ...experience, + skills: experience.skills.map(partialSkill => this.skillsService.getMany(partialSkill)).flat() + } + }) + } } diff --git a/cv/src/experiences/experiences.ts b/cv/src/experiences/experiences.ts new file mode 100644 index 0000000..d91629a --- /dev/null +++ b/cv/src/experiences/experiences.ts @@ -0,0 +1,71 @@ +import { ExperienceType } from "@/experiences/experiences.types"; + +export const experiences: ExperienceType[] = [ + { + name: "Saysimple / Just Internet Group", + city: "Haarlem", + url: "https://saysimple.com/", + jobTitle: "Senior Developer / DevOps", + startDate: new Date(2018, 9, 1), + endDate: null, + description: `I started at Just Internet Group in 2018 working on Cocoon, a SaaS digital Asset Management (DAM) system. Here I implemented ElasticSearch & Kibana to keep track of assets uploaded and downloaded. +Later on I switched to Saysimple, to start building our brand new Customer Communication Platform. For Saysimple I was responsible for creating and maintaining our CI/CD pipelines, the EKS cluster and our HAProxy loadbalancers. +During my time +`, + skills: [ + { + category: "AWS|Containerization|DevOps|Business Intelligence|Team Management" + }, + { + name: "NodeJs|Git" + }, + ], + }, + { + name: "Blackorange", + city: "Amsterdam", + url: "http://blackorange.nl/", + jobTitle: "Junior Developer / System Administrator", + startDate: new Date(2018, 9, 1), + endDate: new Date(2018, 8, 30), + description: "", + skills: [ + { + category: "AWS|Containerization|DevOps" + }, + { + name: "NodeJs|Git|Server" + }, + ], + }, + { + name: "Werring Webdevelopment", + city: "Middenbeemster", + jobTitle: "ZZP", + startDate: new Date(2018, 9, 1), + endDate: new Date(2018, 8, 30), + description: "", + skills: [ + { + name: "PHP|Webhosting" + }, + ], + }, + { + name: "Sv. Ingenium", + city: "Utrecht", + url: "http://ingeniumcabobianci.nl/", + jobTitle: "Penningmeester", + startDate: new Date(2018, 9, 1), + endDate: new Date(2018, 8, 30), + description: "", + skills: [ + { + name: "Finances" + }, + { + category: "Organization" + }, + ], + }, +] satisfies ExperienceType[]; diff --git a/cv/src/experiences/experiences.types.ts b/cv/src/experiences/experiences.types.ts new file mode 100644 index 0000000..5747d2f --- /dev/null +++ b/cv/src/experiences/experiences.types.ts @@ -0,0 +1,52 @@ +import { ApiProperty, getSchemaPath } from "@nestjs/swagger"; +import { DtoClass } from "@/common/types"; +import { SkillDto } from "@/skills/skills.types"; + +export type ExperienceType = { + name: string; + city: string; + url?: string; + jobTitle: string; + description: string; + startDate: Date; + endDate: Date | null; + skills: Partial[]; +} + +export class ExperienceDto extends DtoClass() implements ExperienceType { + @ApiProperty() + readonly name: string; + @ApiProperty() + readonly city: string; + @ApiProperty() + readonly url?: string + @ApiProperty() + readonly jobTitle: string; + @ApiProperty() + readonly description: string; + @ApiProperty() + readonly startDate: Date; + @ApiProperty() + readonly endDate: Date | null; + @ApiProperty({ + type: [SkillDto], + items: { + $ref: getSchemaPath(SkillDto) + } + }) + readonly skills: SkillDto[]; + + constructor(experience: ExperienceType) { + super(experience); + Object.assign(this, { + name: experience.name, + city: experience.city, + url: experience.url, + jobTitle: experience.jobTitle, + description: experience.description, + startDate: experience.startDate, + endDate: experience.endDate, + skills: experience.skills, + }); + } +}