From 309b3a2d964cdcee2092faa2736a5f230d29f4cb Mon Sep 17 00:00:00 2001 From: Amar Zavery Date: Thu, 3 Mar 2016 16:03:49 -0800 Subject: [PATCH] Improved NodeJS support for uuid --- .../BodyArray/operations/arrayModel.js | 10 ++++---- .../NodeJS/NodeJS/ClientModelExtensions.cs | 19 ++++++++++++++- .../NodeJS/NodeJS/NodeJsCodeNamer.cs | 2 +- .../operations/array.py | 9 +++++--- ClientRuntimes/NodeJS/ms-rest/README.md | 2 +- ClientRuntimes/NodeJS/ms-rest/lib/msRest.js | 1 + .../NodeJS/ms-rest/lib/serialization.js | 15 ++++++++---- ClientRuntimes/NodeJS/ms-rest/lib/utils.js | 12 ++++++++++ ClientRuntimes/NodeJS/ms-rest/package.json | 3 ++- .../NodeJS/ms-rest/test/serializationTests.js | 23 +++++++++++++++++++ 10 files changed, 79 insertions(+), 17 deletions(-) diff --git a/AutoRest/Generators/NodeJS/NodeJS.Tests/Expected/AcceptanceTests/BodyArray/operations/arrayModel.js b/AutoRest/Generators/NodeJS/NodeJS.Tests/Expected/AcceptanceTests/BodyArray/operations/arrayModel.js index be5bfe462b..b3d21adb28 100644 --- a/AutoRest/Generators/NodeJS/NodeJS.Tests/Expected/AcceptanceTests/BodyArray/operations/arrayModel.js +++ b/AutoRest/Generators/NodeJS/NodeJS.Tests/Expected/AcceptanceTests/BodyArray/operations/arrayModel.js @@ -3603,7 +3603,7 @@ ArrayModel.prototype.getUuidValid = function (options, callback) { name: 'Sequence', element: { required: false, - serializedName: 'StringElementType', + serializedName: 'UuidElementType', type: { name: 'String' } @@ -3663,8 +3663,8 @@ ArrayModel.prototype.putUuidValid = function (arrayBody, options, callback) { throw new Error('arrayBody cannot be null or undefined and it must be of type array.'); } for (var i = 0; i < arrayBody.length; i++) { - if (arrayBody[i] !== null && arrayBody[i] !== undefined && typeof arrayBody[i].valueOf() !== 'string') { - throw new Error('arrayBody[i] must be of type string.'); + if (arrayBody[i] !== null && arrayBody[i] !== undefined && !(typeof arrayBody[i].valueOf() === 'string' && msRest.isValidUuid(arrayBody[i]))) { + throw new Error('arrayBody[i] must be of type string and must be a valid uuid.'); } } } catch (error) { @@ -3704,7 +3704,7 @@ ArrayModel.prototype.putUuidValid = function (arrayBody, options, callback) { name: 'Sequence', element: { required: false, - serializedName: 'StringElementType', + serializedName: 'UuidElementType', type: { name: 'String' } @@ -3859,7 +3859,7 @@ ArrayModel.prototype.getUuidInvalidChars = function (options, callback) { name: 'Sequence', element: { required: false, - serializedName: 'StringElementType', + serializedName: 'UuidElementType', type: { name: 'String' } diff --git a/AutoRest/Generators/NodeJS/NodeJS/ClientModelExtensions.cs b/AutoRest/Generators/NodeJS/NodeJS/ClientModelExtensions.cs index c13a8cb514..2b5e72bc5d 100644 --- a/AutoRest/Generators/NodeJS/NodeJS/ClientModelExtensions.cs +++ b/AutoRest/Generators/NodeJS/NodeJS/ClientModelExtensions.cs @@ -211,7 +211,7 @@ private static string ValidatePrimaryType(this PrimaryType primary, IScopeProvid builder.AppendLine("if ({0} !== null && {0} !== undefined && typeof {0}.valueOf() !== '{1}') {{", valueReference, lowercaseTypeName); return ConstructValidationCheck(builder, typeErrorMessage, valueReference, primary.Name).ToString(); } - else if (primary.Type == KnownPrimaryType.String || primary.Type == KnownPrimaryType.Uuid) //treat Uuid as strings in NodeJS + else if (primary.Type == KnownPrimaryType.String) { if (isRequired) { @@ -223,6 +223,19 @@ private static string ValidatePrimaryType(this PrimaryType primary, IScopeProvid builder.AppendLine("if ({0} !== null && {0} !== undefined && typeof {0}.valueOf() !== '{1}') {{", valueReference, lowercaseTypeName); return ConstructValidationCheck(builder, typeErrorMessage, valueReference, primary.Name).ToString(); } + else if (primary.Type == KnownPrimaryType.Uuid) + { + if (isRequired) + { + requiredTypeErrorMessage = "throw new Error('{0} cannot be null or undefined and it must be of type string and must be a valid {1}.');"; + //empty string can be a valid value hence we cannot implement the simple check if (!{0}) + builder.AppendLine("if ({0} === null || {0} === undefined || typeof {0}.valueOf() !== 'string' || !msRest.isValidUuid({0})) {{", valueReference); + return ConstructValidationCheck(builder, requiredTypeErrorMessage, valueReference, primary.Name).ToString(); + } + typeErrorMessage = "throw new Error('{0} must be of type string and must be a valid {1}.');"; + builder.AppendLine("if ({0} !== null && {0} !== undefined && !(typeof {0}.valueOf() === 'string' && msRest.isValidUuid({0}))) {{", valueReference); + return ConstructValidationCheck(builder, typeErrorMessage, valueReference, primary.Name).ToString(); + } else if (primary.Type == KnownPrimaryType.ByteArray) { if (isRequired) @@ -735,6 +748,10 @@ public static string ConstructMapper(this IType type, string serializedName, IPa { builder.AppendLine("type: {").Indent().AppendLine("name: 'String'").Outdent().AppendLine("}"); } + else if (primary.Type == KnownPrimaryType.Uuid) + { + builder.AppendLine("type: {").Indent().AppendLine("name: 'Uuid'").Outdent().AppendLine("}"); + } else if (primary.Type == KnownPrimaryType.ByteArray) { builder.AppendLine("type: {").Indent().AppendLine("name: 'ByteArray'").Outdent().AppendLine("}"); diff --git a/AutoRest/Generators/NodeJS/NodeJS/NodeJsCodeNamer.cs b/AutoRest/Generators/NodeJS/NodeJS/NodeJsCodeNamer.cs index 99f8cbe059..bb6a9a7c87 100644 --- a/AutoRest/Generators/NodeJS/NodeJS/NodeJsCodeNamer.cs +++ b/AutoRest/Generators/NodeJS/NodeJS/NodeJsCodeNamer.cs @@ -331,7 +331,7 @@ private static IType NormalizePrimaryType(PrimaryType primaryType) } else if (primaryType.Type == KnownPrimaryType.Uuid) { - primaryType.Name = "String"; + primaryType.Name = "Uuid"; } else if (primaryType.Type == KnownPrimaryType.Object) { diff --git a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyArray/autorestswaggerbatarrayservice/operations/array.py b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyArray/autorestswaggerbatarrayservice/operations/array.py index 663a3757aa..66f47322ee 100644 --- a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyArray/autorestswaggerbatarrayservice/operations/array.py +++ b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyArray/autorestswaggerbatarrayservice/operations/array.py @@ -1182,7 +1182,8 @@ def get_uuid_valid( :param dict custom_headers: headers that will be added to the request :param boolean raw: returns the direct response alongside the deserialized response - :rtype: list or msrest.pipeline.ClientRawResponse + :rtype: list + :rtype: msrest.pipeline.ClientRawResponse if raw=True """ # Construct URL url = '/array/prim/uuid/valid' @@ -1226,7 +1227,8 @@ def put_uuid_valid( :param dict custom_headers: headers that will be added to the request :param boolean raw: returns the direct response alongside the deserialized response - :rtype: None or msrest.pipeline.ClientRawResponse + :rtype: None + :rtype: msrest.pipeline.ClientRawResponse if raw=True """ # Construct URL url = '/array/prim/uuid/valid' @@ -1263,7 +1265,8 @@ def get_uuid_invalid_chars( :param dict custom_headers: headers that will be added to the request :param boolean raw: returns the direct response alongside the deserialized response - :rtype: list or msrest.pipeline.ClientRawResponse + :rtype: list + :rtype: msrest.pipeline.ClientRawResponse if raw=True """ # Construct URL url = '/array/prim/uuid/invalidchars' diff --git a/ClientRuntimes/NodeJS/ms-rest/README.md b/ClientRuntimes/NodeJS/ms-rest/README.md index 2de613038f..cb4aca73ab 100644 --- a/ClientRuntimes/NodeJS/ms-rest/README.md +++ b/ClientRuntimes/NodeJS/ms-rest/README.md @@ -18,7 +18,7 @@ var msrest = require('ms-rest'); ## Serialization/Deserialization Features - Type checking - - (String, Number, Boolean, ByteArray, Date, DateTime, Enum, TimeSpan, DateTimeRfc1123, Object, Stream, Sequence, Dictionary, Composite) + - (String, Number, Boolean, ByteArray, Date, DateTime, Enum, TimeSpan, DateTimeRfc1123, Object, Stream, Sequence, Dictionary, Composite, Uuid(as a string)) - Validation of specified constraints - ExclusiveMaximum, ExclusiveMinimum, InclusiveMaximum, InclusiveMinimum, MaxItems, MaxLength, MinItems, MinLength, MultipleOf, Pattern, UniqueItems - Flattening/Unflattening properties diff --git a/ClientRuntimes/NodeJS/ms-rest/lib/msRest.js b/ClientRuntimes/NodeJS/ms-rest/lib/msRest.js index 9f3d8e2e8f..89381393c4 100644 --- a/ClientRuntimes/NodeJS/ms-rest/lib/msRest.js +++ b/ClientRuntimes/NodeJS/ms-rest/lib/msRest.js @@ -21,6 +21,7 @@ exports.ExponentialRetryPolicyFilter = require('./filters/exponentialRetryPolicy exports.requestPipeline = require('./requestPipeline'); exports.stripResponse = utils.stripResponse; exports.stripRequest = utils.stripRequest; +exports.isValidUuid = utils.isValidUuid; //serialization exports.serializeObject = require('./serialization').serializeObject; diff --git a/ClientRuntimes/NodeJS/ms-rest/lib/serialization.js b/ClientRuntimes/NodeJS/ms-rest/lib/serialization.js index ddbea1743e..1968698f56 100644 --- a/ClientRuntimes/NodeJS/ms-rest/lib/serialization.js +++ b/ClientRuntimes/NodeJS/ms-rest/lib/serialization.js @@ -6,6 +6,7 @@ var util = require('util'); var moment = require('moment'); var stream = require('stream'); +var utils = require('./utils'); /** * Serializes the JSON Object. It serializes Buffer object to a @@ -69,7 +70,7 @@ exports.serialize = function (mapper, object, objectName) { if (mapper.isConstant) object = mapper.defaultValue; //Validate Constraints if any validateConstraints.call(this, mapper, object, objectName); - if (mapperType.match(/^(Number|String|Boolean|Object|Stream)$/ig) !== null) { + if (mapperType.match(/^(Number|String|Boolean|Object|Stream|Uuid)$/ig) !== null) { payload = serializeBasicTypes.call(this, mapperType, objectName, object); } else if (mapperType.match(/^Enum$/ig) !== null) { payload = serializeEnumType.call(this, objectName, mapper.type.allowedValues, object); @@ -283,15 +284,19 @@ function serializeBasicTypes(typeName, objectName, value) { if (value !== null && value !== undefined) { if (typeName.match(/^Number$/ig) !== null) { if (typeof value !== 'number') { - throw new Error(util.format('%s must be of type number.', objectName)); + throw new Error(util.format('%s with value %s must be of type number.', objectName, value)); } } else if (typeName.match(/^String$/ig) !== null) { if (typeof value.valueOf() !== 'string') { - throw new Error(util.format('%s must be of type string.', objectName)); + throw new Error(util.format('%s with value \'%s\' must be of type string.', objectName, value)); + } + } else if (typeName.match(/^Uuid$/ig) !== null) { + if (!(typeof value.valueOf() === 'string' && utils.isValidUuid(value))) { + throw new Error(util.format('%s with value \'%s\' must be of type string and a valid uuid.', objectName, value)); } } else if (typeName.match(/^Boolean$/ig) !== null) { if (typeof value !== 'boolean') { - throw new Error(util.format('%s must be of type boolean.', objectName)); + throw new Error(util.format('%s with value %s must be of type boolean.', objectName, value)); } } else if (typeName.match(/^Object$/ig) !== null) { if (typeof value !== 'object') { @@ -375,7 +380,7 @@ exports.deserialize = function (mapper, responseBody, objectName) { if (!objectName) objectName = mapper.serializedName; if (mapperType.match(/^Sequence$/ig) !== null) payload = []; - if (mapperType.match(/^(Number|String|Boolean|Enum|Object|Stream)$/ig) !== null) { + if (mapperType.match(/^(Number|String|Boolean|Enum|Object|Stream|Uuid)$/ig) !== null) { payload = responseBody; } else if (mapperType.match(/^(Date|DateTime|DateTimeRfc1123)$/ig) !== null) { payload = new Date(responseBody); diff --git a/ClientRuntimes/NodeJS/ms-rest/lib/utils.js b/ClientRuntimes/NodeJS/ms-rest/lib/utils.js index 278be21066..c285768fe3 100644 --- a/ClientRuntimes/NodeJS/ms-rest/lib/utils.js +++ b/ClientRuntimes/NodeJS/ms-rest/lib/utils.js @@ -81,4 +81,16 @@ exports.stripRequest = function (request) { return strippedRequest; }; +/** + * Validates the given uuid as a string + * + * @param {string} uuid - The uuid as a string that needs to be validated + * + * @return {boolean} result - True if the uuid is valid; false otherwise. + */ +exports.isValidUuid = function(uuid) { + var validUuidRegex = new RegExp('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$', 'ig'); + return validUuidRegex.test(uuid); +}; + exports = module.exports; diff --git a/ClientRuntimes/NodeJS/ms-rest/package.json b/ClientRuntimes/NodeJS/ms-rest/package.json index 65bb76c33e..ac28871f01 100644 --- a/ClientRuntimes/NodeJS/ms-rest/package.json +++ b/ClientRuntimes/NodeJS/ms-rest/package.json @@ -32,7 +32,8 @@ "jshint": "2.6.3", "xunit-file": "0.0.5", "mocha": "2.2.5", - "should": "5.2.0" + "should": "5.2.0", + "node-uuid": "*" }, "homepage": "https://github.com/Azure/AutoRest", "repository": { diff --git a/ClientRuntimes/NodeJS/ms-rest/test/serializationTests.js b/ClientRuntimes/NodeJS/ms-rest/test/serializationTests.js index bc22c69dec..d9be4bd39f 100644 --- a/ClientRuntimes/NodeJS/ms-rest/test/serializationTests.js +++ b/ClientRuntimes/NodeJS/ms-rest/test/serializationTests.js @@ -9,6 +9,7 @@ var msRest = require('../lib/msRest'); var testClient = require('./data/TestClient/lib/testClient'); var tokenCredentials = new msRest.TokenCredentials('dummy'); +var valid_uuid = 'ceaafd1e-f936-429f-bbfc-82ee75dddc33'; describe('msrest', function () { describe('serializeObject', function () { @@ -115,12 +116,28 @@ describe('msrest', function () { describe('serialize', function () { var mapper = {}; + var invalid_uuid = 'abcd-efgd90-90890jkh'; it('should correctly serialize a string', function (done) { mapper = { type : { name: 'String' } }; var serializedObject = msRest.serialize(mapper, 'foo', 'stringBody'); serializedObject.should.equal('foo'); done(); }); + it('should correctly serialize a uuid', function (done) { + mapper = { type : { name: 'Uuid' } }; + var serializedObject = msRest.serialize(mapper, valid_uuid, 'uuidBody'); + serializedObject.should.equal(valid_uuid); + done(); + }); + it('should throw an error if the value is not a valid Uuid', function (done) { + mapper = { type : { name: 'Uuid' } }; + try { + var serializedObject = msRest.serialize(mapper, invalid_uuid, 'uuidBody'); + } catch (error) { + error.message.should.match(/.*with value.*must be of type string and a valid uuid/ig); + done(); + } + }); it('should correctly serialize a number', function (done) { mapper = { type : { name: 'Number' } }; var serializedObject = msRest.serialize(mapper, 1.506, 'stringBody'); @@ -372,6 +389,12 @@ describe('msrest', function () { }); describe('deserialize', function () { + it('should correctly deserialize a uuid', function (done) { + mapper = { type : { name: 'Uuid' } }; + var serializedObject = msRest.deserialize(mapper, valid_uuid, 'uuidBody'); + serializedObject.should.equal(valid_uuid); + done(); + }); it('should correctly deserialize a composite type', function (done) { var client = new testClient('http://localhost:9090'); var product = new client.models['Product']();