Skip to content

Commit

Permalink
fix: Don't URI encode schema keys when using $ref
Browse files Browse the repository at this point in the history
This fixes an isssue where using `$` in type names leads to invalid
OpenAPI specs. This would happen because the `refName` property that is
used as the object key for refs and to create `$ref` attributes is URI
encoded on creation, when it only needs to be URI encoded when used in
a `$ref` attribute.

Fixes #1461

Signed-off-by: Lucian Buzzo <[email protected]>
  • Loading branch information
LucianBuzzo committed Aug 17, 2023
1 parent 4f452f9 commit 38c14f1
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 20 deletions.
4 changes: 2 additions & 2 deletions packages/cli/src/metadataGeneration/metadataGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class MetadataGenerator {
}

private setProgramToDynamicControllersFiles(controllers: string[], esm: boolean) {
const allGlobFiles = importClassesFromDirectories(controllers, esm ? ['.mts', '.ts', '.cts']: ['.ts']);
const allGlobFiles = importClassesFromDirectories(controllers, esm ? ['.mts', '.ts', '.cts'] : ['.ts']);
if (allGlobFiles.length === 0) {
throw new GenerateMetadataError(`[${controllers.join(', ')}] globs found 0 controllers.`);
}
Expand Down Expand Up @@ -216,7 +216,7 @@ export class MetadataGenerator {
if (!referenceType.refName) {
return;
}
this.referenceTypeMap[referenceType.refName] = referenceType;
this.referenceTypeMap[decodeURIComponent(referenceType.refName)] = referenceType;
}

public GetReferenceType(refName: string) {
Expand Down
28 changes: 13 additions & 15 deletions packages/cli/src/metadataGeneration/typeResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -763,21 +763,19 @@ export class TypeResolver {
}

private getRefTypeName(name: string): string {
return encodeURIComponent(
name
.replace(/<|>/g, '_')
.replace(/\s+/g, '')
.replace(/,/g, '.')
.replace(/'([^']*)'/g, '$1')
.replace(/"([^"]*)"/g, '$1')
.replace(/&/g, '-and-')
.replace(/\|/g, '-or-')
.replace(/\[\]/g, '-Array')
.replace(/{|}/g, '_') // SuccessResponse_{indexesCreated-number}_ -> SuccessResponse__indexesCreated-number__
.replace(/([a-z]+):([a-z]+)/gi, '$1-$2') // SuccessResponse_indexesCreated:number_ -> SuccessResponse_indexesCreated-number_
.replace(/;/g, '--')
.replace(/([a-z]+)\[([a-z]+)\]/gi, '$1-at-$2'), // Partial_SerializedDatasourceWithVersion[format]_ -> Partial_SerializedDatasourceWithVersion~format~_,
);
return name
.replace(/<|>/g, '_')
.replace(/\s+/g, '')
.replace(/,/g, '.')
.replace(/'([^']*)'/g, '$1')
.replace(/"([^"]*)"/g, '$1')
.replace(/&/g, '-and-')
.replace(/\|/g, '-or-')
.replace(/\[\]/g, '-Array')
.replace(/{|}/g, '_') // SuccessResponse_{indexesCreated-number}_ -> SuccessResponse__indexesCreated-number__
.replace(/([a-z]+):([a-z]+)/gi, '$1-$2') // SuccessResponse_indexesCreated:number_ -> SuccessResponse_indexesCreated-number_
.replace(/;/g, '--')
.replace(/([a-z]+)\[([a-z]+)\]/gi, '$1-at-$2'); // Partial_SerializedDatasourceWithVersion[format]_ -> Partial_SerializedDatasourceWithVersion~format~_,
}

private attemptToResolveKindToPrimitive = (syntaxKind: ts.SyntaxKind): ResolvesToPrimitive | DoesNotResolveToPrimitive => {
Expand Down
7 changes: 4 additions & 3 deletions packages/cli/src/swagger/specGenerator2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,18 @@ export class SpecGenerator2 extends SpecGenerator {
const definitions: { [definitionsName: string]: Swagger.Schema2 } = {};
Object.keys(this.metadata.referenceTypeMap).map(typeName => {
const referenceType = this.metadata.referenceTypeMap[typeName];
const decodedName = decodeURIComponent(referenceType.refName);
if (referenceType.dataType === 'refObject') {
const required = referenceType.properties.filter(p => this.isRequiredWithoutDefault(p) && !this.hasUndefined(p)).map(p => p.name);
definitions[referenceType.refName] = {
definitions[decodedName] = {
description: referenceType.description,
properties: this.buildProperties(referenceType.properties),
required: required && required.length > 0 ? Array.from(new Set(required)) : undefined,
type: 'object',
};

if (referenceType.additionalProperties) {
definitions[referenceType.refName].additionalProperties = this.buildAdditionalProperties(referenceType.additionalProperties);
definitions[decodedName].additionalProperties = this.buildAdditionalProperties(referenceType.additionalProperties);
} else {
// Since additionalProperties was not explicitly set in the TypeScript interface for this model
// ...we need to make a decision
Expand Down Expand Up @@ -478,7 +479,7 @@ export class SpecGenerator2 extends SpecGenerator {
}

protected getSwaggerTypeForReferenceType(referenceType: Tsoa.ReferenceType): Swagger.BaseSchema {
return { $ref: `#/definitions/${referenceType.refName}` };
return { $ref: `#/definitions/${encodeURIComponent(referenceType.refName)}` };
}

private decideEnumType(anEnum: Array<string | number>, nameOfEnum: string): 'string' | 'number' {
Expand Down

0 comments on commit 38c14f1

Please sign in to comment.