diff --git a/packages/minimongo/minimongo.js b/packages/minimongo/minimongo.js index f79f2d4bc1..312c9e4581 100644 --- a/packages/minimongo/minimongo.js +++ b/packages/minimongo/minimongo.js @@ -40,7 +40,11 @@ Minimongo = {}; // Use it to export private functions to test in Tinytest. MinimongoTest = {}; -MinimongoError = function (message) { +MinimongoError = function (message, options={}) { + if (typeof message === "string" && options.field) { + message += ` for field '${options.field}'`; + } + var e = new Error(message); e.name = "MinimongoError"; return e; diff --git a/packages/minimongo/minimongo_tests.js b/packages/minimongo/minimongo_tests.js index 69e728448e..9edc47c52e 100644 --- a/packages/minimongo/minimongo_tests.js +++ b/packages/minimongo/minimongo_tests.js @@ -163,6 +163,22 @@ Tinytest.add("minimongo - basics", function (test) { }); +Tinytest.add("minimongo - error - no options", function (test) { + try { + throw MinimongoError("Not fun to have errors"); + } catch (e) { + test.equal(e.message, "Not fun to have errors"); + } +}); + +Tinytest.add("minimongo - error - with field", function (test) { + try { + throw MinimongoError("Cats are no fun", { field: "mice" }); + } catch (e) { + test.equal(e.message, "Cats are no fun for field 'mice'"); + } +}); + Tinytest.add("minimongo - cursors", function (test) { var c = new LocalCollection(); var res; diff --git a/packages/minimongo/modify.js b/packages/minimongo/modify.js index e595d82cab..7074482ee6 100644 --- a/packages/minimongo/modify.js +++ b/packages/minimongo/modify.js @@ -185,20 +185,24 @@ var MODIFIERS = { $currentDate: function (target, field, arg) { if (typeof arg === "object" && arg.hasOwnProperty("$type")) { if (arg.$type !== "date") { - throw MinimongoError("Minimongo does currently only support the date type in $currentDate modifiers: " + field); + throw MinimongoError( + "Minimongo does currently only support the date type " + + "in $currentDate modifiers", + { field }); } } else if (arg !== true) { - throw MinimongoError("Invalid $currentDate modifier: " + field); + throw MinimongoError("Invalid $currentDate modifier", { field }); } target[field] = new Date(); }, $min: function (target, field, arg) { if (typeof arg !== "number") { - throw MinimongoError("Modifier $min allowed for numbers only: " + field); + throw MinimongoError("Modifier $min allowed for numbers only", { field }); } if (field in target) { if (typeof target[field] !== "number") { - throw MinimongoError("Cannot apply $min modifier to non-number: " + field); + throw MinimongoError( + "Cannot apply $min modifier to non-number", { field }); } if (target[field] > arg) { target[field] = arg; @@ -209,11 +213,12 @@ var MODIFIERS = { }, $max: function (target, field, arg) { if (typeof arg !== "number") { - throw MinimongoError("Modifier $max allowed for numbers only: " + field); + throw MinimongoError("Modifier $max allowed for numbers only", { field }); } if (field in target) { if (typeof target[field] !== "number") { - throw MinimongoError("Cannot apply $max modifier to non-number: " + field); + throw MinimongoError( + "Cannot apply $max modifier to non-number", { field }); } if (target[field] < arg) { target[field] = arg; @@ -224,10 +229,11 @@ var MODIFIERS = { }, $inc: function (target, field, arg) { if (typeof arg !== "number") - throw MinimongoError("Modifier $inc allowed for numbers only: " + field); + throw MinimongoError("Modifier $inc allowed for numbers only", { field }); if (field in target) { if (typeof target[field] !== "number") - throw MinimongoError("Cannot apply $inc modifier to non-number: " + field); + throw MinimongoError( + "Cannot apply $inc modifier to non-number", { field }); target[field] += arg; } else { target[field] = arg; @@ -235,12 +241,13 @@ var MODIFIERS = { }, $set: function (target, field, arg) { if (!_.isObject(target)) { // not an array or an object - var e = MinimongoError("Cannot set property on non-object field: " + field); + var e = MinimongoError( + "Cannot set property on non-object field", { field }); e.setPropertyError = true; throw e; } if (target === null) { - var e = MinimongoError("Cannot set property on null: " + field); + var e = MinimongoError("Cannot set property on null", { field }); e.setPropertyError = true; throw e; } @@ -267,7 +274,8 @@ var MODIFIERS = { if (target[field] === undefined) target[field] = []; if (!(target[field] instanceof Array)) - throw MinimongoError("Cannot apply $push modifier to non-array: " + field); + throw MinimongoError( + "Cannot apply $push modifier to non-array", { field }); if (!(arg && arg.$each)) { // Simple mode: not $each @@ -278,16 +286,17 @@ var MODIFIERS = { // Fancy mode: $each (and maybe $slice and $sort and $position) var toPush = arg.$each; if (!(toPush instanceof Array)) - throw MinimongoError("$each must be an array: " + field); + throw MinimongoError("$each must be an array", { field }); // Parse $position var position = undefined; if ('$position' in arg) { if (typeof arg.$position !== "number") - throw MinimongoError("$position must be a numeric value: " + field); + throw MinimongoError("$position must be a numeric value", { field }); // XXX should check to make sure integer if (arg.$position < 0) - throw MinimongoError("$position in $push must be zero or positive: " + field); + throw MinimongoError( + "$position in $push must be zero or positive", { field }); position = arg.$position; } @@ -295,10 +304,11 @@ var MODIFIERS = { var slice = undefined; if ('$slice' in arg) { if (typeof arg.$slice !== "number") - throw MinimongoError("$slice must be a numeric value: " + field); + throw MinimongoError("$slice must be a numeric value", { field }); // XXX should check to make sure integer if (arg.$slice > 0) - throw MinimongoError("$slice in $push must be zero or negative: " + field); + throw MinimongoError( + "$slice in $push must be zero or negative", { field }); slice = arg.$slice; } @@ -306,7 +316,7 @@ var MODIFIERS = { var sortFunction = undefined; if (arg.$sort) { if (slice === undefined) - throw MinimongoError("$sort requires $slice to be present: " + field); + throw MinimongoError("$sort requires $slice to be present", { field }); // XXX this allows us to use a $sort whose value is an array, but that's // actually an extension of the Node driver, so it won't work // server-side. Could be confusing! @@ -315,7 +325,7 @@ var MODIFIERS = { for (var i = 0; i < toPush.length; i++) { if (LocalCollection._f._type(toPush[i]) !== 3) { throw MinimongoError("$push like modifiers using $sort " + - "require all elements to be objects: " + field); + "require all elements to be objects", { field }); } } } @@ -350,7 +360,8 @@ var MODIFIERS = { if (x === undefined) target[field] = arg; else if (!(x instanceof Array)) - throw MinimongoError("Cannot apply $pushAll modifier to non-array: " + field); + throw MinimongoError( + "Cannot apply $pushAll modifier to non-array", { field }); else { for (var i = 0; i < arg.length; i++) x.push(arg[i]); @@ -371,7 +382,8 @@ var MODIFIERS = { if (x === undefined) target[field] = values; else if (!(x instanceof Array)) - throw MinimongoError("Cannot apply $addToSet modifier to non-array: " + field); + throw MinimongoError( + "Cannot apply $addToSet modifier to non-array", { field }); else { _.each(values, function (value) { for (var i = 0; i < x.length; i++) @@ -388,7 +400,8 @@ var MODIFIERS = { if (x === undefined) return; else if (!(x instanceof Array)) - throw MinimongoError("Cannot apply $pop modifier to non-array: " + field); + throw MinimongoError( + "Cannot apply $pop modifier to non-array", { field }); else { if (typeof arg === 'number' && arg < 0) x.splice(0, 1); @@ -403,7 +416,8 @@ var MODIFIERS = { if (x === undefined) return; else if (!(x instanceof Array)) - throw MinimongoError("Cannot apply $pull/pullAll modifier to non-array: " + field); + throw MinimongoError( + "Cannot apply $pull/pullAll modifier to non-array", { field }); else { var out = []; if (arg != null && typeof arg === "object" && !(arg instanceof Array)) { @@ -430,14 +444,16 @@ var MODIFIERS = { }, $pullAll: function (target, field, arg) { if (!(typeof arg === "object" && arg instanceof Array)) - throw MinimongoError("Modifier $pushAll/pullAll allowed for arrays only: " + field); + throw MinimongoError( + "Modifier $pushAll/pullAll allowed for arrays only", { field }); if (target === undefined) return; var x = target[field]; if (x === undefined) return; else if (!(x instanceof Array)) - throw MinimongoError("Cannot apply $pull/pullAll modifier to non-array: " + field); + throw MinimongoError( + "Cannot apply $pull/pullAll modifier to non-array", { field }); else { var out = []; for (var i = 0; i < x.length; i++) { @@ -457,15 +473,17 @@ var MODIFIERS = { $rename: function (target, field, arg, keypath, doc) { if (keypath === arg) // no idea why mongo has this restriction.. - throw MinimongoError("$rename source must differ from target: " + field); + throw MinimongoError("$rename source must differ from target", { field }); if (target === null) - throw MinimongoError("$rename source field invalid: " + field); + throw MinimongoError("$rename source field invalid", { field }); if (typeof arg !== "string") - throw MinimongoError("$rename target must be a string: " + field); + throw MinimongoError("$rename target must be a string", { field }); if (arg.indexOf('\0') > -1) { // Null bytes are not allowed in Mongo field names // https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names - throw MinimongoError("The 'to' field for $rename cannot contain an embedded null byte: " + field); + throw MinimongoError( + "The 'to' field for $rename cannot contain an embedded null byte", + { field }); } if (target === undefined) return; @@ -475,13 +493,13 @@ var MODIFIERS = { var keyparts = arg.split('.'); var target2 = findModTarget(doc, keyparts, {forbidArray: true}); if (target2 === null) - throw MinimongoError("$rename target field invalid: " + field); + throw MinimongoError("$rename target field invalid", { field }); var field2 = keyparts.pop(); target2[field2] = v; }, $bit: function (target, field, arg) { // XXX mongo only supports $bit on integers, and we only support // native javascript numbers (doubles) so far, so we can't support $bit - throw MinimongoError("$bit is not supported: " + field); + throw MinimongoError("$bit is not supported", { field }); } }; diff --git a/packages/minimongo/package.js b/packages/minimongo/package.js index e4353c687a..d490228a29 100644 --- a/packages/minimongo/package.js +++ b/packages/minimongo/package.js @@ -7,6 +7,7 @@ Package.onUse(function (api) { api.export('LocalCollection'); api.export('Minimongo'); api.export('MinimongoTest', { testOnly: true }); + api.export('MinimongoError', { testOnly: true }); api.use([ 'underscore', 'ejson',