From c7d358a352bde965b3dedf1f74d72d534d4da9f6 Mon Sep 17 00:00:00 2001 From: Thom Werring Date: Sun, 22 Oct 2023 08:17:11 +0200 Subject: [PATCH] Moved doc generation to separate functions in separate files --- src/common/openApi.ts | 147 +++++++++++++++++++++++++ src/experiences/experiences.service.ts | 2 +- src/experiences/experiences.ts | 15 +-- src/main.ts | 80 +------------- 4 files changed, 156 insertions(+), 88 deletions(-) create mode 100644 src/common/openApi.ts diff --git a/src/common/openApi.ts b/src/common/openApi.ts new file mode 100644 index 0000000..fa0d3b4 --- /dev/null +++ b/src/common/openApi.ts @@ -0,0 +1,147 @@ +import { RedocModule, RedocOptions } from "@juicyllama/nestjs-redoc"; +import { INestApplication } from "@nestjs/common"; +import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger"; +import { skills } from "@/skills/skills"; +import { experiences } from "@/experiences/experiences"; +import { ExperienceType } from "@/experiences/experiences.types"; + +const apiDocument = ( + options: { + title: string; + description: string; + version: `${number}.${number}.${number}${string}` | `${number}.${number}${string}` | `${number}${string}`; + contact: { + name: string; + email: string; + }; + }, + redocOptions: RedocOptions +) => { + redocOptions.title = options.title; + + return new DocumentBuilder() + .setTitle(options.title) + .setDescription(options.description) + .setVersion(options.version) + .setContact(options.contact.name, null, options.contact.email) + .addServer("http://localhost:3000/"); +}; + +const addIntro = (document: DocumentBuilder, redocOptions: RedocOptions): DocumentBuilder => { + const intro = [ + "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.", + "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.", + ]; + + const introTagGroup = redocOptions.tagGroups.find((tagGroup) => tagGroup.name === "Intro"); + if (!introTagGroup) { + redocOptions.tagGroups.push({ + name: "Intro", + tags: ["Intro"], + }); + } else { + introTagGroup.tags.push("Intro"); + } + + return document.addTag("Intro", intro.join("
")); +}; + +const addSkills = (document: DocumentBuilder, count: number = 5, redocOptions: RedocOptions): DocumentBuilder => { + const introTagGroup = redocOptions.tagGroups.find((tagGroup) => tagGroup.name === "Intro"); + if (!introTagGroup) { + redocOptions.tagGroups.push({ + name: "Intro", + tags: ["Skills"], + }); + } else { + introTagGroup.tags.push("Skills"); + } + + const intro = "A short excerpt of my skills are"; + const skillList = skills + .slice(0, count) + .reduce((list, skill) => list.concat(`
  • ${skill.name} ${skill.description}
  • `), ""); + + return document.addTag("Skills", `${intro}
    `); +}; + +const formatExperience = (experience: ExperienceType) => { + return `${experience.startDate.toLocaleDateString("en-GB")}`; +}; + +const addExperiences = (document: DocumentBuilder, count: number = 5, redocOptions: RedocOptions): DocumentBuilder => { + let experienceTagGroup = redocOptions.tagGroups.find((tagGroup) => tagGroup.name === "Experience"); + if (!experienceTagGroup) { + experienceTagGroup = { + name: "Experience", + tags: ["Experience"], + }; + redocOptions.tagGroups.push(experienceTagGroup); + } + + experiences.slice(0, count).forEach((experience) => { + experienceTagGroup.tags.push(experience.name); + document.addTag(experience.name, formatExperience(experience)); + }); + + return document; +}; + +const initDocs = async ({ + app, + config, + redocOptions, + swaggerUIPath, + redocPath, +}: { + app: INestApplication; + config: DocumentBuilder; + redocOptions: RedocOptions; + swaggerUIPath: string; + redocPath: string; +}): Promise => { + const document = SwaggerModule.createDocument(app, config.build(), { + operationIdFactory: (controllerKey: string, methodKey: string) => + `${methodKey[0].toUpperCase()}${methodKey.substring(1)}`, + }); + + SwaggerModule.setup(swaggerUIPath, app, document); + await RedocModule.setup(redocPath, app, document, redocOptions); +}; + +export default async (app: INestApplication) => { + const redocOptions: RedocOptions = { + logo: { + url: "https://picsum.photos/256/128", + altText: "Thom Werring", + }, + sortPropsAlphabetically: true, + hideDownloadButton: false, + tagGroups: [], + }; + + const config = apiDocument( + { + title: "Thom Werring - CV", + description: "Westerstraat 83
    1521ZB, Wormerveer
    +31 6 37 65 08 49", + version: "1.0.0a", + contact: { + name: "Thom Werring", + email: "cv@t-werring.nl", + }, + }, + redocOptions + ); + + addIntro(config, redocOptions); + addSkills(config, 5, redocOptions); + addExperiences(config, 3, redocOptions); + + await initDocs({ + app, + config, + redocOptions, + swaggerUIPath: "/api", + redocPath: "/docs", + }); +}; diff --git a/src/experiences/experiences.service.ts b/src/experiences/experiences.service.ts index b650982..d9add27 100644 --- a/src/experiences/experiences.service.ts +++ b/src/experiences/experiences.service.ts @@ -10,7 +10,7 @@ export class ExperiencesService { constructor(private readonly skillsService: SkillsService) { this.experiences = ExperienceDto.asDto(this.linkSkills(experiences)).sort((experienceA, experienceB) => - experienceA.startDate > experienceB.startDate ? 1 : -1 + experienceA.startDate < experienceB.startDate ? 1 : -1 ); } diff --git a/src/experiences/experiences.ts b/src/experiences/experiences.ts index 39b88fb..abda432 100644 --- a/src/experiences/experiences.ts +++ b/src/experiences/experiences.ts @@ -26,15 +26,12 @@ During my time city: "Amsterdam", url: "http://blackorange.nl/", jobTitle: "Junior Developer / System Administrator", - startDate: new Date(2018, 9, 1), + startDate: new Date(2014, 9, 1), endDate: new Date(2018, 8, 30), - description: "", + description: "Blorp", skills: [ { - category: "AWS|Containerization|DevOps", - }, - { - name: "NodeJs|Git|Server", + name: "PHP|Git|Server", }, ], }, @@ -42,9 +39,9 @@ During my time name: "Werring Webdevelopment", city: "Middenbeemster", jobTitle: "ZZP", - startDate: new Date(2018, 9, 1), + startDate: new Date(2012, 9, 1), endDate: new Date(2018, 8, 30), - description: "", + description: "Blorp2", skills: [ { name: "PHP|Webhosting", @@ -56,7 +53,7 @@ During my time city: "Utrecht", url: "http://ingeniumcabobianci.nl/", jobTitle: "Penningmeester", - startDate: new Date(2018, 9, 1), + startDate: new Date(2011, 9, 1), endDate: new Date(2018, 8, 30), description: "", skills: [ diff --git a/src/main.ts b/src/main.ts index 60d815f..62b41ca 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,87 +1,11 @@ import { NestFactory } from "@nestjs/core"; import { AppModule } from "@/app.module"; -import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger"; -import { RedocModule, RedocOptions } from "@juicyllama/nestjs-redoc"; +import openApi from "@/common/openApi"; async function bootstrap() { const app = await NestFactory.create(AppModule); - const config = new DocumentBuilder() - .setTitle("Thom Werring - CV") - .setDescription("Westerstraat 83
    1521ZB, Wormerveer
    +31 6 37 65 08 49") - // .addApiKey({ - // type: "apiKey", - // name: "Authorization", - // bearerFormat: "bearer", - // }, "Credentials") - .setVersion("1.0.0a") - .setContact("Thom Werring", null, "cv@t-werring.nl") - .addTag( - "Skills", - `

    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.
    - 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. -

    -` - ) - .addTag( - "Saysimple", - `

    Senior Developer - Haarlem
    October 2018 - PRESENT

    -

    -

    ` - ) - .addTag( - "Blackorange", - `

    Junior Developer - Amsterdam
    October 2014 - September 2018

    -

    -

    ` - ) - .addTag( - "Werring webdevelopment", - `

    ZZP - Middenbeemster
    Januari 2012 - December 2016

    -

    -

    ` - ) - .build(); - const document = SwaggerModule.createDocument(app, config, { - 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, - - tagGroups: [ - { - name: "Skills", - tags: ["Skills"], - }, - { - name: "Experience", - tags: ["Experience", "Saysimple", "Blackorange", "Werring webdevelopment"], - }, - ], - }; - - await RedocModule.setup("/docs", app, document, redocOptions); - SwaggerModule.setup("/api", app, document); + await openApi(app); await app.listen(3000); }