From 39b0eaf159b54679a3b22b7d302308a5dadbe8a0 Mon Sep 17 00:00:00 2001 From: Ary Obenholzner Date: Mon, 9 Feb 2026 21:36:48 +0100 Subject: [PATCH 1/7] [typescript-nestjs-server] #22928 exclude inline union strings from generating imports --- .../TypeScriptNestjsServerCodegen.java | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java index 46c8b20a74b4..3a78191bf808 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java @@ -173,7 +173,7 @@ public void processOpts() { additionalProperties.put(NEST_VERSION, nestVersion); if (additionalProperties.containsKey(NPM_NAME)) { - if(!additionalProperties.containsKey(NPM_VERSION)) { + if (!additionalProperties.containsKey(NPM_VERSION)) { additionalProperties.put(NPM_VERSION, "0.0.0"); } @@ -274,7 +274,13 @@ private String applyLocalTypeMapping(String type) { } private boolean isLanguagePrimitive(String type) { - return languageSpecificPrimitives.contains(type); + return languageSpecificPrimitives.contains(type) || isInlineUnion(type); + } + + private boolean isInlineUnion(String type) { + return Arrays.stream(type.split("\\|")) + .map(String::trim) + .allMatch(value -> value.matches("([\"'].*[\"'])")); } private boolean isLanguageGenericType(String type) { @@ -343,8 +349,8 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap operations, L // Collect imports from parameters if (operation.allParams != null) { for (CodegenParameter param : operation.allParams) { - if(param.dataType != null) { - if(isLanguageGenericType(param.dataType)) { + if (param.dataType != null) { + if (isLanguageGenericType(param.dataType)) { // Extract generic type and add to imports if its not a primitive String genericType = extractGenericType(param.dataType); if (genericType != null && !isLanguagePrimitive(genericType) && !isRecordType(genericType)) { @@ -366,10 +372,10 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap operations, L if (isLanguageGenericType(operation.returnType)) { // Extract generic type and add to imports if it's not a primitive String genericType = extractGenericType(operation.returnType); - if (genericType != null && !isLanguagePrimitive(genericType) && !isRecordType(genericType)) { + if (needToImport(operation.returnType) && genericType != null && !isLanguagePrimitive(genericType) && !isRecordType(genericType)) { allImports.add(genericType); } - } else { + } else if (needToImport(operation.returnType)) { allImports.add(operation.returnType); } } @@ -397,10 +403,10 @@ private String extractGenericType(String type) { return null; } String genericType = type.substring(startAngleBracketIndex + 1, endAngleBracketIndex); - if(isLanguageGenericType(genericType)) { + if (isLanguageGenericType(genericType)) { return extractGenericType(type); } - if(genericType.contains("|")) { + if (genericType.contains("|")) { return null; } return genericType; @@ -429,7 +435,9 @@ private Set parseImports(CodegenModel cm) { for (String name : cm.imports) { if (name.indexOf(" | ") >= 0) { String[] parts = name.split(" \\| "); - Collections.addAll(newImports, parts); + if (needToImport(parts[0])) { + Collections.addAll(newImports, parts); + } } else { newImports.add(name); } From 1fae8c2df5c4b061863d6737ece3ce33ba25053d Mon Sep 17 00:00:00 2001 From: Ary Obenholzner Date: Mon, 9 Feb 2026 21:52:05 +0100 Subject: [PATCH 2/7] [typescript-nestjs-server] #22928 add optional type hints --- .../src/main/resources/typescript-nestjs-server/api.mustache | 4 ++-- .../resources/typescript-nestjs-server/controller.mustache | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/api.mustache b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/api.mustache index f67fafe6f81a..7bd90bca7c80 100644 --- a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/api.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/api.mustache @@ -7,7 +7,7 @@ import { {{#tsImports}}{{classname}}, {{/tsImports}} } from '../{{modelPackage}} {{#operation}} export type {{#lambda.pascalcase}}{{operationId}}{{/lambda.pascalcase}}RequestParams = { {{#allParams}} - {{paramName}}: {{{dataType}}} + {{paramName}}: {{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{^required}} | undefined{{/required}} {{/allParams}} } {{/operation}} @@ -23,7 +23,7 @@ export abstract class {{classname}} { {{/useSingleRequestParameter}} {{^useSingleRequestParameter}} - abstract {{operationId}}({{#allParams}}{{paramName}}: {{{dataType}}}, {{/allParams}} request: Request): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} | Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> | Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>; + abstract {{operationId}}({{#allParams}}{{paramName}}: {{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{^required}} | undefined{{/required}}, {{/allParams}} request: Request): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} | Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> | Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>; {{/useSingleRequestParameter}} {{/operation}} diff --git a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/controller.mustache b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/controller.mustache index c761a37d5d8f..34551bf15cb0 100644 --- a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/controller.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/controller.mustache @@ -10,7 +10,7 @@ export class {{classname}}Controller { {{#operations}} {{#operation}} @{{#vendorExtensions.x-http-method}}{{.}}{{/vendorExtensions.x-http-method}}{{^vendorExtensions.x-http-method}}{{httpMethod}}{{/vendorExtensions.x-http-method}}('{{path}}') - {{operationId}}({{#allParams}}{{#isPathParam}}@Param('{{paramName}}') {{/isPathParam}}{{#isQueryParam}}@Query('{{paramName}}') {{/isQueryParam}}{{#isBodyParam}}@Body() {{/isBodyParam}}{{paramName}}: {{{dataType}}}, {{/allParams}}@Req() request: Request): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} | Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> | Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> { + {{operationId}}({{#allParams}}{{#isPathParam}}@Param('{{paramName}}') {{/isPathParam}}{{#isQueryParam}}@Query('{{paramName}}') {{/isQueryParam}}{{#isBodyParam}}@Body() {{/isBodyParam}}{{paramName}}: {{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{^required}} | undefined{{/required}}, {{/allParams}}@Req() request: Request): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} | Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> | Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> { return this.{{classVarName}}.{{operationId}}({{#useSingleRequestParameter}}{ {{/useSingleRequestParameter}}{{#allParams}}{{paramName}}, {{/allParams}}{{#useSingleRequestParameter}}}, {{/useSingleRequestParameter}}request); } From 617a8f6d4ff8f9b5839d4d4265817ccfbeb0a990 Mon Sep 17 00:00:00 2001 From: Ary Obenholzner Date: Thu, 12 Feb 2026 23:16:39 +0100 Subject: [PATCH 3/7] [typescript-nestjs-server] #22928 add/improve support for various parameter types --- .../samples-typescript-nestjs-server.yaml | 1 + .../typescript-nestjs-server-parameters.yaml | 6 ++ docs/generators/typescript-nestjs-server.md | 4 +- .../TypeScriptNestjsServerCodegen.java | 8 +- .../typescript-nestjs-server/api.mustache | 2 + .../controller.mustache | 7 +- .../cookies-decorator.mustache | 6 ++ .../builds/default/.openapi-generator/FILES | 1 + .../builds/default/api/PetApi.ts | 6 +- .../default/controllers/PetApi.controller.ts | 9 +- .../controllers/StoreApi.controller.ts | 3 +- .../default/controllers/UserApi.controller.ts | 3 +- .../builds/default/cookies-decorator.ts | 6 ++ .../builds/parameters/.gitignore | 56 ++++++++++++ .../parameters/.openapi-generator-ignore | 23 +++++ .../parameters/.openapi-generator/FILES | 12 +++ .../parameters/.openapi-generator/VERSION | 1 + .../builds/parameters/README.md | 56 ++++++++++++ .../builds/parameters/api-implementations.ts | 9 ++ .../builds/parameters/api.module.ts | 37 ++++++++ .../builds/parameters/api/DefaultApi.ts | 27 ++++++ .../builds/parameters/api/index.ts | 1 + .../controllers/DefaultApi.controller.ts | 15 ++++ .../builds/parameters/controllers/index.ts | 1 + .../builds/parameters/cookie-decorator.ts | 6 ++ .../builds/parameters/cookies-decorator.ts | 6 ++ .../builds/parameters/index.ts | 2 + .../builds/parameters/models/index.ts | 1 + .../builds/parameters/tsconfig.json | 20 +++++ .../package-lock.json | 79 +++++++++++++++++ .../typescript-nestjs-server/package.json | 2 + .../src/app.module.ts | 19 ++-- .../src/handlers/{ => default}/PetService.ts | 6 +- .../handlers/{ => default}/StoreService.ts | 4 +- .../src/handlers/{ => default}/UserService.ts | 4 +- .../src/handlers/parameters/DefaultService.ts | 19 ++++ .../typescript-nestjs-server/src/main.ts | 3 + .../test/app.e2e-spec.ts | 88 +++++++++++++++++-- 38 files changed, 523 insertions(+), 36 deletions(-) create mode 100644 bin/configs/typescript-nestjs-server-parameters.yaml create mode 100644 modules/openapi-generator/src/main/resources/typescript-nestjs-server/cookies-decorator.mustache create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/default/cookies-decorator.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/.gitignore create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator-ignore create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator/FILES create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator/VERSION create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/README.md create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/api-implementations.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/api.module.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/api/DefaultApi.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/api/index.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/DefaultApi.controller.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/index.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/cookie-decorator.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/cookies-decorator.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/index.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/models/index.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/tsconfig.json rename samples/server/petstore/typescript-nestjs-server/src/handlers/{ => default}/PetService.ts (91%) rename samples/server/petstore/typescript-nestjs-server/src/handlers/{ => default}/StoreService.ts (88%) rename samples/server/petstore/typescript-nestjs-server/src/handlers/{ => default}/UserService.ts (92%) create mode 100644 samples/server/petstore/typescript-nestjs-server/src/handlers/parameters/DefaultService.ts diff --git a/.github/workflows/samples-typescript-nestjs-server.yaml b/.github/workflows/samples-typescript-nestjs-server.yaml index e51d9e5e28e0..0b3d0b3d0331 100644 --- a/.github/workflows/samples-typescript-nestjs-server.yaml +++ b/.github/workflows/samples-typescript-nestjs-server.yaml @@ -5,6 +5,7 @@ on: paths: - samples/server/petstore/typescript-nestjs-server/** - .github/workflows/samples-typescript-nestjs-server.yaml + - .github/workflows/samples-typescript-nestjs-server-parameters.yaml jobs: build: name: Test TypeScript NestJS Server diff --git a/bin/configs/typescript-nestjs-server-parameters.yaml b/bin/configs/typescript-nestjs-server-parameters.yaml new file mode 100644 index 000000000000..f4ca4a50e7ee --- /dev/null +++ b/bin/configs/typescript-nestjs-server-parameters.yaml @@ -0,0 +1,6 @@ +generatorName: typescript-nestjs-server +outputDir: samples/server/petstore/typescript-nestjs-server/builds/parameters +inputSpec: modules/openapi-generator/src/test/resources/3_0/parameter-test-spec.yaml +templateDir: modules/openapi-generator/src/main/resources/typescript-nestjs-server +additionalProperties: + "useSingleRequestParameter" : true \ No newline at end of file diff --git a/docs/generators/typescript-nestjs-server.md b/docs/generators/typescript-nestjs-server.md index 3f15cd815bfe..47869311674d 100644 --- a/docs/generators/typescript-nestjs-server.md +++ b/docs/generators/typescript-nestjs-server.md @@ -40,14 +40,14 @@ These options may be applied as additional-properties (cli) or configOptions (pl |nullSafeAdditionalProps|Set to make additional properties types declare that their indexer may return undefined| |false| |paramNaming|Naming convention for parameters: 'camelCase', 'PascalCase', 'snake_case' and 'original', which keeps the original name| |camelCase| |prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false| -|rxjsVersion|The version of RxJS compatible with Angular (see ngVersion option).| |null| +|rxjsVersion|The version of RxJS.| |null| |snapshot|When setting this property to true, the version will be suffixed with -SNAPSHOT.yyyyMMddHHmm| |false| |sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true| |sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true| |stringEnums|Generate string enums instead of objects for enum values.| |false| |supportsES6|Generate code that conforms to ES6.| |false| |taggedUnions|Use discriminators to create tagged unions instead of extending interfaces.| |false| -|tsVersion|The version of typescript compatible with Angular (see ngVersion option).| |null| +|tsVersion|The version of typescript.| |null| |useSingleRequestParameter|Setting this property to true will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter.| |false| ## IMPORT MAPPING diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java index 3a78191bf808..4e75e652cc28 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java @@ -120,8 +120,8 @@ public TypeScriptNestjsServerCodegen() { this.cliOptions.add(new CliOption(FILE_NAMING, "Naming convention for the output files: 'camelCase', 'kebab-case'.").defaultValue(this.fileNaming)); this.cliOptions.add(new CliOption(STRING_ENUMS, STRING_ENUMS_DESC).defaultValue(String.valueOf(this.stringEnums))); this.cliOptions.add(new CliOption(USE_SINGLE_REQUEST_PARAMETER, "Setting this property to true will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter.").defaultValue(Boolean.FALSE.toString())); - this.cliOptions.add(new CliOption(TS_VERSION, "The version of typescript compatible with Angular (see ngVersion option).")); - this.cliOptions.add(new CliOption(RXJS_VERSION, "The version of RxJS compatible with Angular (see ngVersion option).")); + this.cliOptions.add(new CliOption(TS_VERSION, "The version of typescript.")); + this.cliOptions.add(new CliOption(RXJS_VERSION, "The version of RxJS.")); } @Override @@ -156,6 +156,7 @@ public void processOpts() { supportingFiles.add(new SupportingFile("api-implementations.mustache", "", "api-implementations.ts")); supportingFiles.add(new SupportingFile("api.module.mustache", "", "api.module.ts")); supportingFiles.add(new SupportingFile("controllers.mustache", "controllers", "index.ts")); + supportingFiles.add(new SupportingFile("cookies-decorator.mustache", "cookies-decorator.ts")); supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore")); supportingFiles.add(new SupportingFile("README.md", "", "README.md")); supportingFiles.add(new SupportingFile("tsconfig.mustache", "", "tsconfig.json")); @@ -300,6 +301,9 @@ private boolean isRecordType(String type) { public void postProcessParameter(CodegenParameter parameter) { super.postProcessParameter(parameter); parameter.dataType = applyLocalTypeMapping(parameter.dataType); + if("undefined".equals(parameter.defaultValue)) { + parameter.defaultValue = null; + } } @Override diff --git a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/api.mustache b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/api.mustache index 7bd90bca7c80..ce63e0c6c4c3 100644 --- a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/api.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/api.mustache @@ -1,6 +1,8 @@ import { Injectable } from '@nestjs/common'; import { Observable } from 'rxjs'; +{{#tsImports.0}} import { {{#tsImports}}{{classname}}, {{/tsImports}} } from '../{{modelPackage}}'; +{{/tsImports.0}} {{#useSingleRequestParameter}} {{#operations}} diff --git a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/controller.mustache b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/controller.mustache index 34551bf15cb0..53c0a759c875 100644 --- a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/controller.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/controller.mustache @@ -1,7 +1,10 @@ -import { Body, Controller{{#httpMethods}}, {{.}}{{/httpMethods}}, Param, Query, Req } from '@nestjs/common'; +import { Body, Controller{{#httpMethods}}, {{.}}{{/httpMethods}}, Param, Query, Headers, Req } from '@nestjs/common'; import { Observable } from 'rxjs'; +import { Cookies } from '../cookies-decorator'; import { {{classname}} } from '../{{apiPackage}}'; +{{#tsImports.0}} import { {{#tsImports}}{{classname}}, {{/tsImports}} } from '../{{modelPackage}}'; +{{/tsImports.0}} @Controller() export class {{classname}}Controller { @@ -10,7 +13,7 @@ export class {{classname}}Controller { {{#operations}} {{#operation}} @{{#vendorExtensions.x-http-method}}{{.}}{{/vendorExtensions.x-http-method}}{{^vendorExtensions.x-http-method}}{{httpMethod}}{{/vendorExtensions.x-http-method}}('{{path}}') - {{operationId}}({{#allParams}}{{#isPathParam}}@Param('{{paramName}}') {{/isPathParam}}{{#isQueryParam}}@Query('{{paramName}}') {{/isQueryParam}}{{#isBodyParam}}@Body() {{/isBodyParam}}{{paramName}}: {{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{^required}} | undefined{{/required}}, {{/allParams}}@Req() request: Request): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} | Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> | Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> { + {{operationId}}({{#allParams}}{{#isPathParam}}@Param('{{baseName}}') {{/isPathParam}}{{#isQueryParam}}@Query('{{baseName}}') {{/isQueryParam}}{{#isHeaderParam}}@Headers('{{baseName}}') {{/isHeaderParam}}{{#isCookieParam}}@Cookies('{{baseName}}') {{/isCookieParam}}{{#isBodyParam}}@Body() {{/isBodyParam}}{{paramName}}: {{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{^required}} | undefined{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}, {{/allParams}}@Req() request: Request): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} | Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> | Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> { return this.{{classVarName}}.{{operationId}}({{#useSingleRequestParameter}}{ {{/useSingleRequestParameter}}{{#allParams}}{{paramName}}, {{/allParams}}{{#useSingleRequestParameter}}}, {{/useSingleRequestParameter}}request); } diff --git a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/cookies-decorator.mustache b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/cookies-decorator.mustache new file mode 100644 index 000000000000..e9fdcf7354f0 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/cookies-decorator.mustache @@ -0,0 +1,6 @@ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; + +export const Cookies = createParamDecorator((data: string, ctx: ExecutionContext) => { +const request = ctx.switchToHttp().getRequest(); +return data ? request.cookies?.[data] : request.cookies; +}); \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/.openapi-generator/FILES b/samples/server/petstore/typescript-nestjs-server/builds/default/.openapi-generator/FILES index cae2c93f2c35..4a89ba119c5c 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/default/.openapi-generator/FILES +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/.openapi-generator/FILES @@ -10,6 +10,7 @@ controllers/PetApi.controller.ts controllers/StoreApi.controller.ts controllers/UserApi.controller.ts controllers/index.ts +cookies-decorator.ts index.ts models/api-response.ts models/category.ts diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/api/PetApi.ts b/samples/server/petstore/typescript-nestjs-server/builds/default/api/PetApi.ts index b6a3a4f5da5c..5005435ad0de 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/default/api/PetApi.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/api/PetApi.ts @@ -9,7 +9,7 @@ export abstract class PetApi { abstract addPet(pet: Pet, request: Request): Pet | Promise | Observable; - abstract deletePet(petId: number, apiKey: string, request: Request): void | Promise | Observable; + abstract deletePet(petId: number, apiKey: string | undefined, request: Request): void | Promise | Observable; abstract findPetsByStatus(status: Array<'available' | 'pending' | 'sold'>, request: Request): Array | Promise> | Observable>; @@ -24,9 +24,9 @@ export abstract class PetApi { abstract updatePet(pet: Pet, request: Request): Pet | Promise | Observable; - abstract updatePetWithForm(petId: number, name: string, status: string, request: Request): void | Promise | Observable; + abstract updatePetWithForm(petId: number, name: string | undefined, status: string | undefined, request: Request): void | Promise | Observable; - abstract uploadFile(petId: number, additionalMetadata: string, file: Blob, request: Request): ApiResponse | Promise | Observable; + abstract uploadFile(petId: number, additionalMetadata: string | undefined, file: Blob | undefined, request: Request): ApiResponse | Promise | Observable; } \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/PetApi.controller.ts b/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/PetApi.controller.ts index 8e5f0ffd771d..694146d53fa2 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/PetApi.controller.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/PetApi.controller.ts @@ -1,5 +1,6 @@ -import { Body, Controller, Delete, Get, Post, Put, Param, Query, Req } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Post, Put, Param, Query, Headers, Req } from '@nestjs/common'; import { Observable } from 'rxjs'; +import { Cookies } from '../cookies-decorator'; import { PetApi } from '../api'; import { ApiResponse, Pet, } from '../models'; @@ -13,7 +14,7 @@ export class PetApiController { } @Delete('/pet/:petId') - deletePet(@Param('petId') petId: number, apiKey: string, @Req() request: Request): void | Promise | Observable { + deletePet(@Param('petId') petId: number, @Headers('api_key') apiKey: string | undefined, @Req() request: Request): void | Promise | Observable { return this.petApi.deletePet(petId, apiKey, request); } @@ -38,12 +39,12 @@ export class PetApiController { } @Post('/pet/:petId') - updatePetWithForm(@Param('petId') petId: number, name: string, status: string, @Req() request: Request): void | Promise | Observable { + updatePetWithForm(@Param('petId') petId: number, name: string | undefined, status: string | undefined, @Req() request: Request): void | Promise | Observable { return this.petApi.updatePetWithForm(petId, name, status, request); } @Post('/pet/:petId/uploadImage') - uploadFile(@Param('petId') petId: number, additionalMetadata: string, file: Blob, @Req() request: Request): ApiResponse | Promise | Observable { + uploadFile(@Param('petId') petId: number, additionalMetadata: string | undefined, file: Blob | undefined, @Req() request: Request): ApiResponse | Promise | Observable { return this.petApi.uploadFile(petId, additionalMetadata, file, request); } diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/StoreApi.controller.ts b/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/StoreApi.controller.ts index f32cd6358dfd..9fa672cf5f55 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/StoreApi.controller.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/StoreApi.controller.ts @@ -1,5 +1,6 @@ -import { Body, Controller, Delete, Get, Post, Param, Query, Req } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Post, Param, Query, Headers, Req } from '@nestjs/common'; import { Observable } from 'rxjs'; +import { Cookies } from '../cookies-decorator'; import { StoreApi } from '../api'; import { Order, } from '../models'; diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/UserApi.controller.ts b/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/UserApi.controller.ts index d8c9ed60f24c..f948661dc7e9 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/UserApi.controller.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/UserApi.controller.ts @@ -1,5 +1,6 @@ -import { Body, Controller, Delete, Get, Post, Put, Param, Query, Req } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Post, Put, Param, Query, Headers, Req } from '@nestjs/common'; import { Observable } from 'rxjs'; +import { Cookies } from '../cookies-decorator'; import { UserApi } from '../api'; import { User, } from '../models'; diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/cookies-decorator.ts b/samples/server/petstore/typescript-nestjs-server/builds/default/cookies-decorator.ts new file mode 100644 index 000000000000..e9fdcf7354f0 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/cookies-decorator.ts @@ -0,0 +1,6 @@ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; + +export const Cookies = createParamDecorator((data: string, ctx: ExecutionContext) => { +const request = ctx.switchToHttp().getRequest(); +return data ? request.cookies?.[data] : request.cookies; +}); \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/.gitignore b/samples/server/petstore/typescript-nestjs-server/builds/parameters/.gitignore new file mode 100644 index 000000000000..4b56acfbebf4 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/.gitignore @@ -0,0 +1,56 @@ +# compiled output +/dist +/node_modules +/build + +# Logs +logs +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# temp directory +.temp +.tmp + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator-ignore b/samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator/FILES b/samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator/FILES new file mode 100644 index 000000000000..40300313d172 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator/FILES @@ -0,0 +1,12 @@ +.gitignore +README.md +api-implementations.ts +api.module.ts +api/DefaultApi.ts +api/index.ts +controllers/DefaultApi.controller.ts +controllers/index.ts +cookies-decorator.ts +index.ts +models/index.ts +tsconfig.json diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator/VERSION b/samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator/VERSION new file mode 100644 index 000000000000..193a12d6e891 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.20.0-SNAPSHOT diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/README.md b/samples/server/petstore/typescript-nestjs-server/builds/parameters/README.md new file mode 100644 index 000000000000..6fe10cd8d8ee --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/README.md @@ -0,0 +1,56 @@ +# OpenApi Generator _typescript-nestjs-server_ + +Usage: The generated output is intended to be its own module, that can be imported into your NestJS App Module. You do not need to change generated files, just import the module and implement the API + +Example usage (with the openapi sample `petstore.yaml`): + +1. Invoke openapi-generator + ``` + openapi-generator-cli.jar generate -i petstore.yaml -g typescript-nestjs-server -o api-module/ + ``` +2. implement the contracts from `api-module/api` + `handlers/PetService.ts`: + ```typescript + import { Pet, ApiResponse } from "models"; + import { Observable } from "rxjs"; + import { PetApi } from "../api"; + import { Inject, Injectable } from "@nestjs/common"; + + @Injectable() + export class PetService implements PetApi { + addPet(pet: Pet, request: Request): Pet | Promise | Observable { + throw new Error("Method not implemented."); + } + + deletePet(petId: number, apiKey: string, request: Request): void | Promise | Observable { + throw new Error("Method not implemented."); + } + + ... + ``` +3. Import the generated `ApiModule` with `ApiModule.forRoot` and provide a instance of `ApiImplementations` with a reference to your implementation + `app.module.ts` + ```typescript + import { Module } from "@nestjs/common"; + import { ApiModule, ApiImplementations } from "api-module"; + import { PetService } from "./handlers/PetService"; + import { UserService } from "./handlers/UserService"; + import { StoreService } from "./handlers/StoreService"; + + const apiImplementations: ApiImplementations = { + petApi: PetService, + userApi: UserService, + storeApi: StoreService, + } + + @Module({ + imports: [ + ApiModule.forRoot(apiImplementations), + ], + controllers: [], + providers: [], + }) + export class AppModule {} + ``` + +You now can regenerate the API module as often as you want without overriding your implementation. \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/api-implementations.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/api-implementations.ts new file mode 100644 index 000000000000..b3e65c743c6b --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/api-implementations.ts @@ -0,0 +1,9 @@ +import { Type } from '@nestjs/common'; +import { DefaultApi } from './api'; + +/** + * Provide this type to {@link ApiModule} to provide your API implementations +**/ +export type ApiImplementations = { + defaultApi: Type +}; diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/api.module.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/api.module.ts new file mode 100644 index 000000000000..e36a211b6772 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/api.module.ts @@ -0,0 +1,37 @@ +import { DynamicModule, Module, Provider } from '@nestjs/common'; +import { ApiImplementations } from './api-implementations' +import { DefaultApi } from './api'; +import { DefaultApiController } from './controllers'; + +export type ApiModuleConfiguration = { + /** + * your Api implementations + */ + apiImplementations: ApiImplementations, + /** + * additional Providers that may be used by your implementations + */ + providers?: Provider[], +} + +@Module({}) +export class ApiModule { + static forRoot(configuration: ApiModuleConfiguration): DynamicModule { + const providers: Provider[] = [ + { + provide: DefaultApi, + useClass: configuration.apiImplementations.defaultApi + }, + ...(configuration.providers || []), + ]; + + return { + module: ApiModule, + controllers: [ + DefaultApiController, + ], + providers: [...providers], + exports: [...providers] + } + } +} \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/api/DefaultApi.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/api/DefaultApi.ts new file mode 100644 index 000000000000..5e59e6ad4d11 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/api/DefaultApi.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@nestjs/common'; +import { Observable } from 'rxjs'; + +export type FindPetsByStatusRequestParams = { + pathDefault: string + pathNullable: string + queryDefault: string | undefined + queryDefaultEnum: 'A' | 'B' | 'C' | undefined + queryDefaultInt: number | undefined + headerDefault: string | undefined + headerDefaultEnum: 'A' | 'B' | 'C' | undefined + headerDefaultInt: number | undefined + cookieDefault: string | undefined + cookieDefaultEnum: 'A' | 'B' | 'C' | undefined + cookieDefaultInt: number | undefined + queryNullable: string | null | undefined + headerNullable: string | null | undefined + cookieNullable: string | null | undefined + $query$dollarSign: string | undefined +} + +@Injectable() +export abstract class DefaultApi { + abstract findPetsByStatus(findPetsByStatusRequestParams: FindPetsByStatusRequestParams, request: Request): void | Promise | Observable; + + +} \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/api/index.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/api/index.ts new file mode 100644 index 000000000000..a1aa4698ff25 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/api/index.ts @@ -0,0 +1 @@ +export * from './DefaultApi'; diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/DefaultApi.controller.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/DefaultApi.controller.ts new file mode 100644 index 000000000000..adf0aa0e0577 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/DefaultApi.controller.ts @@ -0,0 +1,15 @@ +import { Body, Controller, Get, Param, Query, Headers, Req } from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { Cookies } from '../cookies-decorator'; +import { DefaultApi } from '../api'; + +@Controller() +export class DefaultApiController { + constructor(private readonly defaultApi: DefaultApi) {} + + @Get('/test/parameters/:path_default/:path_nullable') + findPetsByStatus(@Param('path_default') pathDefault: string, @Param('path_nullable') pathNullable: string, @Query('query_default') queryDefault: string | undefined = 'available', @Query('query_default_enum') queryDefaultEnum: 'A' | 'B' | 'C' | undefined = 'B', @Query('query_default_int') queryDefaultInt: number | undefined = 3, @Headers('header_default') headerDefault: string | undefined = 'available', @Headers('header_default_enum') headerDefaultEnum: 'A' | 'B' | 'C' | undefined = 'B', @Headers('header_default_int') headerDefaultInt: number | undefined = 3, @Cookies('cookie_default') cookieDefault: string | undefined = 'available', @Cookies('cookie_default_enum') cookieDefaultEnum: 'A' | 'B' | 'C' | undefined = 'B', @Cookies('cookie_default_int') cookieDefaultInt: number | undefined = 3, @Query('query_nullable') queryNullable: string | null | undefined, @Headers('header_nullable') headerNullable: string | null | undefined, @Cookies('cookie_nullable') cookieNullable: string | null | undefined, @Query('$query-$dollar-sign') $query$dollarSign: string | undefined, @Req() request: Request): void | Promise | Observable { + return this.defaultApi.findPetsByStatus({ pathDefault, pathNullable, queryDefault, queryDefaultEnum, queryDefaultInt, headerDefault, headerDefaultEnum, headerDefaultInt, cookieDefault, cookieDefaultEnum, cookieDefaultInt, queryNullable, headerNullable, cookieNullable, $query$dollarSign, }, request); + } + +} \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/index.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/index.ts new file mode 100644 index 000000000000..d3d9a7becb6b --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/index.ts @@ -0,0 +1 @@ +export * from './DefaultApi.controller'; diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/cookie-decorator.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/cookie-decorator.ts new file mode 100644 index 000000000000..e9fdcf7354f0 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/cookie-decorator.ts @@ -0,0 +1,6 @@ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; + +export const Cookies = createParamDecorator((data: string, ctx: ExecutionContext) => { +const request = ctx.switchToHttp().getRequest(); +return data ? request.cookies?.[data] : request.cookies; +}); \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/cookies-decorator.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/cookies-decorator.ts new file mode 100644 index 000000000000..e9fdcf7354f0 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/cookies-decorator.ts @@ -0,0 +1,6 @@ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; + +export const Cookies = createParamDecorator((data: string, ctx: ExecutionContext) => { +const request = ctx.switchToHttp().getRequest(); +return data ? request.cookies?.[data] : request.cookies; +}); \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/index.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/index.ts new file mode 100644 index 000000000000..0f2a6865ff56 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/index.ts @@ -0,0 +1,2 @@ +export * from './api.module' +export * from './api-implementations' \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/models/index.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/models/index.ts new file mode 100644 index 000000000000..0519ecba6ea9 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/models/index.ts @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/tsconfig.json b/samples/server/petstore/typescript-nestjs-server/builds/parameters/tsconfig.json new file mode 100644 index 000000000000..7f6bd7f1eb77 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2023", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": true, + "noImplicitAny": false, + "strictBindCallApply": false, + "noFallthroughCasesInSwitch": false + } +} diff --git a/samples/server/petstore/typescript-nestjs-server/package-lock.json b/samples/server/petstore/typescript-nestjs-server/package-lock.json index fa553d0ffb9c..8099ebd15d17 100644 --- a/samples/server/petstore/typescript-nestjs-server/package-lock.json +++ b/samples/server/petstore/typescript-nestjs-server/package-lock.json @@ -12,6 +12,7 @@ "@nestjs/common": "^11.0.1", "@nestjs/core": "^11.0.1", "@nestjs/platform-express": "^11.1.5", + "cookie-parser": "^1.4.7", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1" }, @@ -23,6 +24,7 @@ "@nestjs/testing": "^11.0.1", "@swc/cli": "^0.6.0", "@swc/core": "^1.10.7", + "@types/cookie-parser": "^1.4.10", "@types/express": "^5.0.0", "@types/jest": "^29.5.14", "@types/node": "^22.10.7", @@ -3147,6 +3149,16 @@ "@types/node": "*" } }, + "node_modules/@types/cookie-parser": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.10.tgz", + "integrity": "sha512-B4xqkqfZ8Wek+rCOeRxsjMS9OgvzebEzzLYw7NHYuvzb7IdxOkI0ZHGgeEBX4PUM7QGVvNSK60T3OvWj3YfBRg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/express": "*" + } + }, "node_modules/@types/cookiejar": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", @@ -3369,6 +3381,14 @@ "@types/superagent": "^8.1.0" } }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -5229,6 +5249,27 @@ "dev": true, "license": "MIT" }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/class-validator": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz", + "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@types/validator": "^13.15.3", + "libphonenumber-js": "^1.11.1", + "validator": "^13.15.20" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -5504,6 +5545,25 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, "node_modules/cookie-signature": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", @@ -8328,6 +8388,14 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.12.36", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.36.tgz", + "integrity": "sha512-woWhKMAVx1fzzUnMCyOzglgSgf6/AFHLASdOBcchYCyvWSGWt12imw3iu2hdI5d4dGZRsNWAmWiz37sDKUPaRQ==", + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -11158,6 +11226,17 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.15.26", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz", + "integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/samples/server/petstore/typescript-nestjs-server/package.json b/samples/server/petstore/typescript-nestjs-server/package.json index 5903b9f7aa05..f924c1123c13 100644 --- a/samples/server/petstore/typescript-nestjs-server/package.json +++ b/samples/server/petstore/typescript-nestjs-server/package.json @@ -19,6 +19,7 @@ "@nestjs/common": "^11.0.1", "@nestjs/core": "^11.0.1", "@nestjs/platform-express": "^11.1.5", + "cookie-parser": "^1.4.7", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1" }, @@ -30,6 +31,7 @@ "@nestjs/testing": "^11.0.1", "@swc/cli": "^0.6.0", "@swc/core": "^1.10.7", + "@types/cookie-parser": "^1.4.10", "@types/express": "^5.0.0", "@types/jest": "^29.5.14", "@types/node": "^22.10.7", diff --git a/samples/server/petstore/typescript-nestjs-server/src/app.module.ts b/samples/server/petstore/typescript-nestjs-server/src/app.module.ts index fc5667a2e4ca..cf7f3e96d16d 100644 --- a/samples/server/petstore/typescript-nestjs-server/src/app.module.ts +++ b/samples/server/petstore/typescript-nestjs-server/src/app.module.ts @@ -1,9 +1,11 @@ import { Module } from '@nestjs/common'; -import { PetService } from './handlers/PetService'; -import { UserService } from './handlers/UserService'; -import { StoreService } from './handlers/StoreService'; +import { PetService } from './handlers/default/PetService'; +import { UserService } from './handlers/default/UserService'; +import { StoreService } from './handlers/default/StoreService'; import { ApiModule } from '../builds/default'; +import { ApiModule as ParamsModule } from '../builds/parameters'; import { TestService } from './TestService'; +import { DefaultService } from './handlers/parameters/DefaultService'; @Module({ imports: [ @@ -12,9 +14,14 @@ import { TestService } from './TestService'; petApi: PetService, userApi: UserService, storeApi: StoreService, - }, - providers: [TestService] -}), + }, + providers: [TestService], + }), + ParamsModule.forRoot({ + apiImplementations: { + defaultApi: DefaultService, + }, + }), ], controllers: [], providers: [], diff --git a/samples/server/petstore/typescript-nestjs-server/src/handlers/PetService.ts b/samples/server/petstore/typescript-nestjs-server/src/handlers/default/PetService.ts similarity index 91% rename from samples/server/petstore/typescript-nestjs-server/src/handlers/PetService.ts rename to samples/server/petstore/typescript-nestjs-server/src/handlers/default/PetService.ts index 858a93019a30..99b291766c48 100644 --- a/samples/server/petstore/typescript-nestjs-server/src/handlers/PetService.ts +++ b/samples/server/petstore/typescript-nestjs-server/src/handlers/default/PetService.ts @@ -1,9 +1,9 @@ import { Observable } from 'rxjs'; import { Injectable } from '@nestjs/common'; -import { PetApi } from '../../builds/default/api'; -import { ApiResponse, Pet } from '../../builds/default/models'; -import { TestService } from '../TestService' +import { PetApi } from '../../../builds/default/api'; +import { ApiResponse, Pet } from '../../../builds/default/models'; +import { TestService } from '../../TestService' @Injectable() export class PetService implements PetApi { diff --git a/samples/server/petstore/typescript-nestjs-server/src/handlers/StoreService.ts b/samples/server/petstore/typescript-nestjs-server/src/handlers/default/StoreService.ts similarity index 88% rename from samples/server/petstore/typescript-nestjs-server/src/handlers/StoreService.ts rename to samples/server/petstore/typescript-nestjs-server/src/handlers/default/StoreService.ts index dc299f65cea5..49d8f1a78e93 100644 --- a/samples/server/petstore/typescript-nestjs-server/src/handlers/StoreService.ts +++ b/samples/server/petstore/typescript-nestjs-server/src/handlers/default/StoreService.ts @@ -1,7 +1,7 @@ import { Observable } from 'rxjs'; import { Injectable } from '@nestjs/common'; -import { StoreApi } from '../../builds/default/api'; -import { Order } from '../../builds/default/models'; +import { StoreApi } from '../../../builds/default/api'; +import { Order } from '../../../builds/default/models'; @Injectable() export class StoreService implements StoreApi { diff --git a/samples/server/petstore/typescript-nestjs-server/src/handlers/UserService.ts b/samples/server/petstore/typescript-nestjs-server/src/handlers/default/UserService.ts similarity index 92% rename from samples/server/petstore/typescript-nestjs-server/src/handlers/UserService.ts rename to samples/server/petstore/typescript-nestjs-server/src/handlers/default/UserService.ts index 3e4e1257634b..7407c48ba611 100644 --- a/samples/server/petstore/typescript-nestjs-server/src/handlers/UserService.ts +++ b/samples/server/petstore/typescript-nestjs-server/src/handlers/default/UserService.ts @@ -1,7 +1,7 @@ import { Observable } from 'rxjs'; import { Injectable } from '@nestjs/common'; -import { UserApi } from '../../builds/default/api'; -import { User } from '../../builds/default/models'; +import { UserApi } from '../../../builds/default/api'; +import { User } from '../../../builds/default/models'; @Injectable() export class UserService implements UserApi { diff --git a/samples/server/petstore/typescript-nestjs-server/src/handlers/parameters/DefaultService.ts b/samples/server/petstore/typescript-nestjs-server/src/handlers/parameters/DefaultService.ts new file mode 100644 index 000000000000..acf0abfd872e --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/src/handlers/parameters/DefaultService.ts @@ -0,0 +1,19 @@ +import { + DefaultApi, + FindPetsByStatusRequestParams, +} from '../../../builds/parameters/api'; +import { Observable } from 'rxjs'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class DefaultService extends DefaultApi { + + lastRequestParams?: FindPetsByStatusRequestParams; + + findPetsByStatus( + findPetsByStatusRequestParams: FindPetsByStatusRequestParams, + request: Request, + ): void | Promise | Observable { + this.lastRequestParams = findPetsByStatusRequestParams; + } +} \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/src/main.ts b/samples/server/petstore/typescript-nestjs-server/src/main.ts index f76bc8d977be..a7d3d3d52304 100644 --- a/samples/server/petstore/typescript-nestjs-server/src/main.ts +++ b/samples/server/petstore/typescript-nestjs-server/src/main.ts @@ -1,8 +1,11 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; +import * as cookieParser from 'cookie-parser'; async function bootstrap() { const app = await NestFactory.create(AppModule); + app.use(cookieParser()); await app.listen(process.env.PORT ?? 3000); } + bootstrap(); diff --git a/samples/server/petstore/typescript-nestjs-server/test/app.e2e-spec.ts b/samples/server/petstore/typescript-nestjs-server/test/app.e2e-spec.ts index 84b901ad0568..a1b831948332 100644 --- a/samples/server/petstore/typescript-nestjs-server/test/app.e2e-spec.ts +++ b/samples/server/petstore/typescript-nestjs-server/test/app.e2e-spec.ts @@ -4,6 +4,9 @@ import * as request from 'supertest'; import { App } from 'supertest/types'; import { AppModule } from '../src/app.module'; import { PetApiController } from '../builds/default/controllers'; +import { DefaultService } from '../src/handlers/parameters/DefaultService'; +import { DefaultApi } from '../builds/parameters/api'; +import * as cookieParser from 'cookie-parser'; describe('AppModule (e2e)', () => { let app: INestApplication; @@ -14,11 +17,10 @@ describe('AppModule (e2e)', () => { }).compile(); app = moduleFixture.createNestApplication(); + app.use(cookieParser()); await app.init(); }); - - describe('PetApiController', () => { it('should be provided', () => { const petApiController = app.get(PetApiController); @@ -29,25 +31,95 @@ describe('AppModule (e2e)', () => { return request(app.getHttpServer()) .get('/pet/1') .expect(200) - .expect({name: 'MyPetB', photoUrls: []}); + .expect({ name: 'MyPetB', photoUrls: [] }); }); it('should handle POST request with body', () => { return request(app.getHttpServer()) .post('/pet') - .send({name: 'POST Pet'}) + .send({ name: 'POST Pet' }) .expect(201) - .expect({name: 'POST Pet'}); + .expect({ name: 'POST Pet' }); }); it('should handle GET request with query params', () => { return request(app.getHttpServer()) .get('/pet/findByStatus') - .query({status: ['available', 'pending']}) + .query({ status: ['available', 'pending'] }) .expect(200) - .expect([{name: 'available', photoUrls: []}, {name: 'pending', photoUrls: []}]); + .expect([ + { name: 'available', photoUrls: [] }, + { name: 'pending', photoUrls: [] }, + ]); + }); + }); + + describe('ParametersDefaultController', () => { + it('should set default parameters', () => { + let defaultService: DefaultService = app.get(DefaultApi); + + request(app.getHttpServer()) + .get('/test/parameters/path_a/path_b') + .expect(200, () => { + expect(defaultService.lastRequestParams).toEqual({ + pathDefault: 'path_a', + pathNullable: 'path_b', + queryDefault: 'available', + queryDefaultEnum: 'B', + queryDefaultInt: 3, + headerDefault: 'available', + headerDefaultEnum: 'B', + headerDefaultInt: 3, + cookieDefault: 'available', + cookieDefaultEnum: 'B', + cookieDefaultInt: 3, + queryNullable: undefined, + headerNullable: undefined, + cookieNullable: undefined, + $query$dollarSign: undefined, + }); + }); }); - }) + it('should receive request parameters', () => { + let defaultService: DefaultService = app.get(DefaultApi); + request(app.getHttpServer()) + .get('/test/parameters/path_a/path_b') + .query({ + query_default: 'custom_query', + query_default_enum: 'C', + query_default_int: 5, + query_nullable: 'not_null_query', + '$query-$dollar-sign': '$$$', + }) + .set('header_default', 'custom_header') + .set('header_default_enum', 'C') + .set('header_default_int', '6') + .set('header_nullable', 'null') + .set( + 'Cookie', + 'cookie_default=custom_cookie; cookie_default_enum=C; cookie_default_int=7; cookie_nullable=a_cookie', + ) + .expect(200, () => { + expect(defaultService.lastRequestParams).toEqual({ + pathDefault: 'path_a', + pathNullable: 'path_b', + queryDefault: 'custom_query', + queryDefaultEnum: 'C', + queryDefaultInt: '5', + headerDefault: 'custom_header', + headerDefaultEnum: 'C', + headerDefaultInt: '6', + cookieDefault: 'custom_cookie', + cookieDefaultEnum: 'C', + cookieDefaultInt: '7', + queryNullable: 'not_null_query', + headerNullable: 'null', + cookieNullable: 'a_cookie', + $query$dollarSign: '$$$', + }); + }); + }); + }); }); From cd3b9a182d7af1e75bc12aac1df2f5ab97144359 Mon Sep 17 00:00:00 2001 From: Ary Obenholzner Date: Fri, 13 Feb 2026 20:52:49 +0100 Subject: [PATCH 4/7] [typescript-nestjs-server] #22928 add docs, fix indentations and test execution --- .../TypeScriptNestjsServerCodegen.java | 10 +++++++++- .../cookies-decorator.mustache | 19 +++++++++++++++++-- .../builds/default/cookies-decorator.ts | 19 +++++++++++++++++-- .../builds/parameters/cookies-decorator.ts | 19 +++++++++++++++++-- .../test/app.e2e-spec.ts | 10 ++++++---- 5 files changed, 66 insertions(+), 11 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java index 4e75e652cc28..f95b5cc80853 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java @@ -278,6 +278,14 @@ private boolean isLanguagePrimitive(String type) { return languageSpecificPrimitives.contains(type) || isInlineUnion(type); } + /** + *

