diff --git a/packages/cli/generators/openapi/schema-helper.js b/packages/cli/generators/openapi/schema-helper.js index 3275d646780b..a402932a8492 100644 --- a/packages/cli/generators/openapi/schema-helper.js +++ b/packages/cli/generators/openapi/schema-helper.js @@ -9,7 +9,7 @@ const { isExtension, titleCase, kebabCase, - escapeIdentifier, + escapePropertyName, toJsonStr, } = require('./utils'); @@ -159,12 +159,12 @@ function mapObjectType(schema, options) { }), ); // The property name might have chars such as `-` - const propName = escapeIdentifier(p); + const propName = escapePropertyName(p); - let propDecoration = `@property({name: '${p}'})`; + let propDecoration = `@property()`; if (required.includes(p)) { - propDecoration = `@property({name: '${p}', required: true})`; + propDecoration = `@property({required: true})`; } if (propertyType.itemType) { @@ -177,7 +177,7 @@ function mapObjectType(schema, options) { getJSType(propertyType.itemType.name); if (itemType) { // Use `@property.array` for array types - propDecoration = `@property.array(${itemType}, {name: '${p}'})`; + propDecoration = `@property.array(${itemType})`; if (propertyType.itemType.className) { // The referenced item type is either a class or type collectImports(typeSpec, propertyType.itemType); diff --git a/packages/cli/generators/openapi/utils.js b/packages/cli/generators/openapi/utils.js index 055129dfc7da..f5fcb01f069f 100644 --- a/packages/cli/generators/openapi/utils.js +++ b/packages/cli/generators/openapi/utils.js @@ -151,6 +151,17 @@ function escapeIdentifier(name) { return name; } +/** + * Escape the property if it's not a valid JavaScript identifer + * @param {string} name + */ +function escapePropertyName(name) { + if (JS_KEYWORDS.includes(name) || !name.match(SAFE_IDENTIFER)) { + return toJsonStr(name); + } + return name; +} + function toJsonStr(val) { return json5.stringify(val, null, 2); } @@ -163,6 +174,7 @@ module.exports = { kebabCase: utils.kebabCase, camelCase: _.camelCase, escapeIdentifier, + escapePropertyName, toJsonStr, validateUrlOrFile, }; diff --git a/packages/cli/test/integration/generators/openapi-petstore.integration.js b/packages/cli/test/integration/generators/openapi-petstore.integration.js index dc1bda1afd87..ed1015e28cd8 100644 --- a/packages/cli/test/integration/generators/openapi-petstore.integration.js +++ b/packages/cli/test/integration/generators/openapi-petstore.integration.js @@ -71,12 +71,9 @@ describe('openapi-generator specific files', function() { assert.file(newPetModel); assert.fileContent(newPetModel, `export class NewPet {`); assert.fileContent(newPetModel, `@model({name: 'NewPet'})`); - assert.fileContent( - newPetModel, - `@property({name: 'name', required: true})`, - ); + assert.fileContent(newPetModel, `@property({required: true})`); assert.fileContent(newPetModel, `name: string;`); - assert.fileContent(newPetModel, `@property({name: 'tag'})`); + assert.fileContent(newPetModel, `@property()`); assert.fileContent(newPetModel, `tag?: string`); assert.file(errorModel); }); diff --git a/packages/cli/test/integration/generators/swagger-petstore.integration.js b/packages/cli/test/integration/generators/swagger-petstore.integration.js index f839b9639d69..ad8d1512eb5e 100644 --- a/packages/cli/test/integration/generators/swagger-petstore.integration.js +++ b/packages/cli/test/integration/generators/swagger-petstore.integration.js @@ -69,12 +69,9 @@ describe('openapi-generator specific files', () => { assert.fileContent(newPetModel, `export class NewPet {`); assert.fileContent(newPetModel, `constructor(data?: Partial) {`); assert.fileContent(newPetModel, `@model({name: 'NewPet'})`); - assert.fileContent( - newPetModel, - `@property({name: 'name', required: true})`, - ); + assert.fileContent(newPetModel, `@property({required: true})`); assert.fileContent(newPetModel, `name: string;`); - assert.fileContent(newPetModel, `@property({name: 'tag'})`); + assert.fileContent(newPetModel, `@property()`); assert.fileContent(newPetModel, `tag?: string`); assert.file(errorModel); }); diff --git a/packages/cli/test/unit/openapi/openapi-utils.unit.js b/packages/cli/test/unit/openapi/openapi-utils.unit.js index c30ef6e0b6cd..64444e11f64a 100644 --- a/packages/cli/test/unit/openapi/openapi-utils.unit.js +++ b/packages/cli/test/unit/openapi/openapi-utils.unit.js @@ -22,4 +22,17 @@ describe('openapi utils', () => { expect(utils.escapeIdentifier('fooBar')).to.eql('fooBar'); expect(utils.escapeIdentifier('Foobar')).to.eql('Foobar'); }); + + it('escapes property names with illegal chars', () => { + expect(utils.escapePropertyName('customer-id')).to.eql("'customer-id'"); + expect(utils.escapePropertyName('customer id')).to.eql("'customer id'"); + expect(utils.escapePropertyName('customer.id')).to.eql("'customer.id'"); + expect(utils.escapePropertyName('default')).to.eql("'default'"); + }); + + it('does not escape property names with legal chars', () => { + expect(utils.escapePropertyName('customerId')).to.eql('customerId'); + expect(utils.escapePropertyName('customer_id')).to.eql('customer_id'); + expect(utils.escapePropertyName('customerid')).to.eql('customerid'); + }); }); diff --git a/packages/cli/test/unit/openapi/schema-model.unit.js b/packages/cli/test/unit/openapi/schema-model.unit.js index 602a8150672e..9b7cbec4e3e1 100644 --- a/packages/cli/test/unit/openapi/schema-model.unit.js +++ b/packages/cli/test/unit/openapi/schema-model.unit.js @@ -50,14 +50,14 @@ describe('schema to model', () => { { name: 'total', signature: 'total?: number;', - decoration: "@property({name: 'total'})", + decoration: '@property()', }, { name: 'apis', signature: 'apis?: {\n apiKey?: string;\n apiVersionNumber?: string;\n' + ' apiUrl?: string;\n apiDocumentationUrl?: string;\n}[];', - decoration: "@property({name: 'apis'})", + decoration: '@property()', }, ], declaration: @@ -81,14 +81,14 @@ describe('schema to model', () => { { name: 'total', signature: 'total?: number;', - decoration: "@property({name: 'total'})", + decoration: '@property()', }, { name: 'apis', signature: 'apis?: {\n apiKey?: string;\n apiVersionNumber?: string;\n ' + 'apiUrl?: string;\n apiDocumentationUrl?: string;\n}[];', - decoration: "@property({name: 'apis'})", + decoration: '@property()', }, ], imports: [], @@ -109,7 +109,7 @@ describe('schema to model', () => { { name: 'criteria', signature: "criteria: string = '*:*';", - decoration: "@property({name: 'criteria', required: true})", + decoration: '@property({required: true})', description: 'Uses Lucene Query Syntax in the format of propertyName:value, ' + 'propertyName:[num1 TO num2] and date range format: ' + @@ -121,13 +121,13 @@ describe('schema to model', () => { { name: 'start', signature: 'start?: number = 0;', - decoration: "@property({name: 'start'})", + decoration: '@property()', description: 'Starting record number. Default value is 0.', }, { name: 'rows', signature: 'rows?: number = 100;', - decoration: "@property({name: 'rows'})", + decoration: '@property()', description: 'Specify number of rows to be returned. If you run the search ' + "with default values, in the response you will see 'numFound' " + @@ -188,12 +188,12 @@ describe('schema to model', () => { { name: 'name', signature: 'name: string;', - decoration: "@property({name: 'name', required: true})", + decoration: '@property({required: true})', }, { name: 'tag', signature: 'tag?: string;', - decoration: "@property({name: 'tag'})", + decoration: '@property()', }, ], imports: [], @@ -208,7 +208,7 @@ describe('schema to model', () => { { name: 'id', signature: 'id: number;', - decoration: "@property({name: 'id', required: true})", + decoration: '@property({required: true})', }, ], signature: '{\n id: number;\n}', @@ -228,12 +228,12 @@ describe('schema to model', () => { { name: 'name', signature: 'name: string;', - decoration: "@property({name: 'name', required: true})", + decoration: '@property({required: true})', }, { name: 'tag', signature: 'tag?: string;', - decoration: "@property({name: 'tag'})", + decoration: '@property()', }, ], imports: [], @@ -251,12 +251,12 @@ describe('schema to model', () => { { name: 'code', signature: 'code: number;', - decoration: "@property({name: 'code', required: true})", + decoration: '@property({required: true})', }, { name: 'message', signature: 'message: string;', - decoration: "@property({name: 'message', required: true})", + decoration: '@property({required: true})', }, ], imports: [], @@ -312,22 +312,22 @@ describe('schema to model', () => { { name: 'street', signature: 'street?: string;', - decoration: "@property({name: 'street'})", + decoration: '@property()', }, { name: 'city', signature: 'city?: string;', - decoration: "@property({name: 'city'})", + decoration: '@property()', }, { name: 'state', signature: 'state?: string;', - decoration: "@property({name: 'state'})", + decoration: '@property()', }, { name: 'zipCode', signature: 'zipCode?: string;', - decoration: "@property({name: 'zipCode'})", + decoration: '@property()', }, ], imports: [], @@ -347,22 +347,22 @@ describe('schema to model', () => { { name: 'street', signature: 'street?: string;', - decoration: "@property({name: 'street'})", + decoration: '@property()', }, { name: 'city', signature: 'city?: string;', - decoration: "@property({name: 'city'})", + decoration: '@property()', }, { name: 'state', signature: 'state?: string;', - decoration: "@property({name: 'state'})", + decoration: '@property()', }, { name: 'zipCode', signature: 'zipCode?: string;', - decoration: "@property({name: 'zipCode'})", + decoration: '@property()', }, ], imports: [], @@ -381,37 +381,37 @@ describe('schema to model', () => { { name: 'id', signature: 'id: number;', - decoration: "@property({name: 'id', required: true})", + decoration: '@property({required: true})', }, { name: 'first-name', - signature: 'firstName?: string;', - decoration: "@property({name: 'first-name'})", + signature: "'first-name'?: string;", + decoration: '@property()', }, { name: 'last-name', - signature: 'lastName?: Name;', - decoration: "@property({name: 'last-name'})", + signature: "'last-name'?: Name;", + decoration: '@property()', }, { name: 'profiles', signature: 'profiles?: ProfileId[];', - decoration: "@property.array(String, {name: 'profiles'})", + decoration: '@property.array(String)', }, { name: 'emails', signature: 'emails?: string[];', - decoration: "@property.array(String, {name: 'emails'})", + decoration: '@property.array(String)', }, { name: 'addresses', signature: 'addresses?: AddressList;', - decoration: "@property.array(Address, {name: 'addresses'})", + decoration: '@property.array(Address)', }, { name: 'us-office', - signature: 'usOffice?: Address;', - decoration: "@property({name: 'us-office'})", + signature: "'us-office'?: Address;", + decoration: '@property()', }, ], imports: [ @@ -423,9 +423,9 @@ describe('schema to model', () => { import: "import {Customer} from './customer.model';", kind: 'class', declaration: - '{\n id: number;\n firstName?: string;\n lastName?: Name;\n' + + "{\n id: number;\n 'first-name'?: string;\n 'last-name'?: Name;\n" + ' profiles?: ProfileId[];\n emails?: string[];\n' + - ' addresses?: AddressList;\n usOffice?: Address;\n}', + " addresses?: AddressList;\n 'us-office'?: Address;\n}", signature: 'Customer', }, ]);