Escaping regexp pattern properly

This commit is contained in:
Ferdi Koomen 2020-10-15 20:14:44 +02:00
parent 97f8204028
commit be5161a7c5
17 changed files with 109 additions and 16 deletions

View File

@ -7,6 +7,7 @@ import { getComment } from './getComment';
import { getEnum } from './getEnum';
import { getEnumFromDescription } from './getEnumFromDescription';
import { getModelProperties } from './getModelProperties';
import { getPattern } from './getPattern';
import { getType } from './getType';
export function getModel(openApi: OpenApi, definition: OpenApiSchema, isDefinition: boolean = false, name: string = ''): Model {
@ -30,12 +31,12 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, isDefiniti
multipleOf: definition.multipleOf,
maxLength: definition.maxLength,
minLength: definition.minLength,
pattern: definition.pattern,
maxItems: definition.maxItems,
minItems: definition.minItems,
uniqueItems: definition.uniqueItems,
maxProperties: definition.maxProperties,
minProperties: definition.minProperties,
pattern: getPattern(definition.pattern),
imports: [],
extends: [],
enum: [],

View File

@ -3,6 +3,7 @@ import type { OpenApi } from '../interfaces/OpenApi';
import type { OpenApiSchema } from '../interfaces/OpenApiSchema';
import { escapeName } from './escapeName';
import { getComment } from './getComment';
import { getPattern } from './getPattern';
import { getType } from './getType';
// Fix for circular dependency between getModel and getModelProperties
@ -36,12 +37,12 @@ export function getModelProperties(openApi: OpenApi, definition: OpenApiSchema,
multipleOf: property.multipleOf,
maxLength: property.maxLength,
minLength: property.minLength,
pattern: property.pattern,
maxItems: property.maxItems,
minItems: property.minItems,
uniqueItems: property.uniqueItems,
maxProperties: property.maxProperties,
minProperties: property.minProperties,
pattern: getPattern(property.pattern),
imports: model.imports,
extends: [],
enum: [],
@ -70,12 +71,12 @@ export function getModelProperties(openApi: OpenApi, definition: OpenApiSchema,
multipleOf: property.multipleOf,
maxLength: property.maxLength,
minLength: property.minLength,
pattern: property.pattern,
maxItems: property.maxItems,
minItems: property.minItems,
uniqueItems: property.uniqueItems,
maxProperties: property.maxProperties,
minProperties: property.minProperties,
pattern: getPattern(property.pattern),
imports: model.imports,
extends: model.extends,
enum: model.enum,

View File

@ -9,6 +9,7 @@ import { getEnumFromDescription } from './getEnumFromDescription';
import { getModel } from './getModel';
import { getOperationParameterDefault } from './getOperationParameterDefault';
import { getOperationParameterName } from './getOperationParameterName';
import { getPattern } from './getPattern';
import { getType } from './getType';
export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParameter): OperationParameter {
@ -34,10 +35,10 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame
multipleOf: parameter.multipleOf,
maxLength: parameter.maxLength,
minLength: parameter.minLength,
pattern: parameter.pattern,
maxItems: parameter.maxItems,
minItems: parameter.minItems,
uniqueItems: parameter.uniqueItems,
pattern: getPattern(parameter.pattern),
imports: [],
extends: [],
enum: [],

View File

@ -4,6 +4,7 @@ import type { OpenApiResponse } from '../interfaces/OpenApiResponse';
import { PrimaryType } from './constants';
import { getComment } from './getComment';
import { getModel } from './getModel';
import { getPattern } from './getPattern';
import { getType } from './getType';
export function getOperationResponse(openApi: OpenApi, response: OpenApiResponse, responseCode: number): OperationResponse {
@ -73,12 +74,12 @@ export function getOperationResponse(openApi: OpenApi, response: OpenApiResponse
operationResponse.multipleOf = model.multipleOf;
operationResponse.maxLength = model.maxLength;
operationResponse.minLength = model.minLength;
operationResponse.pattern = model.pattern;
operationResponse.maxItems = model.maxItems;
operationResponse.minItems = model.minItems;
operationResponse.uniqueItems = model.uniqueItems;
operationResponse.maxProperties = model.maxProperties;
operationResponse.minProperties = model.minProperties;
operationResponse.pattern = getPattern(model.pattern);
operationResponse.imports.push(...model.imports);
operationResponse.extends.push(...model.extends);
operationResponse.enum.push(...model.enum);

View File

@ -0,0 +1,14 @@
import { getPattern } from './getPattern';
describe('getPattern', () => {
it('should produce correct result', () => {
expect(getPattern()).toEqual(undefined);
expect(getPattern('')).toEqual('');
expect(getPattern('^[a-zA-Z]')).toEqual('^[a-zA-Z]');
expect(getPattern('^\\w+$')).toEqual('^\\\\w+$');
expect(getPattern('^\\d{3}-\\d{2}-\\d{4}$')).toEqual('^\\\\d{3}-\\\\d{2}-\\\\d{4}$');
expect(getPattern('\\')).toEqual('\\\\');
expect(getPattern('\\/')).toEqual('\\\\/');
expect(getPattern('\\/\\/')).toEqual('\\\\/\\\\/');
});
});

View File

@ -0,0 +1,10 @@
/**
* The spec generates a pattern like this '^\d{3}-\d{2}-\d{4}$'
* However, to use it in HTML or inside new RegExp() we need to
* escape the pattern to become: '^\\d{3}-\\d{2}-\\d{4}$' in order
* to make it a valid regexp string.
* @param pattern
*/
export function getPattern(pattern?: string): string | undefined {
return pattern?.replace(/\\/g, '\\\\');
}

View File

@ -8,6 +8,7 @@ import { getEnum } from './getEnum';
import { getEnumFromDescription } from './getEnumFromDescription';
import { getModelDefault } from './getModelDefault';
import { getModelProperties } from './getModelProperties';
import { getPattern } from './getPattern';
import { getType } from './getType';
export function getModel(openApi: OpenApi, definition: OpenApiSchema, isDefinition: boolean = false, name: string = ''): Model {
@ -31,12 +32,12 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, isDefiniti
multipleOf: definition.multipleOf,
maxLength: definition.maxLength,
minLength: definition.minLength,
pattern: definition.pattern,
maxItems: definition.maxItems,
minItems: definition.minItems,
uniqueItems: definition.uniqueItems,
maxProperties: definition.maxProperties,
minProperties: definition.minProperties,
pattern: getPattern(definition.pattern),
imports: [],
extends: [],
enum: [],

View File

@ -3,6 +3,7 @@ import type { OpenApi } from '../interfaces/OpenApi';
import type { OpenApiSchema } from '../interfaces/OpenApiSchema';
import { escapeName } from './escapeName';
import { getComment } from './getComment';
import { getPattern } from './getPattern';
import { getType } from './getType';
// Fix for circular dependency between getModel and getModelProperties
@ -36,12 +37,12 @@ export function getModelProperties(openApi: OpenApi, definition: OpenApiSchema,
multipleOf: property.multipleOf,
maxLength: property.maxLength,
minLength: property.minLength,
pattern: property.pattern,
maxItems: property.maxItems,
minItems: property.minItems,
uniqueItems: property.uniqueItems,
maxProperties: property.maxProperties,
minProperties: property.minProperties,
pattern: getPattern(property.pattern),
imports: model.imports,
extends: [],
enum: [],
@ -70,12 +71,12 @@ export function getModelProperties(openApi: OpenApi, definition: OpenApiSchema,
multipleOf: property.multipleOf,
maxLength: property.maxLength,
minLength: property.minLength,
pattern: property.pattern,
maxItems: property.maxItems,
minItems: property.minItems,
uniqueItems: property.uniqueItems,
maxProperties: property.maxProperties,
minProperties: property.minProperties,
pattern: getPattern(property.pattern),
imports: model.imports,
extends: model.extends,
enum: model.enum,

View File

@ -6,6 +6,7 @@ import { getComment } from './getComment';
import { getModel } from './getModel';
import { getModelDefault } from './getModelDefault';
import { getOperationParameterName } from './getOperationParameterName';
import { getPattern } from './getPattern';
import { getType } from './getType';
export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParameter): OperationParameter {
@ -68,12 +69,12 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame
operationParameter.multipleOf = model.multipleOf;
operationParameter.maxLength = model.maxLength;
operationParameter.minLength = model.minLength;
operationParameter.pattern = model.pattern;
operationParameter.maxItems = model.maxItems;
operationParameter.minItems = model.minItems;
operationParameter.uniqueItems = model.uniqueItems;
operationParameter.maxProperties = model.maxProperties;
operationParameter.minProperties = model.minProperties;
operationParameter.pattern = getPattern(model.pattern);
operationParameter.default = model.default;
operationParameter.imports.push(...model.imports);
operationParameter.extends.push(...model.extends);

View File

@ -5,6 +5,7 @@ import { PrimaryType } from './constants';
import { getComment } from './getComment';
import { getContent } from './getContent';
import { getModel } from './getModel';
import { getPattern } from './getPattern';
import { getType } from './getType';
export function getOperationRequestBody(openApi: OpenApi, parameter: OpenApiRequestBody): OperationParameter {
@ -59,12 +60,12 @@ export function getOperationRequestBody(openApi: OpenApi, parameter: OpenApiRequ
requestBody.multipleOf = model.multipleOf;
requestBody.maxLength = model.maxLength;
requestBody.minLength = model.minLength;
requestBody.pattern = model.pattern;
requestBody.maxItems = model.maxItems;
requestBody.minItems = model.minItems;
requestBody.uniqueItems = model.uniqueItems;
requestBody.maxProperties = model.maxProperties;
requestBody.minProperties = model.minProperties;
requestBody.pattern = getPattern(model.pattern);
requestBody.imports.push(...model.imports);
requestBody.extends.push(...model.extends);
requestBody.enum.push(...model.enum);

View File

@ -5,6 +5,7 @@ import { PrimaryType } from './constants';
import { getComment } from './getComment';
import { getContent } from './getContent';
import { getModel } from './getModel';
import { getPattern } from './getPattern';
import { getType } from './getType';
export function getOperationResponse(openApi: OpenApi, response: OpenApiResponse, responseCode: number): OperationResponse {
@ -72,12 +73,12 @@ export function getOperationResponse(openApi: OpenApi, response: OpenApiResponse
operationResponse.multipleOf = model.multipleOf;
operationResponse.maxLength = model.maxLength;
operationResponse.minLength = model.minLength;
operationResponse.pattern = model.pattern;
operationResponse.maxItems = model.maxItems;
operationResponse.minItems = model.minItems;
operationResponse.uniqueItems = model.uniqueItems;
operationResponse.maxProperties = model.maxProperties;
operationResponse.minProperties = model.minProperties;
operationResponse.pattern = getPattern(model.pattern);
operationResponse.imports.push(...model.imports);
operationResponse.extends.push(...model.extends);
operationResponse.enum.push(...model.enum);

View File

@ -0,0 +1,14 @@
import { getPattern } from './getPattern';
describe('getPattern', () => {
it('should produce correct result', () => {
expect(getPattern()).toEqual(undefined);
expect(getPattern('')).toEqual('');
expect(getPattern('^[a-zA-Z]')).toEqual('^[a-zA-Z]');
expect(getPattern('^\\w+$')).toEqual('^\\\\w+$');
expect(getPattern('^\\d{3}-\\d{2}-\\d{4}$')).toEqual('^\\\\d{3}-\\\\d{2}-\\\\d{4}$');
expect(getPattern('\\')).toEqual('\\\\');
expect(getPattern('\\/')).toEqual('\\\\/');
expect(getPattern('\\/\\/')).toEqual('\\\\/\\\\/');
});
});

View File

@ -0,0 +1,10 @@
/**
* The spec generates a pattern like this '^\d{3}-\d{2}-\d{4}$'
* However, to use it in HTML or inside new RegExp() we need to
* escape the pattern to become: '^\\d{3}-\\d{2}-\\d{4}$' in order
* to make it a valid regexp string.
* @param pattern
*/
export function getPattern(pattern?: string): string | undefined {
return pattern?.replace(/\\/g, '\\\\');
}

View File

@ -36,7 +36,7 @@
minLength: {{{minLength}}},
{{/if}}
{{#if pattern}}
pattern: /{{{pattern}}}/,
pattern: '{{{pattern}}}',
{{/if}}
{{#if maxItems}}
maxItems: {{{maxItems}}},

View File

@ -883,6 +883,8 @@ export interface ModelWithPattern {
name: string;
readonly enabled?: boolean;
readonly modified?: string;
id?: string;
text?: string;
}
"
`;
@ -1525,7 +1527,7 @@ export const $ModelWithPattern = {
type: 'string',
isRequired: true,
maxLength: 64,
pattern: /^[a-zA-Z0-9_]*$/,
pattern: '^[a-zA-Z0-9_]*$',
},
name: {
type: 'string',
@ -1541,6 +1543,14 @@ export const $ModelWithPattern = {
isReadOnly: true,
format: 'date-time',
},
id: {
type: 'string',
pattern: '^\\\\\\\\d{2}-\\\\\\\\d{3}-\\\\\\\\d{4}$',
},
text: {
type: 'string',
pattern: '^\\\\\\\\w+$',
},
},
};"
`;
@ -1671,7 +1681,7 @@ exports[`v2 should generate: ./test/generated/v2/schemas/$SimpleStringWithPatter
export const $SimpleStringWithPattern = {
type: 'string',
maxLength: 64,
pattern: /^[a-zA-Z0-9_]*$/,
pattern: '^[a-zA-Z0-9_]*$',
};"
`;
@ -3105,6 +3115,8 @@ export interface ModelWithPattern {
name: string;
readonly enabled?: boolean;
readonly modified?: string;
id?: string;
text?: string;
}
"
`;
@ -3758,7 +3770,7 @@ export const $ModelWithPattern = {
type: 'string',
isRequired: true,
maxLength: 64,
pattern: /^[a-zA-Z0-9_]*$/,
pattern: '^[a-zA-Z0-9_]*$',
},
name: {
type: 'string',
@ -3774,6 +3786,14 @@ export const $ModelWithPattern = {
isReadOnly: true,
format: 'date-time',
},
id: {
type: 'string',
pattern: '^\\\\\\\\d{2}-\\\\\\\\d{3}-\\\\\\\\d{4}$',
},
text: {
type: 'string',
pattern: '^\\\\\\\\w+$',
},
},
};"
`;
@ -3910,7 +3930,7 @@ export const $SimpleStringWithPattern = {
type: 'string',
isNullable: true,
maxLength: 64,
pattern: /^[a-zA-Z0-9_]*$/,
pattern: '^[a-zA-Z0-9_]*$',
};"
`;

View File

@ -1234,6 +1234,14 @@
"type": "string",
"format": "date-time",
"readOnly": true
},
"id": {
"type": "string",
"pattern": "^\\d{2}-\\d{3}-\\d{4}$"
},
"text": {
"type": "string",
"pattern": "^\\w+$"
}
}
}

View File

@ -1751,6 +1751,14 @@
"type": "string",
"format": "date-time",
"readOnly": true
},
"id": {
"type": "string",
"pattern": "^\\d{2}-\\d{3}-\\d{4}$"
},
"text": {
"type": "string",
"pattern": "^\\w+$"
}
}
}