From 4263dc70dad70caa35ea5145daec31e552db84d0 Mon Sep 17 00:00:00 2001 From: Thom Werring Date: Sat, 21 Oct 2023 10:29:20 +0200 Subject: [PATCH] Added redoc and skills endpoints --- cv/src/app.controller.ts | 19 +++++++- cv/src/app.module.ts | 3 +- cv/src/main.ts | 24 ++++++++++ cv/src/skills/skills.service.spec.ts | 18 +++++++ cv/src/skills/skills.service.ts | 72 ++++++++++++++++++++++++++++ cv/src/skills/skills.types.ts | 35 ++++++++++++++ 6 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 cv/src/skills/skills.service.spec.ts create mode 100644 cv/src/skills/skills.service.ts create mode 100644 cv/src/skills/skills.types.ts diff --git a/cv/src/app.controller.ts b/cv/src/app.controller.ts index ab4b447..29fa669 100644 --- a/cv/src/app.controller.ts +++ b/cv/src/app.controller.ts @@ -1,13 +1,30 @@ import { Controller, Get } from '@nestjs/common'; import { AppService } from './app.service'; +import { ApiExtraModels, ApiOkResponse, ApiResponse, ApiTags, getSchemaPath } from "@nestjs/swagger"; +import { SkillsService } from "src/skills/skills.service"; +import { SkillDto } from "src/skills/skills.types"; @Controller() +@ApiExtraModels(SkillDto) export class AppController { - constructor(private readonly appService: AppService) {} + constructor(private readonly appService: AppService, private readonly skillsService: SkillsService) {} @Get() sayHello(): string { return this.appService.getHello(); } + @Get("skills") + @ApiTags("Skills") + @ApiOkResponse({ + description: "Returns a list of all skills", + schema: { + items: { + $ref: getSchemaPath(SkillDto) + } + } + }) + getSkills(): ReadonlyArray { + return this.skillsService.getSkills({ }) + } } diff --git a/cv/src/app.module.ts b/cv/src/app.module.ts index 8662803..31adbc9 100644 --- a/cv/src/app.module.ts +++ b/cv/src/app.module.ts @@ -1,10 +1,11 @@ import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; +import { SkillsService } from './skills/skills.service'; @Module({ imports: [], controllers: [AppController], - providers: [AppService], + providers: [AppService, SkillsService], }) export class AppModule {} diff --git a/cv/src/main.ts b/cv/src/main.ts index 17a7126..4bb7d54 100644 --- a/cv/src/main.ts +++ b/cv/src/main.ts @@ -1,6 +1,7 @@ import { NestFactory } from "@nestjs/core"; import { AppModule } from "./app.module"; import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger"; +import { RedocModule, RedocOptions } from "@juicyllama/nestjs-redoc"; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -19,6 +20,29 @@ async function bootstrap() { operationIdFactory: (controllerKey: string, methodKey: string) => (`${methodKey[0].toUpperCase()}${methodKey.substring(1)}`) }); + const redocOptions: RedocOptions = { + title: 'CV - Thom Werring', + logo: { + url: 'https://picsum.photos/256/128', + altText: 'Thom Werring' + }, + sortPropsAlphabetically: true, + hideDownloadButton: false, + hideHostname: true, + + tagGroups: [ + { + name: 'Skills', + tags: ['Skills', ], + }, + { + name: 'Experience', + tags: ['Saysimple', 'Blackorange', 'Werring webdevelopment'], + }, + ], + }; + + await RedocModule.setup('/docs', app, document, redocOptions); SwaggerModule.setup("/api", app, document); await app.listen(3000); diff --git a/cv/src/skills/skills.service.spec.ts b/cv/src/skills/skills.service.spec.ts new file mode 100644 index 0000000..2480bfe --- /dev/null +++ b/cv/src/skills/skills.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SkillsService } from './skills.service'; + +describe('SkillsService', () => { + let service: SkillsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [SkillsService], + }).compile(); + + service = module.get(SkillsService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/cv/src/skills/skills.service.ts b/cv/src/skills/skills.service.ts new file mode 100644 index 0000000..579c801 --- /dev/null +++ b/cv/src/skills/skills.service.ts @@ -0,0 +1,72 @@ +import { Injectable } from "@nestjs/common"; +import { SkillDto, SkillType } from "src/skills/skills.types"; + +@Injectable() +export class SkillsService { + private readonly skills: ReadonlyArray; + + constructor() { + this.skills = SkillDto.asDto([ + { + name: "Git", + description: "Working with git, maintaining repositories, managing pull/merge requests.", + category: "Version control systems", + }, + { + name: "NodeJs, TypeScript, and Javascript", + description: "Building efficient and scalable microservices.", + category: "Programming languages", + }, + { + name: "Docker", + description: "Containerizing and running microservices in a local development environment.", + category: "Containerization", + }, + { + name: "Kubernetes", + description: "Deploying microservices to AWS (EKS).", + category: "Containerization", + }, + { + name: "EKS", + description: "Managing Elastic Kubernetes Service on AWS.", + category: "AWS", + }, + { + name: "API Gateway", + description: "Building and deploying cloud based applications.", + category: "AWS", + }, + { + name: "Lambda", + description: "Building and deploying cloud based applications.", + category: "AWS", + }, + { + name: "CloudWatch", + description: "Monitoring cloud based applications.", + category: "AWS", + }, + { + name: "CI/CD pipelines", + description: "Automating testing, building and deployments.", + category: "DevOps", + }, + { + name: "PHP", + description: "Building Websites and applications in PHP Laravel.", + category: "Programming languages", + }, + ]) + .sort((skillA, skillB) => skillA.category > skillB.category ? 1 : -1); + } + + getSkills(filter?: Partial) { + const filtersValues = Object.entries(filter).map(([key, filterValue]) => ([key, new RegExp(filterValue, "i")])) as [keyof SkillType, RegExp][]; + if (!filter || filtersValues.length === 0) { + return this.skills; + } + + return this.skills.filter((skill) => filtersValues.some(([key, filterValue]) => filterValue.test(skill[key]))); + } +} diff --git a/cv/src/skills/skills.types.ts b/cv/src/skills/skills.types.ts new file mode 100644 index 0000000..4e86cee --- /dev/null +++ b/cv/src/skills/skills.types.ts @@ -0,0 +1,35 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export type SkillType = { + name: string; + category: string; + description: string; +} + +type Test = T extends SkillType ? SkillDto : SkillDto[]; +export class SkillDto implements SkillType { + @ApiProperty() + readonly name: string; + @ApiProperty() + readonly category: string; + @ApiProperty() + readonly description: string; + + constructor(skill: SkillType) { + Object.assign(this, { + name: skill.name, + category: skill.category, + descriptions: skill.description + }); + } + + public static asDto(skills: T): SkillDto + public static asDto(skills: T): SkillDto[] + public static asDto(skills: T): SkillDto | SkillDto[] { + if (!Array.isArray(skills)) { + return new SkillDto(skills); + } + + return skills.map((skill) => new SkillDto(skill)); + } +}