diff --git a/lib/waterline/query/deferred.js b/lib/waterline/query/deferred.js index cb962a0a4..0552118a2 100644 --- a/lib/waterline/query/deferred.js +++ b/lib/waterline/query/deferred.js @@ -296,6 +296,9 @@ Deferred.prototype.where = function(criteria) { if(!criteria) return this; + // Normalize any primary keys in criteria + criteria = normalize.expandPK(this._context, criteria); + // Normalize criteria criteria = normalize.criteria(criteria); diff --git a/lib/waterline/utils/normalize.js b/lib/waterline/utils/normalize.js index f5c67e76f..69b86651a 100644 --- a/lib/waterline/utils/normalize.js +++ b/lib/waterline/utils/normalize.js @@ -5,6 +5,8 @@ var switchback = require('node-switchback'); var errorify = require('../error'); var WLUsageError = require('../error/WLUsageError'); +var uuidPattern = /[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}/i + var normalize = module.exports = { // Expand Primary Key criteria into objects @@ -50,6 +52,24 @@ var normalize = module.exports = { else if (context.attributes[pk].type == 'string') { coercePK = function(pk) {return String(pk).toString();}; } + + // If the the data type is uuid (both according to the model definition + // as well as the database schema) attempt to coerce the value to a + // properly formatted uuid, stripping any extraneous characters from it. + else if (context.attributes[pk].type == 'uuid' && + context.schema && + context.schema[pk] && + context.schema[pk].type == 'uuid') { + coercePK = function(pk) { + var matches = uuidPattern.exec(pk); + var uuid = matches && matches[0]; + if (uuid) { + return uuid; + } else { + throw new TypeError("value '" + pk + "' cannot be coerced to UUID"); + } + }; + } // If the data type is unspecified, return the key as-is else { coercePK = function(pk) {return pk;}; diff --git a/test/unit/query/query.find.js b/test/unit/query/query.find.js index 5c4fa591c..83271c4df 100644 --- a/test/unit/query/query.find.js +++ b/test/unit/query/query.find.js @@ -82,6 +82,16 @@ describe('Collection Query', function() { }); }); + it('should normalize primary keys when using deferreds', function(done) { + query.find() + .where({ id: '123' }) + .exec(function(err, results) { + assert(!err); + assert(results[0].where.id === 123); + done(); + }); + }); + describe('.paginate()', function() { it('should skip to 0 and limit to 10 by default', function(done) { query.find() diff --git a/test/unit/utils/utils.normalize.js b/test/unit/utils/utils.normalize.js index b42586014..e5c1a6711 100644 --- a/test/unit/utils/utils.normalize.js +++ b/test/unit/utils/utils.normalize.js @@ -33,4 +33,128 @@ describe("Normalize utility", function() { }); -}); \ No newline at end of file + describe(".expandPK()", function() { + it("casts integers", function() { + var context = { + attributes: { + id: { + type: 'integer', + primaryKey: true + } + } + }; + + var options = { + id: '123' + } + + var result = normalize.expandPK(context, options); + + assert(result.id === 123); + }); + + it("casts uuids", function() { + var context = { + attributes: { + id: { + type: 'uuid', + primaryKey: true + } + }, + schema: { + id: { + type: 'uuid' + } + } + }; + + var options = { + id: 'prefix_0b6c28e0-a117-4a9e-9a0d-60f0992edbee' + } + + var result = normalize.expandPK(context, options); + + assert(result.id === '0b6c28e0-a117-4a9e-9a0d-60f0992edbee'); + }); + + it("casts uuids with capitals", function() { + var context = { + attributes: { + id: { + type: 'uuid', + primaryKey: true + } + }, + schema: { + id: { + type: 'uuid' + } + } + }; + + var options = { + id: '0B6C28E0-A117-4A9E-9A0D-60F0992EDBEE' + } + + var result = normalize.expandPK(context, options); + + assert(result.id === '0B6C28E0-A117-4A9E-9A0D-60F0992EDBEE'); + }); + + it("does not cast uuids with an underlying non-uuid type", function() { + var context = { + attributes: { + id: { + type: 'uuid', + primaryKey: true + } + }, + schema: { + id: { + type: 'text' + } + } + }; + + var options = { + id: 'prefix_0b6c28e0-a117-4a9e-9a0d-60f0992edbee' + } + + var result = normalize.expandPK(context, options); + + assert(result.id === 'prefix_0b6c28e0-a117-4a9e-9a0d-60f0992edbee'); + + }); + + it("throws a TypeError when attempting to cast a non-uuid", function() { + var context = { + attributes: { + id: { + type: 'uuid', + primaryKey: true + } + }, + schema: { + id: { + type: 'uuid' + } + } + }; + + var options = { + id: 'hello' + } + + try { + normalize.expandPK(context, options); + throw new Error('wrong error'); + } catch (e) { + assert(e); + assert(e instanceof TypeError); + assert(e.message === "value 'hello' cannot be coerced to UUID"); + } + + }); + }); + +});