Add experience endpoint & data

This commit is contained in:
Thom Werring 2023-10-21 14:05:43 +02:00
parent d5132520cb
commit b3812c2b40
6 changed files with 191 additions and 6 deletions

View file

@ -2,23 +2,27 @@ import { Test, TestingModule } from "@nestjs/testing";
import { AppController } from "@/app.controller"; import { AppController } from "@/app.controller";
import { AppService } from "@/app.service"; import { AppService } from "@/app.service";
import { SkillsService } from "@/skills/skills.service"; import { SkillsService } from "@/skills/skills.service";
import { ExperiencesService } from "@/experiences/experiences.service";
import { SkillDto } from "@/skills/skills.types"; import { SkillDto } from "@/skills/skills.types";
import { ExperienceDto, ExperienceType } from "@/experiences/experiences.types";
describe("AppController", () => { describe("AppController", () => {
let appController: AppController; let appController: AppController;
let appService: AppService; let appService: AppService;
let skillsService: SkillsService; let skillsService: SkillsService;
let experiencesService: ExperiencesService;
beforeEach(async () => { beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({ const app: TestingModule = await Test.createTestingModule({
controllers: [AppController], controllers: [AppController],
providers: [AppService, SkillsService], providers: [AppService, SkillsService, ExperiencesService],
}).compile(); }).compile();
appController = app.get<AppController>(AppController); appController = app.get<AppController>(AppController);
appService = app.get<AppService>(AppService); appService = app.get<AppService>(AppService);
skillsService = app.get<SkillsService>(SkillsService); skillsService = app.get<SkillsService>(SkillsService);
experiencesService = app.get<ExperiencesService>(ExperiencesService);
}); });
describe("root", () => { describe("root", () => {
@ -46,5 +50,22 @@ describe("AppController", () => {
expect(skillsService.getMany).toBeCalled(); 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();
});
}); });
}); });

View file

@ -4,9 +4,10 @@ import { ApiExcludeEndpoint, ApiExtraModels, ApiOkResponse, ApiTags, getSchemaPa
import { SkillsService } from "@/skills/skills.service"; import { SkillsService } from "@/skills/skills.service";
import { ExperiencesService } from "@/experiences/experiences.service"; import { ExperiencesService } from "@/experiences/experiences.service";
import { SkillDto } from "@/skills/skills.types"; import { SkillDto } from "@/skills/skills.types";
import { ExperienceDto } from "@/experiences/experiences.types";
@Controller() @Controller()
@ApiExtraModels(SkillDto) @ApiExtraModels(SkillDto, ExperienceDto)
export class AppController { export class AppController {
constructor(private readonly appService: AppService, private readonly skillsService: SkillsService, private readonly experiencesService: ExperiencesService) { constructor(private readonly appService: AppService, private readonly skillsService: SkillsService, private readonly experiencesService: ExperiencesService) {
} }
@ -30,4 +31,18 @@ export class AppController {
getSkills(): ReadonlyArray<SkillDto> { getSkills(): ReadonlyArray<SkillDto> {
return this.skillsService.getMany(); return this.skillsService.getMany();
} }
@Get("experiences")
@ApiTags("Experience")
@ApiOkResponse({
description: "Returns a list of previous work experiences",
schema: {
items: {
$ref: getSchemaPath(ExperienceDto),
},
},
})
getExperiences(): ReadonlyArray<ExperienceDto> {
return this.experiencesService.getMany();
}
} }

View file

@ -1,12 +1,13 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { ExperiencesService } from './experiences.service'; import { ExperiencesService } from './experiences.service';
import { SkillsService } from "@/skills/skills.service";
describe('ExperiencesService', () => { describe('ExperiencesService', () => {
let service: ExperiencesService; let service: ExperiencesService;
beforeEach(async () => { beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
providers: [ExperiencesService], providers: [ExperiencesService, SkillsService],
}).compile(); }).compile();
service = module.get<ExperiencesService>(ExperiencesService); service = module.get<ExperiencesService>(ExperiencesService);

View file

@ -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() @Injectable()
export class ExperiencesService { export class ExperiencesService {
private readonly private readonly experiences: ReadonlyArray<ExperienceDto>;
constructor() {
constructor(
private readonly skillsService: SkillsService
) {
this.experiences = ExperienceDto.asDto(this.linkSkills(experiences))
.sort((experienceA, experienceB) => experienceA.startDate > experienceB.startDate ? 1 : -1);
} }
getMany(filter?: Partial<Omit<ExperienceType, "startDate" | "endDate" | "skills">>) {
const filtersValues = Object.entries(filter ?? {}).map(([key, filterValue]) => ([key, new RegExp(filterValue, "i")])) as [keyof Omit<ExperienceType, "startDate" | "endDate" | "skills">, 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<ExperienceType & { skills: ReadonlyArray<SkillDto> }> {
return experiences.map((experience) => {
return {
...experience,
skills: experience.skills.map(partialSkill => this.skillsService.getMany(partialSkill)).flat()
}
})
}
} }

View file

@ -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[];

View file

@ -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<SkillDto>[];
}
export class ExperienceDto extends DtoClass<ExperienceType, ExperienceDto>() 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,
});
}
}