+ * Determines if the given type is an inline union of strings, described as an enum without being an explicit component in OpenAPI spec. + *

+ * Example input that matches: {@code "'A' | 'B'" } + * + * @param type The Typescript type to evaluate. + */ private boolean isInlineUnion(String type) { return Arrays.stream(type.split("\\|")) .map(String::trim) @@ -301,7 +309,7 @@ private boolean isRecordType(String type) { public void postProcessParameter(CodegenParameter parameter) { super.postProcessParameter(parameter); parameter.dataType = applyLocalTypeMapping(parameter.dataType); - if("undefined".equals(parameter.defaultValue)) { + if ("undefined".equals(parameter.defaultValue)) { parameter.defaultValue = null; } } diff --git a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/cookies-decorator.mustache b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/cookies-decorator.mustache index e9fdcf7354f0..19927e8c299c 100644 --- a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/cookies-decorator.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/cookies-decorator.mustache @@ -1,6 +1,21 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; +/** +* A decorator function for retrieving cookies from the request object in an HTTP context. +* +* This decorator only works, if the framework specific cookie middleware is installed and enabled. +* - For Express, you need to use the `cookie-parser` middleware. +* - For Fastify, you need to use the `@fastify/cookie` plugin. +* +* Consult https://docs.nestjs.com/techniques/cookies for further information +* +* Usage: +* ``` +* @Get() +* findAll(@Cookies('name') name: string) {} +* ``` +*/ export const Cookies = createParamDecorator((data: string, ctx: ExecutionContext) => { -const request = ctx.switchToHttp().getRequest(); -return data ? request.cookies?.[data] : request.cookies; + const request = ctx.switchToHttp().getRequest(); + return data ? request.cookies?.[data] : request.cookies; }); \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/cookies-decorator.ts b/samples/server/petstore/typescript-nestjs-server/builds/default/cookies-decorator.ts index e9fdcf7354f0..19927e8c299c 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/default/cookies-decorator.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/cookies-decorator.ts @@ -1,6 +1,21 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; +/** +* A decorator function for retrieving cookies from the request object in an HTTP context. +* +* This decorator only works, if the framework specific cookie middleware is installed and enabled. +* - For Express, you need to use the `cookie-parser` middleware. +* - For Fastify, you need to use the `@fastify/cookie` plugin. +* +* Consult https://docs.nestjs.com/techniques/cookies for further information +* +* Usage: +* ``` +* @Get() +* findAll(@Cookies('name') name: string) {} +* ``` +*/ export const Cookies = createParamDecorator((data: string, ctx: ExecutionContext) => { -const request = ctx.switchToHttp().getRequest(); -return data ? request.cookies?.[data] : request.cookies; + const request = ctx.switchToHttp().getRequest(); + return data ? request.cookies?.[data] : request.cookies; }); \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/cookies-decorator.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/cookies-decorator.ts index e9fdcf7354f0..19927e8c299c 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/parameters/cookies-decorator.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/cookies-decorator.ts @@ -1,6 +1,21 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; +/** +* A decorator function for retrieving cookies from the request object in an HTTP context. +* +* This decorator only works, if the framework specific cookie middleware is installed and enabled. +* - For Express, you need to use the `cookie-parser` middleware. +* - For Fastify, you need to use the `@fastify/cookie` plugin. +* +* Consult https://docs.nestjs.com/techniques/cookies for further information +* +* Usage: +* ``` +* @Get() +* findAll(@Cookies('name') name: string) {} +* ``` +*/ export const Cookies = createParamDecorator((data: string, ctx: ExecutionContext) => { -const request = ctx.switchToHttp().getRequest(); -return data ? request.cookies?.[data] : request.cookies; + const request = ctx.switchToHttp().getRequest(); + return data ? request.cookies?.[data] : request.cookies; }); \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/test/app.e2e-spec.ts b/samples/server/petstore/typescript-nestjs-server/test/app.e2e-spec.ts index a1b831948332..7184f5c4ce7b 100644 --- a/samples/server/petstore/typescript-nestjs-server/test/app.e2e-spec.ts +++ b/samples/server/petstore/typescript-nestjs-server/test/app.e2e-spec.ts @@ -58,9 +58,10 @@ describe('AppModule (e2e)', () => { it('should set default parameters', () => { let defaultService: DefaultService = app.get(DefaultApi); - request(app.getHttpServer()) + return request(app.getHttpServer()) .get('/test/parameters/path_a/path_b') - .expect(200, () => { + .expect(200) + .then(() => { expect(defaultService.lastRequestParams).toEqual({ pathDefault: 'path_a', pathNullable: 'path_b', @@ -84,7 +85,7 @@ describe('AppModule (e2e)', () => { it('should receive request parameters', () => { let defaultService: DefaultService = app.get(DefaultApi); - request(app.getHttpServer()) + return request(app.getHttpServer()) .get('/test/parameters/path_a/path_b') .query({ query_default: 'custom_query', @@ -101,7 +102,8 @@ describe('AppModule (e2e)', () => { 'Cookie', 'cookie_default=custom_cookie; cookie_default_enum=C; cookie_default_int=7; cookie_nullable=a_cookie', ) - .expect(200, () => { + .expect(200) + .then(() => { expect(defaultService.lastRequestParams).toEqual({ pathDefault: 'path_a', pathNullable: 'path_b', From f3b4491c80ea35af8ae3d62f12330574ec3af3e9 Mon Sep 17 00:00:00 2001 From: Ary Obenholzner Date: Fri, 13 Feb 2026 23:59:00 +0100 Subject: [PATCH 5/7] [typescript-nestjs-server] #22928 correctly parse numeric parameters, use DefaultValuePipe for default values --- .../TypeScriptNestjsServerCodegen.java | 4 +++- .../controller.mustache | 6 ++--- .../cookies-decorator.mustache | 5 +++- .../decorators.mustache | 2 ++ .../headers-decorator.mustache | 16 +++++++++++++ .../paramPipe.mustache | 1 + .../builds/default/.openapi-generator/FILES | 4 +++- .../default/controllers/PetApi.controller.ts | 4 ++-- .../controllers/StoreApi.controller.ts | 4 ++-- .../default/controllers/UserApi.controller.ts | 4 ++-- .../{ => decorators}/cookies-decorator.ts | 5 +++- .../default/decorators/headers-decorator.ts | 16 +++++++++++++ .../builds/default/decorators/index.ts | 2 ++ .../parameters/.openapi-generator/FILES | 4 +++- .../controllers/DefaultApi.controller.ts | 6 ++--- .../decorators/cookies-decorator.ts | 24 +++++++++++++++++++ .../decorators/headers-decorator.ts | 16 +++++++++++++ .../builds/parameters/decorators/index.ts | 2 ++ .../test/app.e2e-spec.ts | 10 +++++--- 19 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/typescript-nestjs-server/decorators.mustache create mode 100644 modules/openapi-generator/src/main/resources/typescript-nestjs-server/headers-decorator.mustache create mode 100644 modules/openapi-generator/src/main/resources/typescript-nestjs-server/paramPipe.mustache rename samples/server/petstore/typescript-nestjs-server/builds/default/{ => decorators}/cookies-decorator.ts (82%) create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/default/decorators/headers-decorator.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/default/decorators/index.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/cookies-decorator.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/headers-decorator.ts create mode 100644 samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/index.ts diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java index f95b5cc80853..46f2ccb59a0f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java @@ -156,7 +156,9 @@ public void processOpts() { supportingFiles.add(new SupportingFile("api-implementations.mustache", "", "api-implementations.ts")); supportingFiles.add(new SupportingFile("api.module.mustache", "", "api.module.ts")); supportingFiles.add(new SupportingFile("controllers.mustache", "controllers", "index.ts")); - supportingFiles.add(new SupportingFile("cookies-decorator.mustache", "cookies-decorator.ts")); + supportingFiles.add(new SupportingFile("cookies-decorator.mustache", "decorators", "cookies-decorator.ts")); + supportingFiles.add(new SupportingFile("headers-decorator.mustache", "decorators", "headers-decorator.ts")); + supportingFiles.add(new SupportingFile("decorators.mustache", "decorators", "index.ts")); supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore")); supportingFiles.add(new SupportingFile("README.md", "", "README.md")); supportingFiles.add(new SupportingFile("tsconfig.mustache", "", "tsconfig.json")); diff --git a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/controller.mustache b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/controller.mustache index 53c0a759c875..5438aa5b185c 100644 --- a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/controller.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/controller.mustache @@ -1,6 +1,6 @@ -import { Body, Controller{{#httpMethods}}, {{.}}{{/httpMethods}}, Param, Query, Headers, Req } from '@nestjs/common'; +import { Body, Controller, DefaultValuePipe{{#httpMethods}}, {{.}}{{/httpMethods}}, Param, ParseIntPipe, ParseFloatPipe, Query, Req } from '@nestjs/common'; import { Observable } from 'rxjs'; -import { Cookies } from '../cookies-decorator'; +import { Cookies, Headers } from '../decorators'; import { {{classname}} } from '../{{apiPackage}}'; {{#tsImports.0}} import { {{#tsImports}}{{classname}}, {{/tsImports}} } from '../{{modelPackage}}'; @@ -13,7 +13,7 @@ export class {{classname}}Controller { {{#operations}} {{#operation}} @{{#vendorExtensions.x-http-method}}{{.}}{{/vendorExtensions.x-http-method}}{{^vendorExtensions.x-http-method}}{{httpMethod}}{{/vendorExtensions.x-http-method}}('{{path}}') - {{operationId}}({{#allParams}}{{#isPathParam}}@Param('{{baseName}}') {{/isPathParam}}{{#isQueryParam}}@Query('{{baseName}}') {{/isQueryParam}}{{#isHeaderParam}}@Headers('{{baseName}}') {{/isHeaderParam}}{{#isCookieParam}}@Cookies('{{baseName}}') {{/isCookieParam}}{{#isBodyParam}}@Body() {{/isBodyParam}}{{paramName}}: {{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{^required}} | undefined{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}, {{/allParams}}@Req() request: Request): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} | Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> | Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> { + {{operationId}}({{#allParams}}{{#isPathParam}}@Param('{{baseName}}'{{>paramPipe}}) {{/isPathParam}}{{#isQueryParam}}@Query('{{baseName}}'{{>paramPipe}}) {{/isQueryParam}}{{#isHeaderParam}}@Headers('{{baseName}}'{{>paramPipe}}) {{/isHeaderParam}}{{#isCookieParam}}@Cookies('{{baseName}}'{{>paramPipe}}) {{/isCookieParam}}{{#isBodyParam}}@Body() {{/isBodyParam}}{{paramName}}: {{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{^required}} | undefined{{/required}}, {{/allParams}}@Req() request: Request): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} | Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> | Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> { return this.{{classVarName}}.{{operationId}}({{#useSingleRequestParameter}}{ {{/useSingleRequestParameter}}{{#allParams}}{{paramName}}, {{/allParams}}{{#useSingleRequestParameter}}}, {{/useSingleRequestParameter}}request); } diff --git a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/cookies-decorator.mustache b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/cookies-decorator.mustache index 19927e8c299c..e61332374128 100644 --- a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/cookies-decorator.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/cookies-decorator.mustache @@ -17,5 +17,8 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; */ export const Cookies = createParamDecorator((data: string, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); - return data ? request.cookies?.[data] : request.cookies; + if (!data) { + return { ...request.cookies, ...request.signedCookies }; + } + return request.cookies?.[data] ?? request.signedCookies?.[data]; }); \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/decorators.mustache b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/decorators.mustache new file mode 100644 index 000000000000..4bd1d7be5b42 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/decorators.mustache @@ -0,0 +1,2 @@ +export * from './cookies-decorator'; +export * from './headers-decorator'; \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/headers-decorator.mustache b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/headers-decorator.mustache new file mode 100644 index 000000000000..2c2a16636685 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/headers-decorator.mustache @@ -0,0 +1,16 @@ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; + +/** +* A decorator function for retrieving headers from the request object in an HTTP context. +* Workaround for enabling PipeTransformers on Headers (see https://github.com/nestjs/nest/issues/356) +* +* Usage: +* ``` +* @Get() +* findAll(@Headers('name') name: string) {} +* ``` +*/ +export const Headers = createParamDecorator((data: string, ctx: ExecutionContext) => { +const request = ctx.switchToHttp().getRequest(); +return data ? request.headers?.[data] : request.headers; +}); \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/paramPipe.mustache b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/paramPipe.mustache new file mode 100644 index 000000000000..5082146db2fe --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/paramPipe.mustache @@ -0,0 +1 @@ +{{#defaultValue}}, new DefaultValuePipe({{{defaultValue}}}){{/defaultValue}}{{#isNumber}}, {{#isFloat}}ParseFloatPipe{{/isFloat}}{{^isFloat}}ParseIntPipe{{/isFloat}}{{/isNumber}} \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/.openapi-generator/FILES b/samples/server/petstore/typescript-nestjs-server/builds/default/.openapi-generator/FILES index 4a89ba119c5c..9196bafc8c4e 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/default/.openapi-generator/FILES +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/.openapi-generator/FILES @@ -10,7 +10,9 @@ controllers/PetApi.controller.ts controllers/StoreApi.controller.ts controllers/UserApi.controller.ts controllers/index.ts -cookies-decorator.ts +decorators/cookies-decorator.ts +decorators/headers-decorator.ts +decorators/index.ts index.ts models/api-response.ts models/category.ts diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/PetApi.controller.ts b/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/PetApi.controller.ts index 694146d53fa2..1dd604f5b73a 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/PetApi.controller.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/PetApi.controller.ts @@ -1,6 +1,6 @@ -import { Body, Controller, Delete, Get, Post, Put, Param, Query, Headers, Req } from '@nestjs/common'; +import { Body, Controller, DefaultValuePipe, Delete, Get, Post, Put, Param, ParseIntPipe, ParseFloatPipe, Query, Req } from '@nestjs/common'; import { Observable } from 'rxjs'; -import { Cookies } from '../cookies-decorator'; +import { Cookies, Headers } from '../decorators'; import { PetApi } from '../api'; import { ApiResponse, Pet, } from '../models'; diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/StoreApi.controller.ts b/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/StoreApi.controller.ts index 9fa672cf5f55..c9e5f52526df 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/StoreApi.controller.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/StoreApi.controller.ts @@ -1,6 +1,6 @@ -import { Body, Controller, Delete, Get, Post, Param, Query, Headers, Req } from '@nestjs/common'; +import { Body, Controller, DefaultValuePipe, Delete, Get, Post, Param, ParseIntPipe, ParseFloatPipe, Query, Req } from '@nestjs/common'; import { Observable } from 'rxjs'; -import { Cookies } from '../cookies-decorator'; +import { Cookies, Headers } from '../decorators'; import { StoreApi } from '../api'; import { Order, } from '../models'; diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/UserApi.controller.ts b/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/UserApi.controller.ts index f948661dc7e9..797964b18e85 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/UserApi.controller.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/controllers/UserApi.controller.ts @@ -1,6 +1,6 @@ -import { Body, Controller, Delete, Get, Post, Put, Param, Query, Headers, Req } from '@nestjs/common'; +import { Body, Controller, DefaultValuePipe, Delete, Get, Post, Put, Param, ParseIntPipe, ParseFloatPipe, Query, Req } from '@nestjs/common'; import { Observable } from 'rxjs'; -import { Cookies } from '../cookies-decorator'; +import { Cookies, Headers } from '../decorators'; import { UserApi } from '../api'; import { User, } from '../models'; diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/cookies-decorator.ts b/samples/server/petstore/typescript-nestjs-server/builds/default/decorators/cookies-decorator.ts similarity index 82% rename from samples/server/petstore/typescript-nestjs-server/builds/default/cookies-decorator.ts rename to samples/server/petstore/typescript-nestjs-server/builds/default/decorators/cookies-decorator.ts index 19927e8c299c..e61332374128 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/default/cookies-decorator.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/decorators/cookies-decorator.ts @@ -17,5 +17,8 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; */ export const Cookies = createParamDecorator((data: string, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); - return data ? request.cookies?.[data] : request.cookies; + if (!data) { + return { ...request.cookies, ...request.signedCookies }; + } + return request.cookies?.[data] ?? request.signedCookies?.[data]; }); \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/decorators/headers-decorator.ts b/samples/server/petstore/typescript-nestjs-server/builds/default/decorators/headers-decorator.ts new file mode 100644 index 000000000000..2c2a16636685 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/decorators/headers-decorator.ts @@ -0,0 +1,16 @@ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; + +/** +* A decorator function for retrieving headers from the request object in an HTTP context. +* Workaround for enabling PipeTransformers on Headers (see https://github.com/nestjs/nest/issues/356) +* +* Usage: +* ``` +* @Get() +* findAll(@Headers('name') name: string) {} +* ``` +*/ +export const Headers = createParamDecorator((data: string, ctx: ExecutionContext) => { +const request = ctx.switchToHttp().getRequest(); +return data ? request.headers?.[data] : request.headers; +}); \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/decorators/index.ts b/samples/server/petstore/typescript-nestjs-server/builds/default/decorators/index.ts new file mode 100644 index 000000000000..4bd1d7be5b42 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/decorators/index.ts @@ -0,0 +1,2 @@ +export * from './cookies-decorator'; +export * from './headers-decorator'; \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator/FILES b/samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator/FILES index 40300313d172..b193787b4883 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator/FILES +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/.openapi-generator/FILES @@ -6,7 +6,9 @@ api/DefaultApi.ts api/index.ts controllers/DefaultApi.controller.ts controllers/index.ts -cookies-decorator.ts +decorators/cookies-decorator.ts +decorators/headers-decorator.ts +decorators/index.ts index.ts models/index.ts tsconfig.json diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/DefaultApi.controller.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/DefaultApi.controller.ts index adf0aa0e0577..18daae527d6c 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/DefaultApi.controller.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/DefaultApi.controller.ts @@ -1,6 +1,6 @@ -import { Body, Controller, Get, Param, Query, Headers, Req } from '@nestjs/common'; +import { Body, Controller, DefaultValuePipe, Get, Param, ParseIntPipe, ParseFloatPipe, Query, Req } from '@nestjs/common'; import { Observable } from 'rxjs'; -import { Cookies } from '../cookies-decorator'; +import { Cookies, Headers } from '../decorators'; import { DefaultApi } from '../api'; @Controller() @@ -8,7 +8,7 @@ export class DefaultApiController { constructor(private readonly defaultApi: DefaultApi) {} @Get('/test/parameters/:path_default/:path_nullable') - findPetsByStatus(@Param('path_default') pathDefault: string, @Param('path_nullable') pathNullable: string, @Query('query_default') queryDefault: string | undefined = 'available', @Query('query_default_enum') queryDefaultEnum: 'A' | 'B' | 'C' | undefined = 'B', @Query('query_default_int') queryDefaultInt: number | undefined = 3, @Headers('header_default') headerDefault: string | undefined = 'available', @Headers('header_default_enum') headerDefaultEnum: 'A' | 'B' | 'C' | undefined = 'B', @Headers('header_default_int') headerDefaultInt: number | undefined = 3, @Cookies('cookie_default') cookieDefault: string | undefined = 'available', @Cookies('cookie_default_enum') cookieDefaultEnum: 'A' | 'B' | 'C' | undefined = 'B', @Cookies('cookie_default_int') cookieDefaultInt: number | undefined = 3, @Query('query_nullable') queryNullable: string | null | undefined, @Headers('header_nullable') headerNullable: string | null | undefined, @Cookies('cookie_nullable') cookieNullable: string | null | undefined, @Query('$query-$dollar-sign') $query$dollarSign: string | undefined, @Req() request: Request): void | Promise | Observable { + findPetsByStatus(@Param('path_default') pathDefault: string, @Param('path_nullable') pathNullable: string, @Query('query_default', new DefaultValuePipe('available')) queryDefault: string | undefined, @Query('query_default_enum', new DefaultValuePipe('B')) queryDefaultEnum: 'A' | 'B' | 'C' | undefined, @Query('query_default_int', new DefaultValuePipe(3), ParseIntPipe) queryDefaultInt: number | undefined, @Headers('header_default', new DefaultValuePipe('available')) headerDefault: string | undefined, @Headers('header_default_enum', new DefaultValuePipe('B')) headerDefaultEnum: 'A' | 'B' | 'C' | undefined, @Headers('header_default_int', new DefaultValuePipe(3), ParseIntPipe) headerDefaultInt: number | undefined, @Cookies('cookie_default', new DefaultValuePipe('available')) cookieDefault: string | undefined, @Cookies('cookie_default_enum', new DefaultValuePipe('B')) cookieDefaultEnum: 'A' | 'B' | 'C' | undefined, @Cookies('cookie_default_int', new DefaultValuePipe(3), ParseIntPipe) cookieDefaultInt: number | undefined, @Query('query_nullable') queryNullable: string | null | undefined, @Headers('header_nullable') headerNullable: string | null | undefined, @Cookies('cookie_nullable') cookieNullable: string | null | undefined, @Query('$query-$dollar-sign') $query$dollarSign: string | undefined, @Req() request: Request): void | Promise | Observable { return this.defaultApi.findPetsByStatus({ pathDefault, pathNullable, queryDefault, queryDefaultEnum, queryDefaultInt, headerDefault, headerDefaultEnum, headerDefaultInt, cookieDefault, cookieDefaultEnum, cookieDefaultInt, queryNullable, headerNullable, cookieNullable, $query$dollarSign, }, request); } diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/cookies-decorator.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/cookies-decorator.ts new file mode 100644 index 000000000000..e61332374128 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/cookies-decorator.ts @@ -0,0 +1,24 @@ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; + +/** +* A decorator function for retrieving cookies from the request object in an HTTP context. +* +* This decorator only works, if the framework specific cookie middleware is installed and enabled. +* - For Express, you need to use the `cookie-parser` middleware. +* - For Fastify, you need to use the `@fastify/cookie` plugin. +* +* Consult https://docs.nestjs.com/techniques/cookies for further information +* +* Usage: +* ``` +* @Get() +* findAll(@Cookies('name') name: string) {} +* ``` +*/ +export const Cookies = createParamDecorator((data: string, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest(); + if (!data) { + return { ...request.cookies, ...request.signedCookies }; + } + return request.cookies?.[data] ?? request.signedCookies?.[data]; +}); \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/headers-decorator.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/headers-decorator.ts new file mode 100644 index 000000000000..2c2a16636685 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/headers-decorator.ts @@ -0,0 +1,16 @@ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; + +/** +* A decorator function for retrieving headers from the request object in an HTTP context. +* Workaround for enabling PipeTransformers on Headers (see https://github.com/nestjs/nest/issues/356) +* +* Usage: +* ``` +* @Get() +* findAll(@Headers('name') name: string) {} +* ``` +*/ +export const Headers = createParamDecorator((data: string, ctx: ExecutionContext) => { +const request = ctx.switchToHttp().getRequest(); +return data ? request.headers?.[data] : request.headers; +}); \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/index.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/index.ts new file mode 100644 index 000000000000..4bd1d7be5b42 --- /dev/null +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/index.ts @@ -0,0 +1,2 @@ +export * from './cookies-decorator'; +export * from './headers-decorator'; \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/test/app.e2e-spec.ts b/samples/server/petstore/typescript-nestjs-server/test/app.e2e-spec.ts index 7184f5c4ce7b..45aa15d283e1 100644 --- a/samples/server/petstore/typescript-nestjs-server/test/app.e2e-spec.ts +++ b/samples/server/petstore/typescript-nestjs-server/test/app.e2e-spec.ts @@ -60,6 +60,9 @@ describe('AppModule (e2e)', () => { return request(app.getHttpServer()) .get('/test/parameters/path_a/path_b') + .expect((res)=> { + console.log('Body:', JSON.stringify(res.body, null, 2)); + }) .expect(200) .then(() => { expect(defaultService.lastRequestParams).toEqual({ @@ -82,6 +85,7 @@ describe('AppModule (e2e)', () => { }); }); + it('should receive request parameters', () => { let defaultService: DefaultService = app.get(DefaultApi); @@ -109,13 +113,13 @@ describe('AppModule (e2e)', () => { pathNullable: 'path_b', queryDefault: 'custom_query', queryDefaultEnum: 'C', - queryDefaultInt: '5', + queryDefaultInt: 5, headerDefault: 'custom_header', headerDefaultEnum: 'C', - headerDefaultInt: '6', + headerDefaultInt: 6, cookieDefault: 'custom_cookie', cookieDefaultEnum: 'C', - cookieDefaultInt: '7', + cookieDefaultInt: 7, queryNullable: 'not_null_query', headerNullable: 'null', cookieNullable: 'a_cookie', From bb85c065e6153a1f8089b3558d5a83ba2098d2eb Mon Sep 17 00:00:00 2001 From: Ary Obenholzner Date: Sat, 14 Feb 2026 00:14:33 +0100 Subject: [PATCH 6/7] [typescript-nestjs-server] #22928 lowercase header access, check each import for unions --- .../codegen/languages/TypeScriptNestjsServerCodegen.java | 6 ++++-- .../typescript-nestjs-server/headers-decorator.mustache | 2 +- .../builds/default/decorators/headers-decorator.ts | 2 +- .../builds/parameters/decorators/headers-decorator.ts | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java index 46f2ccb59a0f..8e8a9a2fdc1d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java @@ -449,8 +449,10 @@ private Set parseImports(CodegenModel cm) { for (String name : cm.imports) { if (name.indexOf(" | ") >= 0) { String[] parts = name.split(" \\| "); - if (needToImport(parts[0])) { - Collections.addAll(newImports, parts); + for (String part : parts) { + if (needToImport(part)) { + newImports.add(part); + } } } else { newImports.add(name); diff --git a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/headers-decorator.mustache b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/headers-decorator.mustache index 2c2a16636685..0648f28feba1 100644 --- a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/headers-decorator.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/headers-decorator.mustache @@ -12,5 +12,5 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; */ export const Headers = createParamDecorator((data: string, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); -return data ? request.headers?.[data] : request.headers; +return data ? request.headers?.[data.toLowerCase()] : request.headers; }); \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/default/decorators/headers-decorator.ts b/samples/server/petstore/typescript-nestjs-server/builds/default/decorators/headers-decorator.ts index 2c2a16636685..0648f28feba1 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/default/decorators/headers-decorator.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/default/decorators/headers-decorator.ts @@ -12,5 +12,5 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; */ export const Headers = createParamDecorator((data: string, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); -return data ? request.headers?.[data] : request.headers; +return data ? request.headers?.[data.toLowerCase()] : request.headers; }); \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/headers-decorator.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/headers-decorator.ts index 2c2a16636685..0648f28feba1 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/headers-decorator.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/decorators/headers-decorator.ts @@ -12,5 +12,5 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; */ export const Headers = createParamDecorator((data: string, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); -return data ? request.headers?.[data] : request.headers; +return data ? request.headers?.[data.toLowerCase()] : request.headers; }); \ No newline at end of file From 57ec23f9ddd59aa0966cf4233498962f6634e9d0 Mon Sep 17 00:00:00 2001 From: Ary Obenholzner Date: Sat, 14 Feb 2026 00:33:02 +0100 Subject: [PATCH 7/7] [typescript-nestjs-server] #22928 allow optional parameters for number parse pipes --- .../main/resources/typescript-nestjs-server/paramPipe.mustache | 2 +- .../builds/parameters/controllers/DefaultApi.controller.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/paramPipe.mustache b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/paramPipe.mustache index 5082146db2fe..9803a8fd8dce 100644 --- a/modules/openapi-generator/src/main/resources/typescript-nestjs-server/paramPipe.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-nestjs-server/paramPipe.mustache @@ -1 +1 @@ -{{#defaultValue}}, new DefaultValuePipe({{{defaultValue}}}){{/defaultValue}}{{#isNumber}}, {{#isFloat}}ParseFloatPipe{{/isFloat}}{{^isFloat}}ParseIntPipe{{/isFloat}}{{/isNumber}} \ No newline at end of file +{{#defaultValue}}, new DefaultValuePipe({{{defaultValue}}}){{/defaultValue}}{{#isNumber}}, new {{#isFloat}}ParseFloatPipe({{/isFloat}}{{^isFloat}}ParseIntPipe({{/isFloat}}{{^isRequired}}{optional: true}{{/isRequired}}{{#isNullable}}{optional: true}{{/isNullable}}){{/isNumber}} \ No newline at end of file diff --git a/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/DefaultApi.controller.ts b/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/DefaultApi.controller.ts index 18daae527d6c..da8e5fc3fba6 100644 --- a/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/DefaultApi.controller.ts +++ b/samples/server/petstore/typescript-nestjs-server/builds/parameters/controllers/DefaultApi.controller.ts @@ -8,7 +8,7 @@ export class DefaultApiController { constructor(private readonly defaultApi: DefaultApi) {} @Get('/test/parameters/:path_default/:path_nullable') - findPetsByStatus(@Param('path_default') pathDefault: string, @Param('path_nullable') pathNullable: string, @Query('query_default', new DefaultValuePipe('available')) queryDefault: string | undefined, @Query('query_default_enum', new DefaultValuePipe('B')) queryDefaultEnum: 'A' | 'B' | 'C' | undefined, @Query('query_default_int', new DefaultValuePipe(3), ParseIntPipe) queryDefaultInt: number | undefined, @Headers('header_default', new DefaultValuePipe('available')) headerDefault: string | undefined, @Headers('header_default_enum', new DefaultValuePipe('B')) headerDefaultEnum: 'A' | 'B' | 'C' | undefined, @Headers('header_default_int', new DefaultValuePipe(3), ParseIntPipe) headerDefaultInt: number | undefined, @Cookies('cookie_default', new DefaultValuePipe('available')) cookieDefault: string | undefined, @Cookies('cookie_default_enum', new DefaultValuePipe('B')) cookieDefaultEnum: 'A' | 'B' | 'C' | undefined, @Cookies('cookie_default_int', new DefaultValuePipe(3), ParseIntPipe) cookieDefaultInt: number | undefined, @Query('query_nullable') queryNullable: string | null | undefined, @Headers('header_nullable') headerNullable: string | null | undefined, @Cookies('cookie_nullable') cookieNullable: string | null | undefined, @Query('$query-$dollar-sign') $query$dollarSign: string | undefined, @Req() request: Request): void | Promise | Observable { + findPetsByStatus(@Param('path_default') pathDefault: string, @Param('path_nullable') pathNullable: string, @Query('query_default', new DefaultValuePipe('available')) queryDefault: string | undefined, @Query('query_default_enum', new DefaultValuePipe('B')) queryDefaultEnum: 'A' | 'B' | 'C' | undefined, @Query('query_default_int', new DefaultValuePipe(3), new ParseIntPipe({optional: true})) queryDefaultInt: number | undefined, @Headers('header_default', new DefaultValuePipe('available')) headerDefault: string | undefined, @Headers('header_default_enum', new DefaultValuePipe('B')) headerDefaultEnum: 'A' | 'B' | 'C' | undefined, @Headers('header_default_int', new DefaultValuePipe(3), new ParseIntPipe({optional: true})) headerDefaultInt: number | undefined, @Cookies('cookie_default', new DefaultValuePipe('available')) cookieDefault: string | undefined, @Cookies('cookie_default_enum', new DefaultValuePipe('B')) cookieDefaultEnum: 'A' | 'B' | 'C' | undefined, @Cookies('cookie_default_int', new DefaultValuePipe(3), new ParseIntPipe({optional: true})) cookieDefaultInt: number | undefined, @Query('query_nullable') queryNullable: string | null | undefined, @Headers('header_nullable') headerNullable: string | null | undefined, @Cookies('cookie_nullable') cookieNullable: string | null | undefined, @Query('$query-$dollar-sign') $query$dollarSign: string | undefined, @Req() request: Request): void | Promise | Observable { return this.defaultApi.findPetsByStatus({ pathDefault, pathNullable, queryDefault, queryDefaultEnum, queryDefaultInt, headerDefault, headerDefaultEnum, headerDefaultInt, cookieDefault, cookieDefaultEnum, cookieDefaultInt, queryNullable, headerNullable, cookieNullable, $query$dollarSign, }, request); }