Added education endpoints & information
This commit is contained in:
parent
c3dc1f9e48
commit
7aca1b737d
8 changed files with 235 additions and 3 deletions
|
|
@ -5,14 +5,17 @@ import { SkillsService } from "@/skills/skills.service";
|
|||
import { ExperiencesService } from "@/experiences/experiences.service";
|
||||
import { SkillDto } from "@/skills/skills.types";
|
||||
import { ExperienceDto } from "@/experiences/experiences.types";
|
||||
import { EducationDto } from "@/education/education.types";
|
||||
import { EducationService } from "@/education/education.service";
|
||||
|
||||
@Controller()
|
||||
@ApiExtraModels(SkillDto, ExperienceDto)
|
||||
@ApiExtraModels(SkillDto, ExperienceDto, EducationDto)
|
||||
export class AppController {
|
||||
constructor(
|
||||
private readonly appService: AppService,
|
||||
private readonly skillsService: SkillsService,
|
||||
private readonly experiencesService: ExperiencesService
|
||||
private readonly experiencesService: ExperiencesService,
|
||||
private readonly educationService: EducationService
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
|
|
@ -48,4 +51,18 @@ export class AppController {
|
|||
getExperiences(): readonly ExperienceDto[] {
|
||||
return this.experiencesService.getMany();
|
||||
}
|
||||
|
||||
@Get("education")
|
||||
@ApiTags("Education")
|
||||
@ApiOkResponse({
|
||||
description: "Returns a list of received education",
|
||||
schema: {
|
||||
items: {
|
||||
$ref: getSchemaPath(EducationDto),
|
||||
},
|
||||
},
|
||||
})
|
||||
getEducation(): readonly EducationDto[] {
|
||||
return this.educationService.getMany();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ import { AppController } from "@/app.controller";
|
|||
import { AppService } from "@/app.service";
|
||||
import { SkillsService } from "@/skills/skills.service";
|
||||
import { ExperiencesService } from "@/experiences/experiences.service";
|
||||
import { EducationService } from "@/education/education.service";
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [AppController],
|
||||
providers: [AppService, SkillsService, ExperiencesService],
|
||||
providers: [AppService, SkillsService, ExperiencesService, EducationService],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
|
|||
59
src/cvdocs/education.ts
Normal file
59
src/cvdocs/education.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { EducationType } from "@/education/education.types";
|
||||
import { DocumentBuilder } from "@nestjs/swagger";
|
||||
import { RedocOptions } from "@juicyllama/nestjs-redoc";
|
||||
import { education } from "@/education/education";
|
||||
|
||||
const formatEducation = (education: EducationType) => {
|
||||
const period = `${formatDate(education.startDate)} - ${
|
||||
education.endDate ? formatDate(education.endDate) : "present"
|
||||
}`;
|
||||
const institute = education.url
|
||||
? `<a target="_blank" href="${education.url}">${education.institute}</a>`
|
||||
: education.institute;
|
||||
const description = education.description.split("\n").join("</p><p>");
|
||||
|
||||
return `<header><strong>${institute}</strong>, <em>${education.city}</em><br/>
|
||||
<strong>${education.level}</strong> - ${education.course}<br/>
|
||||
<time>${period}</time></header>
|
||||
<p>${description}</p>`;
|
||||
};
|
||||
|
||||
const formatDate = (date: Date, locale: string | string[] = "en-GB"): string => {
|
||||
const dateFormatter = new Intl.DateTimeFormat(locale, {
|
||||
dateStyle: "long",
|
||||
});
|
||||
const dateParts = dateFormatter.formatToParts(date).reduce<Record<"day" | "month" | "year", string>>(
|
||||
(result, datePart: Intl.DateTimeFormatPart) => {
|
||||
if (datePart.type !== "literal") {
|
||||
result[datePart.type] = datePart.value;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
{ day: "", month: "", year: "" }
|
||||
);
|
||||
|
||||
return `${dateParts.month} ${dateParts.year}`;
|
||||
};
|
||||
|
||||
export const addEducation = (
|
||||
document: DocumentBuilder,
|
||||
count: number = 5,
|
||||
redocOptions: RedocOptions
|
||||
): DocumentBuilder => {
|
||||
let educationTagGroup = redocOptions.tagGroups.find((tagGroup) => tagGroup.name === "Education");
|
||||
if (!educationTagGroup) {
|
||||
educationTagGroup = {
|
||||
name: "Education",
|
||||
tags: ["Education"],
|
||||
};
|
||||
redocOptions.tagGroups.push(educationTagGroup);
|
||||
}
|
||||
|
||||
education.slice(0, count).forEach((education) => {
|
||||
educationTagGroup.tags.push(education.institute);
|
||||
document.addTag(education.institute, formatEducation(education));
|
||||
});
|
||||
|
||||
return document;
|
||||
};
|
||||
|
|
@ -4,6 +4,7 @@ import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
|
|||
import { addExperiences } from "@/cvdocs/experience";
|
||||
import { addSkills } from "@/cvdocs/skills";
|
||||
import { addIntro } from "@/cvdocs/intro";
|
||||
import { addEducation } from "@/cvdocs/education";
|
||||
|
||||
const apiDocument = (
|
||||
options: {
|
||||
|
|
@ -80,6 +81,7 @@ export default async (app: INestApplication) => {
|
|||
addIntro(config, redocOptions);
|
||||
addSkills(config, 5, redocOptions);
|
||||
addExperiences(config, 3, redocOptions);
|
||||
addEducation(config, 2, redocOptions);
|
||||
|
||||
await initDocs({
|
||||
app,
|
||||
|
|
|
|||
32
src/education/education.service.spec.ts
Normal file
32
src/education/education.service.spec.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { EducationService } from "src/education/education.service";
|
||||
|
||||
describe("SkillsService", () => {
|
||||
let service: EducationService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [EducationService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<EducationService>(EducationService);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
|
||||
describe("getMany method", () => {
|
||||
it("should be defined", () => {
|
||||
expect(service.getMany).toBeDefined();
|
||||
});
|
||||
|
||||
it("should return an array", () => {
|
||||
expect(Array.isArray(service.getMany())).toBe(true);
|
||||
});
|
||||
|
||||
it("should return an array", () => {
|
||||
expect(Array.isArray(service.getMany())).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
28
src/education/education.service.ts
Normal file
28
src/education/education.service.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { EducationDto, EducationType } from "@/education/education.types";
|
||||
import { education } from "@/education/education";
|
||||
|
||||
@Injectable()
|
||||
export class EducationService {
|
||||
private readonly education: readonly EducationDto[];
|
||||
|
||||
constructor() {
|
||||
this.education = EducationDto.asDto(education).sort((educationA, educationB) =>
|
||||
educationA.startDate < educationB.startDate ? 1 : -1
|
||||
);
|
||||
}
|
||||
|
||||
getMany(filter?: Partial<Omit<EducationType, "startDate" | "endDate">>) {
|
||||
const filtersValues = Object.entries(filter ?? {}).map(([key, filterValue]) => [
|
||||
key,
|
||||
new RegExp(filterValue, "i"),
|
||||
]) as Array<[keyof Omit<EducationType, "startDate" | "endDate">, RegExp]>;
|
||||
if (!filter || filtersValues.length === 0) {
|
||||
return this.education;
|
||||
}
|
||||
|
||||
return this.education.filter((education) =>
|
||||
filtersValues.some(([key, filterValue]) => filterValue.test(education[key]))
|
||||
);
|
||||
}
|
||||
}
|
||||
47
src/education/education.ts
Normal file
47
src/education/education.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { EducationType } from "@/education/education.types";
|
||||
|
||||
export const education: EducationType[] = [
|
||||
{
|
||||
institute: "Hogeschool Utrecht",
|
||||
city: "Utrecht",
|
||||
url: "https://hu.nl",
|
||||
course: "Mediatechnologie",
|
||||
level: "HBO",
|
||||
startDate: new Date(2010, 8),
|
||||
endDate: new Date(2015, 9),
|
||||
description: `My time at Hogeschool Utrecht was a period of academic and personal growth.
|
||||
While pursuing my Media Technology degree, I took a gap year to co-found and serve as treasurer of Sv. Ingenium, a university-affiliated student society. This experience sharpened my organizational, communication, and leadership skills, proving invaluable throughout my studies and beyond.
|
||||
Additionally, active participation in projects and presentations enhanced my presentation skills and public speaking confidence.`,
|
||||
},
|
||||
{
|
||||
institute: "Mediacollege Amsterdam",
|
||||
city: "Amsterdam",
|
||||
url: "https://www.ma-web.nl/",
|
||||
course: "Interactive Design & Media technology",
|
||||
level: "MBO 4",
|
||||
startDate: new Date(2006, 8),
|
||||
endDate: new Date(2010, 5),
|
||||
description: `My time at Mediacollege Amsterdam began with Interactive Design, but programming quickly captured my interest. ActionScript 2, now a relic of the past, marked my introduction to the world of programming, while PHP and JavaScript truly sparked my passion.
|
||||
With help of my teachers and a careful consideration of course, I transitioned towards Media Technology, paving the way for my future in software development.`,
|
||||
},
|
||||
{
|
||||
institute: "Clusius college",
|
||||
city: "Castricum",
|
||||
url: "https://www.vonknh.nl/vmbo/castricum",
|
||||
course: "General secondary education",
|
||||
level: "VMBO - GL",
|
||||
startDate: new Date(2002, 8),
|
||||
endDate: new Date(2006, 5),
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
institute: "Watermolen",
|
||||
city: "Koog aan de zaan",
|
||||
url: "https://www.obsdewatermolen.nl/",
|
||||
course: "General primary education",
|
||||
level: "Primary education",
|
||||
startDate: new Date(1994, 8),
|
||||
endDate: new Date(2002, 5),
|
||||
description: "",
|
||||
},
|
||||
] satisfies EducationType[];
|
||||
46
src/education/education.types.ts
Normal file
46
src/education/education.types.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { DtoClass } from "@/common/dtoClass.factory";
|
||||
|
||||
export type EducationType = {
|
||||
institute: string;
|
||||
city: string;
|
||||
url?: string;
|
||||
course: string;
|
||||
level: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export class EducationDto extends DtoClass<EducationType, EducationDto>() implements EducationType {
|
||||
@ApiProperty()
|
||||
readonly institute: string;
|
||||
@ApiProperty()
|
||||
readonly city: string;
|
||||
@ApiProperty()
|
||||
readonly url?: string;
|
||||
@ApiProperty()
|
||||
readonly course: string;
|
||||
@ApiProperty()
|
||||
readonly level: string;
|
||||
@ApiProperty()
|
||||
readonly startDate: Date;
|
||||
@ApiProperty()
|
||||
readonly endDate: Date;
|
||||
@ApiProperty()
|
||||
readonly description: string;
|
||||
|
||||
constructor(education: EducationType) {
|
||||
super(education);
|
||||
Object.assign(this, {
|
||||
institute: education.institute,
|
||||
city: education.city,
|
||||
url: education.url,
|
||||
course: education.course,
|
||||
level: education.level,
|
||||
startDate: education.startDate,
|
||||
endDate: education.endDate,
|
||||
description: education.description,
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue