Compare commits
10 Commits
e83403d67d
...
3d74ab5cc1
| Author | SHA1 | Date |
|---|---|---|
|
|
3d74ab5cc1 | |
|
|
6d7b0220a1 | |
|
|
f37bcad333 | |
|
|
c151cdf691 | |
|
|
8cd335dac7 | |
|
|
e6ebb0e4c1 | |
|
|
ececd76e10 | |
|
|
2fda3e3410 | |
|
|
532909d0d4 | |
|
|
1c533394b1 |
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"deleteOutDir": true
|
||||
}
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"deleteOutDir": true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@
|
|||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json",
|
||||
"test:e2e-watch": "jest --watch --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@juicyllama/nestjs-redoc": "^2.3.11",
|
||||
|
|
|
|||
|
|
@ -1,43 +1,32 @@
|
|||
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 } from "@/experiences/experiences.types";
|
||||
import { EducationService } from "@/education/education.service";
|
||||
import { EducationDto } from "@/education/education.types";
|
||||
import { IntroService } from "@/intro/intro.service";
|
||||
import { IntroDto } from "@/intro/intro.types";
|
||||
|
||||
describe("AppController", () => {
|
||||
let appController: AppController;
|
||||
let appService: AppService;
|
||||
let skillsService: SkillsService;
|
||||
let experiencesService: ExperiencesService;
|
||||
let educationService: EducationService;
|
||||
let introService: IntroService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const app: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AppController],
|
||||
providers: [AppService, SkillsService, ExperiencesService, EducationService],
|
||||
providers: [SkillsService, ExperiencesService, EducationService, IntroService],
|
||||
}).compile();
|
||||
|
||||
appController = app.get<AppController>(AppController);
|
||||
appService = app.get<AppService>(AppService);
|
||||
skillsService = app.get<SkillsService>(SkillsService);
|
||||
experiencesService = app.get<ExperiencesService>(ExperiencesService);
|
||||
educationService = app.get<EducationService>(EducationService);
|
||||
});
|
||||
|
||||
describe("root", () => {
|
||||
it("Should call AppService", () => {
|
||||
jest.spyOn(appService, "sayHello").mockImplementation(() => "Hello World!");
|
||||
appController.sayHello();
|
||||
expect(appService.sayHello).toBeCalled();
|
||||
});
|
||||
|
||||
it('should return "Hello World!"', () => {
|
||||
expect(appController.sayHello()).toBe("Hello World!");
|
||||
});
|
||||
introService = app.get<IntroService>(IntroService);
|
||||
});
|
||||
|
||||
describe("getSkills", () => {
|
||||
|
|
@ -96,4 +85,20 @@ describe("AppController", () => {
|
|||
expect(educationService.getMany).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getIntros", () => {
|
||||
it("Should return an Array of IntroDtos", () => {
|
||||
const result: IntroDto[] = IntroDto.asDto([
|
||||
{
|
||||
name: "name",
|
||||
value: "Bob Ross",
|
||||
category: "Personal details",
|
||||
},
|
||||
]);
|
||||
|
||||
jest.spyOn(introService, "getMany").mockImplementation(() => result);
|
||||
expect(appController.getIntros()).toBe(result);
|
||||
expect(introService.getMany).toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,29 +1,24 @@
|
|||
import { Controller, Get } from "@nestjs/common";
|
||||
import { AppService } from "@/app.service";
|
||||
import { ApiExcludeEndpoint, ApiExtraModels, ApiOkResponse, ApiTags, getSchemaPath } from "@nestjs/swagger";
|
||||
import { ApiExtraModels, ApiOkResponse, ApiTags, getSchemaPath } from "@nestjs/swagger";
|
||||
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";
|
||||
import { IntroDto } from "@/intro/intro.types";
|
||||
import { IntroService } from "@/intro/intro.service";
|
||||
|
||||
@Controller()
|
||||
@ApiExtraModels(SkillDto, ExperienceDto, EducationDto)
|
||||
@ApiExtraModels(SkillDto, ExperienceDto, EducationDto, IntroDto)
|
||||
export class AppController {
|
||||
constructor(
|
||||
private readonly appService: AppService,
|
||||
private readonly skillsService: SkillsService,
|
||||
private readonly experiencesService: ExperiencesService,
|
||||
private readonly educationService: EducationService
|
||||
private readonly educationService: EducationService,
|
||||
private readonly introService: IntroService
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@ApiExcludeEndpoint()
|
||||
sayHello(): string {
|
||||
return this.appService.sayHello();
|
||||
}
|
||||
|
||||
@Get("skills")
|
||||
@ApiTags("Skills")
|
||||
@ApiOkResponse({
|
||||
|
|
@ -65,4 +60,18 @@ export class AppController {
|
|||
getEducation(): readonly EducationDto[] {
|
||||
return this.educationService.getMany();
|
||||
}
|
||||
|
||||
@Get("intro")
|
||||
@ApiTags("Intro")
|
||||
@ApiOkResponse({
|
||||
description: "Returns a list of introduction items",
|
||||
schema: {
|
||||
items: {
|
||||
$ref: getSchemaPath(IntroDto),
|
||||
},
|
||||
},
|
||||
})
|
||||
getIntros(): readonly IntroDto[] {
|
||||
return this.introService.getMany();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { Module } from "@nestjs/common";
|
||||
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";
|
||||
import { IntroService } from "@/intro/intro.service";
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [AppController],
|
||||
providers: [AppService, SkillsService, ExperiencesService, EducationService],
|
||||
providers: [SkillsService, ExperiencesService, EducationService, IntroService],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
sayHello(): string {
|
||||
return "Hello World!";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { EducationType } from "@/education/education.types";
|
||||
import { DocumentBuilder } from "@nestjs/swagger";
|
||||
import { RedocOptions } from "@juicyllama/nestjs-redoc";
|
||||
import { education } from "@/education/education";
|
||||
import educationDataset from "@/education/education.dataset";
|
||||
|
||||
const formatEducation = (education: EducationType) => {
|
||||
const period = `${formatDate(education.startDate)} - ${
|
||||
|
|
@ -50,10 +50,13 @@ export const addEducation = (
|
|||
redocOptions.tagGroups.push(educationTagGroup);
|
||||
}
|
||||
|
||||
education.slice(0, count).forEach((education) => {
|
||||
educationTagGroup.tags.push(education.institute);
|
||||
document.addTag(education.institute, formatEducation(education));
|
||||
});
|
||||
educationDataset
|
||||
.getData()
|
||||
.slice(0, count)
|
||||
.forEach((education) => {
|
||||
educationTagGroup.tags.push(education.institute);
|
||||
document.addTag(education.institute, formatEducation(education));
|
||||
});
|
||||
|
||||
educationTagGroup.tags.push("Education");
|
||||
document.addTag("Education", "For my complete education, take a look at the GetEducation API endpoint.");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ExperienceType } from "@/experiences/experiences.types";
|
||||
import { DocumentBuilder } from "@nestjs/swagger";
|
||||
import { RedocOptions } from "@juicyllama/nestjs-redoc";
|
||||
import { experiences } from "@/experiences/experiences";
|
||||
import experiencesDataset from "@/experiences/experiences.dataset";
|
||||
|
||||
const formatExperience = (experience: ExperienceType) => {
|
||||
const workPeriod = `${formatDate(experience.startDate)} - ${
|
||||
|
|
@ -50,10 +50,13 @@ export const addExperiences = (
|
|||
redocOptions.tagGroups.push(experienceTagGroup);
|
||||
}
|
||||
|
||||
experiences.slice(0, count).forEach((experience) => {
|
||||
experienceTagGroup.tags.push(experience.name);
|
||||
document.addTag(experience.name, formatExperience(experience));
|
||||
});
|
||||
experiencesDataset
|
||||
.getData()
|
||||
.slice(0, count)
|
||||
.forEach((experience) => {
|
||||
experienceTagGroup.tags.push(experience.name);
|
||||
document.addTag(experience.name, formatExperience(experience));
|
||||
});
|
||||
|
||||
experienceTagGroup.tags.push("Experience");
|
||||
document.addTag(
|
||||
|
|
|
|||
|
|
@ -90,6 +90,6 @@ export default async (app: INestApplication) => {
|
|||
config,
|
||||
redocOptions,
|
||||
swaggerUIPath: "/api",
|
||||
redocPath: "/docs",
|
||||
redocPath: "/",
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { DocumentBuilder } from "@nestjs/swagger";
|
||||
import { RedocOptions } from "@juicyllama/nestjs-redoc";
|
||||
import { skills } from "@/skills/skills";
|
||||
import skillsDataset from "@/skills/skills.dataset";
|
||||
|
||||
export const addSkills = (
|
||||
document: DocumentBuilder,
|
||||
|
|
@ -18,7 +18,8 @@ export const addSkills = (
|
|||
}
|
||||
|
||||
const intro = "A short excerpt of my skills are";
|
||||
const skillList = skills
|
||||
const skillList = skillsDataset
|
||||
.getData()
|
||||
.slice(0, count)
|
||||
.reduce((list, skill) => list.concat(`<li><strong>${skill.name}</strong> ${skill.description}</li>`), "");
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
import { EducationType } from "@/education/education.types";
|
||||
|
||||
export default {
|
||||
getData: (): 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[],
|
||||
};
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { EducationService } from "@/education/education.service";
|
||||
import { education } from "@/education/education";
|
||||
import educationDataset from "@/education/education.dataset";
|
||||
|
||||
describe("SkillsService", () => {
|
||||
describe("EducationService", () => {
|
||||
let service: EducationService;
|
||||
|
||||
beforeEach(async () => {
|
||||
|
|
@ -10,29 +10,28 @@ describe("SkillsService", () => {
|
|||
providers: [EducationService],
|
||||
}).compile();
|
||||
|
||||
education.splice(0);
|
||||
|
||||
education.push({
|
||||
institute: "Oxford University",
|
||||
city: "Oxford",
|
||||
url: "https://www.ox.ac.uk/",
|
||||
course: "English",
|
||||
level: "Masters",
|
||||
startDate: new Date(2023, 0),
|
||||
endDate: new Date(2023, 1),
|
||||
description: "Got my english degree",
|
||||
});
|
||||
|
||||
education.push({
|
||||
institute: "Massachusetts Institute of Technology",
|
||||
city: "Cambridge",
|
||||
url: "https://www.mit.edu/",
|
||||
course: "Neuroscience",
|
||||
level: "PHD",
|
||||
startDate: new Date(2023, 2),
|
||||
endDate: new Date(2023, 3),
|
||||
description: "Got my PHD in neuroscience",
|
||||
});
|
||||
jest.spyOn(educationDataset, "getData").mockImplementation(() => [
|
||||
{
|
||||
institute: "Oxford University",
|
||||
city: "Oxford",
|
||||
url: "https://www.ox.ac.uk/",
|
||||
course: "English",
|
||||
level: "Masters",
|
||||
startDate: new Date(2023, 0),
|
||||
endDate: new Date(2023, 1),
|
||||
description: "Got my english degree",
|
||||
},
|
||||
{
|
||||
institute: "Massachusetts Institute of Technology",
|
||||
city: "Cambridge",
|
||||
url: "https://www.mit.edu/",
|
||||
course: "Neuroscience",
|
||||
level: "PHD",
|
||||
startDate: new Date(2023, 2),
|
||||
endDate: new Date(2023, 3),
|
||||
description: "Got my PHD in neuroscience",
|
||||
},
|
||||
]);
|
||||
|
||||
service = module.get<EducationService>(EducationService);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { EducationDto, EducationType } from "@/education/education.types";
|
||||
import { education } from "@/education/education";
|
||||
import educationDataset from "@/education/education.dataset";
|
||||
|
||||
@Injectable()
|
||||
export class EducationService {
|
||||
private readonly education: readonly EducationDto[];
|
||||
|
||||
constructor() {
|
||||
this.education = EducationDto.asDto(education).sort((educationA, educationB) =>
|
||||
this.education = EducationDto.asDto(educationDataset.getData()).sort((educationA, educationB) =>
|
||||
educationA.startDate < educationB.startDate ? 1 : -1
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
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[];
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
import { ExperienceType } from "@/experiences/experiences.types";
|
||||
|
||||
export default {
|
||||
getData: () =>
|
||||
[
|
||||
{
|
||||
name: "Saysimple / Just Internet Group",
|
||||
city: "Haarlem",
|
||||
url: "https://saysimple.com/",
|
||||
jobTitle: "Senior Developer / DevOps",
|
||||
startDate: new Date(2018, 9, 1),
|
||||
endDate: null,
|
||||
description: `My journey at Just Internet Group began in 2018 with Cocoon, a SaaS digital asset management (DAM) system. Here I implemented ElasticSearch & Kibana to effectively monitor asset uploads and downloads.
|
||||
In 2019, I transitioned to Saysimple, where I had the opportunity to contribute to the development of our Customer Communication Platform from the ground up. I participated in creating a microservice architecture using Docker and NodeJS, enabling modularity, scalability, and efficient resource allocation. Additionally, I am responsible for establishing and maintaining our CI/CD pipelines, the EKS cluster, and HAProxy load balancers, contributing to the overall system operation and efficient development processes.
|
||||
Furthermore, I am a member of the team that handles outages and maintenance during off-hours.
|
||||
`,
|
||||
skills: [
|
||||
{
|
||||
category:
|
||||
"AWS|Containerization|DevOps|Business Intelligence|Team Management|Problem-solving|Collaboration",
|
||||
},
|
||||
{
|
||||
name: "NodeJs|Git|PHP",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Blackorange",
|
||||
city: "Amsterdam",
|
||||
url: "http://blackorange.nl/",
|
||||
jobTitle: "Junior Developer / System Administrator",
|
||||
startDate: new Date(2014, 9, 1),
|
||||
endDate: new Date(2018, 8, 30),
|
||||
description: `My internship at Blackorange transformed into a full-time role as a Junior Developer and System Administrator, where I actively contributed to the development and maintenance of various web projects.
|
||||
Notably, I lead to the migration from managed hosting to self-hosted infrastructure using Nginx and PHP-FPM, and I participated in the development of our in-house CMS system using PHP Laravel.`,
|
||||
skills: [
|
||||
{
|
||||
name: "PHP|Git|Server|Communication|Problem-solving",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Mediacollege Amsterdam",
|
||||
city: "Amsterdam",
|
||||
url: "https://ma-web.nl",
|
||||
jobTitle: "Internship / Teacher in training",
|
||||
startDate: new Date(2013, 7, 1),
|
||||
endDate: new Date(2014, 1, 31),
|
||||
description: `My internship as a teacher-in-training at Mediacollege Amsterdam during my Education minor was an enriching experience. I guided students through the intricacies of PHP and JavaScript programming, fostering their comprehension of essential concepts. Alongside teaching, I provided individualized support, assisting students with coursework challenges and promoting their academic success. This internship honed my communication, problem-solving, and event organization skills, shaping my approach to effective and engaging teaching.`,
|
||||
skills: [
|
||||
{
|
||||
name: "Communication|Problem-solving|Event Organization",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Werring Webdevelopment",
|
||||
city: "Middenbeemster",
|
||||
jobTitle: "ZZP",
|
||||
startDate: new Date(2012, 9, 1),
|
||||
endDate: new Date(2016, 11, 31),
|
||||
description: `During my college years, I started as a freelancer as Werring Webdevelopment. I catered to the web development needs of small businesses, primarily building WordPress websites and assisting with domain registration and email setup.`,
|
||||
skills: [
|
||||
{
|
||||
name: "PHP|Web",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Sv. Ingenium",
|
||||
city: "Utrecht",
|
||||
url: "http://ingeniumcabobianci.nl/",
|
||||
jobTitle: "Treasurer",
|
||||
startDate: new Date(2012, 8, 1),
|
||||
endDate: new Date(2014, 7, 31),
|
||||
description: `At Sv. Ingenium, a student society affiliated with Hogeschool Utrecht, I served as Treasurer during a period of significant transformation.
|
||||
In 2012, multiple smaller study societies merged at the request of the university to form Sv. Ingenium. During my tenure, I oversaw financial operations, ensuring the society's stability and growth. I also played a key role in organizing educational international trips to CERN (Switzerland) and Volvo (Sweden), among others.
|
||||
In late 2014, Sv. Ingenium merged with a larger society to become Ingenium Cabo Bianci.`,
|
||||
skills: [
|
||||
{
|
||||
name: "Finances|Communication",
|
||||
},
|
||||
{
|
||||
category: "Organization",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Chellomedia / Film1",
|
||||
city: "Amsterdam",
|
||||
url: "http://film1.nl/",
|
||||
jobTitle: "Internship",
|
||||
startDate: new Date(2008, 2, 1),
|
||||
endDate: new Date(2008, 5, 30),
|
||||
description: `My internship at Chellomedia/Film1 immersed me in the world of digital media and caffeine. Apart from crafting animated banners and modifying movie promotional websites with ActionScript 3, I discovered a hidden talent for serving coffee that kept the team energized and creatively fueled. `,
|
||||
skills: [
|
||||
{
|
||||
name: "Teamwork|Communication",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Pressofoon B.V.",
|
||||
city: "Heemskerk",
|
||||
jobTitle: "Internship",
|
||||
startDate: new Date(2007, 8, 1),
|
||||
endDate: new Date(2009, 11, 31),
|
||||
description: `My internship at Pressofoon B.V. was an opportunity to learn and contribute alongside experienced professionals. Guided by mentors, I applied my developing programming skills, crafting JavaScript, HTML, and PHP scripts for multiple websites. I also assisted in redesigning the company website, learning from the design team's expertise. Alongside these technical contributions, I observed and participated in a new product launch, gaining valuable insights into product marketing and customer engagement. Throughout my internship, I strived to be a team player, collaborating effectively to achieve common goals.`,
|
||||
skills: [
|
||||
{
|
||||
name: "Teamwork|Communication",
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies ExperienceType[],
|
||||
};
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { ExperiencesService } from "./experiences.service";
|
||||
import { SkillsService } from "@/skills/skills.service";
|
||||
import { experiences } from "@/experiences/experiences";
|
||||
import { skills } from "@/skills/skills";
|
||||
import experiencesDataset from "@/experiences/experiences.dataset";
|
||||
import skillsDataset from "@/skills/skills.dataset";
|
||||
|
||||
describe("ExperiencesService", () => {
|
||||
let service: ExperiencesService;
|
||||
|
|
@ -12,53 +12,54 @@ describe("ExperiencesService", () => {
|
|||
providers: [ExperiencesService, SkillsService],
|
||||
}).compile();
|
||||
|
||||
experiences.splice(0);
|
||||
jest.spyOn(experiencesDataset, "getData").mockImplementation(() => [
|
||||
{
|
||||
name: "Employer A",
|
||||
city: "City A",
|
||||
url: "https://example.com/",
|
||||
jobTitle: "Tester",
|
||||
startDate: new Date(2023, 0),
|
||||
endDate: new Date(2023, 1),
|
||||
description: `Description of work`,
|
||||
skills: [
|
||||
{
|
||||
category: "Testing",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Employer B",
|
||||
city: "City B",
|
||||
url: "https://example.com/",
|
||||
jobTitle: "Developer",
|
||||
startDate: new Date(2023, 1),
|
||||
endDate: new Date(2023, 2),
|
||||
description: `Description of work`,
|
||||
skills: [
|
||||
{
|
||||
category: "Programming",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
experiences.push({
|
||||
name: "Employer A",
|
||||
city: "City A",
|
||||
url: "https://example.com/",
|
||||
jobTitle: "Tester",
|
||||
startDate: new Date(2023, 0),
|
||||
endDate: new Date(2023, 1),
|
||||
description: `Description of work`,
|
||||
skills: [
|
||||
{
|
||||
category: "Testing",
|
||||
},
|
||||
],
|
||||
});
|
||||
experiences.push({
|
||||
name: "Employer B",
|
||||
city: "City B",
|
||||
url: "https://example.com/",
|
||||
jobTitle: "Developer",
|
||||
startDate: new Date(2023, 1),
|
||||
endDate: new Date(2023, 2),
|
||||
description: `Description of work`,
|
||||
skills: [
|
||||
{
|
||||
category: "Programming",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
skills.splice(0);
|
||||
skills.push({
|
||||
name: "Writing tests",
|
||||
description: "Writing tests",
|
||||
category: "Testing",
|
||||
});
|
||||
skills.push({
|
||||
name: "TDD",
|
||||
description: "Test Driven Development",
|
||||
category: "Testing",
|
||||
});
|
||||
skills.push({
|
||||
name: "OOP",
|
||||
description: "Object Orientated Programming",
|
||||
category: "Programming style",
|
||||
});
|
||||
jest.spyOn(skillsDataset, "getData").mockImplementation(() => [
|
||||
{
|
||||
name: "Writing tests",
|
||||
description: "Writing tests",
|
||||
category: "Testing",
|
||||
},
|
||||
{
|
||||
name: "TDD",
|
||||
description: "Test Driven Development",
|
||||
category: "Testing",
|
||||
},
|
||||
{
|
||||
name: "OOP",
|
||||
description: "Object Orientated Programming",
|
||||
category: "Programming style",
|
||||
},
|
||||
]);
|
||||
|
||||
service = module.get<ExperiencesService>(ExperiencesService);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { ExperienceDto, ExperienceType } from "@/experiences/experiences.types";
|
||||
import { SkillsService } from "@/skills/skills.service";
|
||||
import { experiences } from "@/experiences/experiences";
|
||||
import experiencesDataset from "@/experiences/experiences.dataset";
|
||||
import { SkillDto } from "@/skills/skills.types";
|
||||
|
||||
@Injectable()
|
||||
|
|
@ -9,8 +9,8 @@ export class ExperiencesService {
|
|||
private readonly experiences: readonly ExperienceDto[];
|
||||
|
||||
constructor(private readonly skillsService: SkillsService) {
|
||||
this.experiences = ExperienceDto.asDto(this.linkSkills(experiences)).sort((experienceA, experienceB) =>
|
||||
experienceA.startDate < experienceB.startDate ? 1 : -1
|
||||
this.experiences = ExperienceDto.asDto(this.linkSkills(experiencesDataset.getData())).sort(
|
||||
(experienceA, experienceB) => (experienceA.startDate < experienceB.startDate ? 1 : -1)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
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: `My journey at Just Internet Group began in 2018 with Cocoon, a SaaS digital asset management (DAM) system. Here I implemented ElasticSearch & Kibana to effectively monitor asset uploads and downloads.
|
||||
In 2019, I transitioned to Saysimple, where I had the opportunity to contribute to the development of our Customer Communication Platform from the ground up. I participated in creating a microservice architecture using Docker and NodeJS, enabling modularity, scalability, and efficient resource allocation. Additionally, I am responsible for establishing and maintaining our CI/CD pipelines, the EKS cluster, and HAProxy load balancers, contributing to the overall system operation and efficient development processes.
|
||||
Furthermore, I am a member of the team that handles outages and maintenance during off-hours.
|
||||
`,
|
||||
skills: [
|
||||
{
|
||||
category:
|
||||
"AWS|Containerization|DevOps|Business Intelligence|Team Management|Problem-solving|Collaboration",
|
||||
},
|
||||
{
|
||||
name: "NodeJs|Git|PHP",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Blackorange",
|
||||
city: "Amsterdam",
|
||||
url: "http://blackorange.nl/",
|
||||
jobTitle: "Junior Developer / System Administrator",
|
||||
startDate: new Date(2014, 9, 1),
|
||||
endDate: new Date(2018, 8, 30),
|
||||
description: `My internship at Blackorange transformed into a full-time role as a Junior Developer and System Administrator, where I actively contributed to the development and maintenance of various web projects.
|
||||
Notably, I lead to the migration from managed hosting to self-hosted infrastructure using Nginx and PHP-FPM, and I participated in the development of our in-house CMS system using PHP Laravel.`,
|
||||
skills: [
|
||||
{
|
||||
name: "PHP|Git|Server|Communication|Problem-solving",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Werring Webdevelopment",
|
||||
city: "Middenbeemster",
|
||||
jobTitle: "ZZP",
|
||||
startDate: new Date(2012, 9, 1),
|
||||
endDate: new Date(2016, 11, 31),
|
||||
description: `During my college years, I started as a freelancer as Werring Webdevelopment. I catered to the web development needs of small businesses, primarily building WordPress websites and assisting with domain registration and email setup.`,
|
||||
skills: [
|
||||
{
|
||||
name: "PHP|Web",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Sv. Ingenium",
|
||||
city: "Utrecht",
|
||||
url: "http://ingeniumcabobianci.nl/",
|
||||
jobTitle: "Treasurer",
|
||||
startDate: new Date(2012, 8, 1),
|
||||
endDate: new Date(2014, 7, 31),
|
||||
description: `At Sv. Ingenium, a student society affiliated with Hogeschool Utrecht, I served as Treasurer during a period of significant transformation.
|
||||
In 2012, multiple smaller study societies merged at the request of the university to form Sv. Ingenium. During my tenure, I oversaw financial operations, ensuring the society's stability and growth. I also played a key role in organizing educational international trips to CERN (Switzerland) and Volvo (Sweden), among others.
|
||||
In late 2014, Sv. Ingenium merged with a larger society to become Ingenium Cabo Bianci.`,
|
||||
skills: [
|
||||
{
|
||||
name: "Finances|Communication",
|
||||
},
|
||||
{
|
||||
category: "Organization",
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies ExperienceType[];
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
import { IntroType } from "@/intro/intro.types";
|
||||
|
||||
export default {
|
||||
getData: (): IntroType[] =>
|
||||
[
|
||||
{
|
||||
category: "Personal details",
|
||||
name: "Name",
|
||||
value: "Thom Werring",
|
||||
},
|
||||
{
|
||||
category: "Personal details",
|
||||
name: "Gender",
|
||||
value: "Male",
|
||||
},
|
||||
{
|
||||
category: "Personal details",
|
||||
name: "Address",
|
||||
value: "Westerstraat 83\n1521ZB, Wormerveer",
|
||||
},
|
||||
{
|
||||
category: "Personal details",
|
||||
name: "Phone number",
|
||||
value: "+31 6 37 65 08 49",
|
||||
},
|
||||
{
|
||||
category: "Personal details",
|
||||
name: "Date of birth",
|
||||
value: "1990-06-05",
|
||||
},
|
||||
{
|
||||
category: "Introduction",
|
||||
name: "Professional introduction",
|
||||
value: `I'm a software lead with 5+ years of experience in cloud-native development and Agile/Scrum methodologies. I'm passionate about building and leading high-performing teams to deliver scalable and reliable cloud-based applications. I have a proven track record of success in using Node.js, TypeScript, JavaScript, Docker, Kubernetes, AWS, DevOps, Git, microservices, and HAProxy to create and deploy innovative solutions.
|
||||
Furthermore I'm also a strong communicator and collaborator, and I'm always looking for ways to improve my skills and knowledge. I'm excited about the future of cloud computing, and I'm eager to use my skills and experience to help others achieve their goals.`,
|
||||
},
|
||||
{
|
||||
category: "Introduction",
|
||||
name: "Personal introduction",
|
||||
value: `My name is Thom Werring, a 33-year-old software developer with a passion for technology and problem-solving. I currently work at Saysimple as a senior developer.
|
||||
Beyond the world of code, I'm a dedicated Judo practitioner, proudly holding a second-degree black belt. The discipline, strategic thinking, and respect instilled through Judo have profoundly enriched my life. And when I'm not grappling with code or opponents, I love the camaraderie and mental challenges of board games and tabletop games. They're the perfect way to unwind, get creative, and connect with friends.`,
|
||||
},
|
||||
{
|
||||
category: "Hobbies",
|
||||
name: "Judo",
|
||||
value: "I'm a dedicated Judo practitioner, proudly holding a second-degree black belt. My commitment to Judo extends to teaching kids and young adults with mental or physical limitations. Their enthusiasm and determination as they embrace Judo is truly heartwarming.",
|
||||
},
|
||||
{
|
||||
category: "Hobbies",
|
||||
name: "Board and tabletop games",
|
||||
value: "Board games and tabletop games are my favorite way to unwind and connect with friends. The mix of strategy, creativity, and camaraderie makes for unforgettable moments and lasting memories.",
|
||||
},
|
||||
] satisfies IntroType[],
|
||||
};
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { IntroService } from "@/intro/intro.service";
|
||||
import introDataset from "@/intro/intro.dataset";
|
||||
|
||||
describe("IntroService", () => {
|
||||
let service: IntroService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [IntroService],
|
||||
}).compile();
|
||||
|
||||
jest.spyOn(introDataset, "getData").mockImplementation(() => [
|
||||
{
|
||||
category: "Personal details",
|
||||
name: "Name",
|
||||
value: "Bob ross",
|
||||
},
|
||||
{
|
||||
category: "Personal details",
|
||||
name: "Gender",
|
||||
value: "Male",
|
||||
},
|
||||
{
|
||||
category: "Personal details",
|
||||
name: "Address",
|
||||
value: "",
|
||||
},
|
||||
{
|
||||
category: "Personal details",
|
||||
name: "Phone number",
|
||||
value: "+31 6 00 00 00 00",
|
||||
},
|
||||
{
|
||||
category: "Personal details",
|
||||
name: "Date of birth",
|
||||
value: "1942-10-29",
|
||||
},
|
||||
{
|
||||
category: "Hobbies",
|
||||
name: "Painting",
|
||||
value: "",
|
||||
},
|
||||
]);
|
||||
|
||||
service = module.get<IntroService>(IntroService);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
|
||||
describe("getMany method", () => {
|
||||
it("should be defined", () => {
|
||||
expect(service.getMany).toBeDefined();
|
||||
});
|
||||
|
||||
it("should return an array with 6 entries", () => {
|
||||
const intro = service.getMany();
|
||||
|
||||
expect(Array.isArray(intro)).toBe(true);
|
||||
expect(intro.length).toBe(6);
|
||||
});
|
||||
|
||||
it("should return a filtered array with 5 entries", () => {
|
||||
const intro = service.getMany({
|
||||
category: "Personal details",
|
||||
});
|
||||
|
||||
expect(Array.isArray(intro)).toBe(true);
|
||||
expect(intro.length).toBe(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { IntroDto, IntroType } from "@/intro/intro.types";
|
||||
import introDataset from "@/intro/intro.dataset";
|
||||
|
||||
@Injectable()
|
||||
export class IntroService {
|
||||
private readonly intro: readonly IntroDto[];
|
||||
|
||||
constructor() {
|
||||
this.intro = IntroDto.asDto(introDataset.getData()).sort((introA, introB) =>
|
||||
introA.category < introB.category ? 1 : -1
|
||||
);
|
||||
}
|
||||
|
||||
getMany(filter?: Partial<Omit<IntroType, "startDate" | "endDate">>) {
|
||||
const filtersValues = Object.entries(filter ?? {}).map(([key, filterValue]) => [
|
||||
key,
|
||||
new RegExp(filterValue, "i"),
|
||||
]) as Array<[keyof Omit<IntroType, "startDate" | "endDate">, RegExp]>;
|
||||
if (!filter || filtersValues.length === 0) {
|
||||
return this.intro;
|
||||
}
|
||||
|
||||
return this.intro.filter((intro) => filtersValues.some(([key, filterValue]) => filterValue.test(intro[key])));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { DtoClass } from "@/common/dtoClass.factory";
|
||||
|
||||
export type IntroType = {
|
||||
name: string;
|
||||
value: string;
|
||||
category: string;
|
||||
};
|
||||
|
||||
export class IntroDto extends DtoClass<IntroType, IntroDto>() implements IntroType {
|
||||
@ApiProperty()
|
||||
readonly name: string;
|
||||
@ApiProperty()
|
||||
readonly value: string;
|
||||
@ApiProperty()
|
||||
readonly category: string;
|
||||
|
||||
constructor(intro: IntroType) {
|
||||
super(intro);
|
||||
Object.assign(this, {
|
||||
name: intro.name,
|
||||
value: intro.value,
|
||||
category: intro.category,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
import { SkillType } from "@/skills/skills.types";
|
||||
|
||||
export default {
|
||||
getData: () =>
|
||||
[
|
||||
{
|
||||
name: "Git",
|
||||
description:
|
||||
"Efficiently manage Git repositories, handling pull/merge requests, and ensuring version control integrity.",
|
||||
category: "Version control systems",
|
||||
stars: 4,
|
||||
},
|
||||
{
|
||||
name: "NodeJs, TypeScript, and Javascript",
|
||||
description: "Develop and maintain scalable microservices using NodeJs, TypeScript, and Javascript.",
|
||||
category: "Programming languages",
|
||||
stars: 5,
|
||||
},
|
||||
{
|
||||
name: "Docker",
|
||||
description:
|
||||
"Containerize and orchestrate microservices in local development environments using Docker.",
|
||||
category: "Containerization",
|
||||
stars: 5,
|
||||
},
|
||||
{
|
||||
name: "Kubernetes",
|
||||
description: "Deploy and manage containerized microservices on AWS (EKS) using Kubernetes.",
|
||||
category: "Containerization",
|
||||
stars: 4,
|
||||
},
|
||||
{
|
||||
name: "EKS",
|
||||
description: "Administer and optimize Elastic Kubernetes Service (EKS) on AWS.",
|
||||
category: "AWS",
|
||||
stars: 3,
|
||||
},
|
||||
{
|
||||
name: "API Gateway",
|
||||
description: "Design, implement, and deploy cloud-based applications using API Gateway on AWS.",
|
||||
category: "AWS",
|
||||
stars: 3,
|
||||
},
|
||||
{
|
||||
name: "Lambda",
|
||||
description: "Develop and deploy serverless cloud-based applications using Lambda on AWS.",
|
||||
category: "AWS",
|
||||
stars: 3,
|
||||
},
|
||||
{
|
||||
name: "CloudWatch",
|
||||
description: "Monitor and analyze cloud-based applications using CloudWatch on AWS.",
|
||||
category: "AWS",
|
||||
stars: 4,
|
||||
},
|
||||
{
|
||||
name: "ElasticSearch, Logstash and Kibana (ELK Stack)",
|
||||
description:
|
||||
"Extract valuable insights and generate comprehensive statistics from SaaS platform data using the ELK Stack.",
|
||||
category: "Business Intelligence",
|
||||
stars: 3,
|
||||
},
|
||||
{
|
||||
name: "CI/CD pipelines",
|
||||
description: "Automate testing, building, and deployment processes using CI/CD pipelines.",
|
||||
category: "DevOps",
|
||||
stars: 5,
|
||||
},
|
||||
{
|
||||
name: "Server Management",
|
||||
description: "Configure, maintain, and troubleshoot hosting servers to ensure optimal performance.",
|
||||
category: "DevOps",
|
||||
stars: 5,
|
||||
},
|
||||
{
|
||||
name: "Webhosting",
|
||||
description: "Provision and manage web hosting environments for websites and email services.",
|
||||
category: "DevOps",
|
||||
stars: 3,
|
||||
},
|
||||
{
|
||||
name: "Web development",
|
||||
description: "Design and develop custom WordPress websites using industry best practices.",
|
||||
category: "Web development",
|
||||
stars: 2,
|
||||
},
|
||||
{
|
||||
name: "PHP",
|
||||
description: "Build and maintain dynamic websites and applications using PHP Laravel framework.",
|
||||
category: "Programming languages",
|
||||
stars: 4,
|
||||
},
|
||||
{
|
||||
name: "One-on-ones",
|
||||
description:
|
||||
"Conduct regular one-on-one meetings with junior developers and interns to guide their professional growth.",
|
||||
category: "Team Management",
|
||||
stars: 4,
|
||||
},
|
||||
{
|
||||
name: "Scrum Master",
|
||||
description:
|
||||
"Facilitate Scrum meetings, manage sprint backlogs, refine user stories, and collaborate with the Product Owner.",
|
||||
category: "Collaboration",
|
||||
stars: 2,
|
||||
},
|
||||
{
|
||||
name: "Finances",
|
||||
description:
|
||||
"Oversee financial operations, manage budgets, and ensure financial compliance for a student organization.",
|
||||
category: "Finance",
|
||||
stars: 2,
|
||||
},
|
||||
{
|
||||
name: "Event Organization",
|
||||
description:
|
||||
"Plan, organize, and execute educational events for students, ensuring engaging and informative experiences.",
|
||||
category: "Organization",
|
||||
stars: 3,
|
||||
},
|
||||
{
|
||||
name: "Communication",
|
||||
description:
|
||||
"Effectively communicate ideas and information both verbally and in writing, adapting communication style to suit the audience and purpose.",
|
||||
category: "Collaboration",
|
||||
stars: 4,
|
||||
},
|
||||
{
|
||||
name: "Problem-solving",
|
||||
description:
|
||||
"Apply analytical and creative thinking to identify, analyze, and resolve complex technical challenges, considering multiple perspectives and potential solutions.",
|
||||
category: "Problem-solving",
|
||||
stars: 5,
|
||||
},
|
||||
{
|
||||
name: "Teamwork",
|
||||
description:
|
||||
"Collaborate effectively with team members in a Scrum environment, sharing knowledge, providing constructive feedback, and contributing to a positive and productive team dynamic.",
|
||||
category: "Collaboration",
|
||||
stars: 5,
|
||||
},
|
||||
] satisfies SkillType[],
|
||||
};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { SkillsService } from "./skills.service";
|
||||
import { skills } from "@/skills/skills";
|
||||
import skillsDataset from "@/skills/skills.dataset";
|
||||
|
||||
describe("SkillsService", () => {
|
||||
let service: SkillsService;
|
||||
|
|
@ -10,22 +10,27 @@ describe("SkillsService", () => {
|
|||
providers: [SkillsService],
|
||||
}).compile();
|
||||
|
||||
skills.splice(0);
|
||||
skills.push({
|
||||
name: "Writing tests",
|
||||
description: "Writing tests",
|
||||
category: "Testing",
|
||||
});
|
||||
skills.push({
|
||||
name: "TDD",
|
||||
description: "Test Driven Development",
|
||||
category: "Testing",
|
||||
});
|
||||
skills.push({
|
||||
name: "OOP",
|
||||
description: "Object Orientated Programming",
|
||||
category: "Programming style",
|
||||
});
|
||||
jest.spyOn(skillsDataset, "getData").mockImplementation(() => [
|
||||
{
|
||||
name: "Writing tests",
|
||||
description: "Writing tests",
|
||||
category: "Testing",
|
||||
stars: 5,
|
||||
},
|
||||
{
|
||||
name: "TDD",
|
||||
description: "Test Driven Development",
|
||||
category: "Testing",
|
||||
stars: 5,
|
||||
},
|
||||
{
|
||||
name: "OOP",
|
||||
description: "Object Orientated Programming",
|
||||
category: "Programming style",
|
||||
stars: 5,
|
||||
},
|
||||
]);
|
||||
|
||||
service = module.get<SkillsService>(SkillsService);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,28 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { SkillDto, SkillType } from "@/skills/skills.types";
|
||||
import { skills } from "@/skills/skills";
|
||||
import skillsDataset from "@/skills/skills.dataset";
|
||||
|
||||
@Injectable()
|
||||
export class SkillsService {
|
||||
private readonly skills: readonly SkillDto[];
|
||||
|
||||
constructor() {
|
||||
this.skills = SkillDto.asDto(skills).sort((skillA, skillB) => (skillA.category > skillB.category ? 1 : -1));
|
||||
this.skills = SkillDto.asDto(skillsDataset.getData()).sort((skillA, skillB) =>
|
||||
skillA.category > skillB.category ? 1 : -1
|
||||
);
|
||||
}
|
||||
|
||||
getMany(filter?: Partial<SkillType>) {
|
||||
const filtersValues = Object.entries(filter ?? {}).map(([key, filterValue]) => [
|
||||
key,
|
||||
new RegExp(filterValue, "i"),
|
||||
new RegExp(String(filterValue), "i"),
|
||||
]) as Array<[keyof SkillType, RegExp]>;
|
||||
if (!filter || filtersValues.length === 0) {
|
||||
return this.skills;
|
||||
}
|
||||
|
||||
return this.skills.filter((skill) => filtersValues.some(([key, filterValue]) => filterValue.test(skill[key])));
|
||||
return this.skills.filter((skill) =>
|
||||
filtersValues.some(([key, filterValue]) => filterValue.test(String(skill[key])))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,118 +0,0 @@
|
|||
import { SkillType } from "@/skills/skills.types";
|
||||
|
||||
export const skills: SkillType[] = [
|
||||
{
|
||||
name: "Git",
|
||||
description:
|
||||
"Efficiently manage Git repositories, handling pull/merge requests, and ensuring version control integrity.",
|
||||
category: "Version control systems",
|
||||
},
|
||||
{
|
||||
name: "NodeJs, TypeScript, and Javascript",
|
||||
description: "Develop and maintain scalable microservices using NodeJs, TypeScript, and Javascript.",
|
||||
category: "Programming languages",
|
||||
},
|
||||
{
|
||||
name: "Docker",
|
||||
description: "Containerize and orchestrate microservices in local development environments using Docker.",
|
||||
category: "Containerization",
|
||||
},
|
||||
{
|
||||
name: "Kubernetes",
|
||||
description: "Deploy and manage containerized microservices on AWS (EKS) using Kubernetes.",
|
||||
category: "Containerization",
|
||||
},
|
||||
{
|
||||
name: "EKS",
|
||||
description: "Administer and optimize Elastic Kubernetes Service (EKS) on AWS.",
|
||||
category: "AWS",
|
||||
},
|
||||
{
|
||||
name: "API Gateway",
|
||||
description: "Design, implement, and deploy cloud-based applications using API Gateway on AWS.",
|
||||
category: "AWS",
|
||||
},
|
||||
{
|
||||
name: "Lambda",
|
||||
description: "Develop and deploy serverless cloud-based applications using Lambda on AWS.",
|
||||
category: "AWS",
|
||||
},
|
||||
{
|
||||
name: "CloudWatch",
|
||||
description: "Monitor and analyze cloud-based applications using CloudWatch on AWS.",
|
||||
category: "AWS",
|
||||
},
|
||||
{
|
||||
name: "ElasticSearch, Logstash and Kibana (ELK Stack)",
|
||||
description:
|
||||
"Extract valuable insights and generate comprehensive statistics from SaaS platform data using the ELK Stack.",
|
||||
category: "Business Intelligence",
|
||||
},
|
||||
{
|
||||
name: "CI/CD pipelines",
|
||||
description: "Automate testing, building, and deployment processes using CI/CD pipelines.",
|
||||
category: "DevOps",
|
||||
},
|
||||
{
|
||||
name: "Server Management",
|
||||
description: "Configure, maintain, and troubleshoot hosting servers to ensure optimal performance.",
|
||||
category: "DevOps",
|
||||
},
|
||||
{
|
||||
name: "Webhosting",
|
||||
description: "Provision and manage web hosting environments for websites and email services.",
|
||||
category: "DevOps",
|
||||
},
|
||||
{
|
||||
name: "Web development",
|
||||
description: "Design and develop custom WordPress websites using industry best practices.",
|
||||
category: "Web development",
|
||||
},
|
||||
{
|
||||
name: "PHP",
|
||||
description: "Build and maintain dynamic websites and applications using PHP Laravel framework.",
|
||||
category: "Programming languages",
|
||||
},
|
||||
{
|
||||
name: "One-on-ones",
|
||||
description:
|
||||
"Conduct regular one-on-one meetings with junior developers and interns to guide their professional growth.",
|
||||
category: "Team Management",
|
||||
},
|
||||
{
|
||||
name: "Scrum Master",
|
||||
description:
|
||||
"Facilitate Scrum meetings, manage sprint backlogs, refine user stories, and collaborate with the Product Owner.",
|
||||
category: "Collaboration",
|
||||
},
|
||||
{
|
||||
name: "Finances",
|
||||
description:
|
||||
"Oversee financial operations, manage budgets, and ensure financial compliance for a student organization.",
|
||||
category: "Finance",
|
||||
},
|
||||
{
|
||||
name: "Event Organization",
|
||||
description:
|
||||
"Plan, organize, and execute educational events for students, ensuring engaging and informative experiences.",
|
||||
category: "Organization",
|
||||
},
|
||||
{
|
||||
name: "Communication",
|
||||
description:
|
||||
"Effectively communicate ideas and information both verbally and in writing, adapting communication style to suit the audience and purpose.",
|
||||
category: "Collaboration",
|
||||
},
|
||||
{
|
||||
name: "Problem-solving",
|
||||
description:
|
||||
"Apply analytical and creative thinking to identify, analyze, and resolve complex technical challenges, considering multiple perspectives and potential solutions.",
|
||||
category: "Problem-solving",
|
||||
},
|
||||
{
|
||||
name: "Teamwork",
|
||||
description:
|
||||
"Collaborate effectively with team members in a Scrum environment, sharing knowledge, providing constructive feedback, and contributing to a positive and productive team dynamic.",
|
||||
category: "Collaboration",
|
||||
},
|
||||
] satisfies SkillType[];
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { DtoClass } from "@/common/dtoClass.factory";
|
||||
|
||||
type StarRating = 0 | 1 | 2 | 3 | 4 | 5;
|
||||
export type SkillType = {
|
||||
name: string;
|
||||
category: string;
|
||||
description: string;
|
||||
stars: StarRating;
|
||||
};
|
||||
|
||||
export class SkillDto extends DtoClass<SkillType, SkillDto>() implements SkillType {
|
||||
|
|
@ -14,6 +16,8 @@ export class SkillDto extends DtoClass<SkillType, SkillDto>() implements SkillTy
|
|||
readonly category: string;
|
||||
@ApiProperty()
|
||||
readonly description: string;
|
||||
@ApiProperty()
|
||||
readonly stars: StarRating;
|
||||
|
||||
constructor(skill: SkillType) {
|
||||
super(skill);
|
||||
|
|
@ -21,6 +25,7 @@ export class SkillDto extends DtoClass<SkillType, SkillDto>() implements SkillTy
|
|||
name: skill.name,
|
||||
category: skill.category,
|
||||
descriptions: skill.description,
|
||||
stars: skill.stars,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { INestApplication } from "@nestjs/common";
|
||||
import * as request from "supertest";
|
||||
import { AppModule } from "./../src/app.module";
|
||||
import { AppModule } from "@/app.module";
|
||||
|
||||
describe("AppController (e2e)", () => {
|
||||
let app: INestApplication;
|
||||
|
|
@ -15,7 +15,16 @@ describe("AppController (e2e)", () => {
|
|||
await app.init();
|
||||
});
|
||||
|
||||
it("/ (GET)", () => {
|
||||
return request(app.getHttpServer()).get("/").expect(200).expect("Hello World!");
|
||||
it("/skills", () => {
|
||||
return request(app.getHttpServer()).get("/skills").expect(200);
|
||||
});
|
||||
it("/experiences", () => {
|
||||
return request(app.getHttpServer()).get("/experiences").expect(200);
|
||||
});
|
||||
it("/education", () => {
|
||||
return request(app.getHttpServer()).get("/education").expect(200);
|
||||
});
|
||||
it("/intro", () => {
|
||||
return request(app.getHttpServer()).get("/intro").expect(200);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"test",
|
||||
"dist",
|
||||
"**/*spec.ts"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,12 @@
|
|||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@": ["./src/main.ts"],
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
],
|
||||
"@": [
|
||||
"./src/main.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue