Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

mongoose-beautiful-unique-validation

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mongoose-beautiful-unique-validation - npm Package Compare versions

Comparing version 3.0.1 to 3.0.2

8

CHANGELOG.md
# Changelog
## v3.0.2
### Fixed bugs
* Abandon parsing duplicated values using regexes and use
JSON.parse for a more robust result. This allows duplicated
values that contain spaces.
## v3.0.1

@@ -4,0 +12,0 @@

61

index.js

@@ -7,3 +7,3 @@ 'use strict';

var errorRegex = /index:\s*(?:.+?\.\$)?(.*?)\s*dup key:\s*\{(.*?)\}/;
var errorRegex = /index:\s*(?:.+?\.\$)?(.*?)\s*dup key:\s*(\{.*?\})/;
var indexesCache = {};

@@ -58,7 +58,6 @@

function beautify(error, model, messages) {
var matches, indexName, rawValues, valueRegex;
return new Promise(function (resolve, reject) {
// get index name from the error message
matches = errorRegex.exec(error.message);
// get the index name and duplicated values
// from the error message (with a hacky regex)
var matches = errorRegex.exec(error.message);

@@ -74,6 +73,27 @@ if (!matches) {

indexName = matches[1], rawValues = matches[2].trim() + ',';
valueRegex = /\s*:\s*(\S*),/g;
var indexName = matches[1];
var valuesHash = matches[2].trim(), valuesList;
// look for the index contained in the MongoDB error
// values hash is a pseudo-JSON hash containing the values
// of the unique fields in the index, in which they keys are missing:
// { : "value of field 1", : "value of field 2" }
// we fix that by converting it to an array, then parsing it as JSON:
// ["value of field 1", "value of field 2"]
valuesHash = valuesHash.replace(/^\{ :/, '[');
valuesHash = valuesHash.replace(/\}$/, ']');
valuesHash = valuesHash.replace(/, :/, ',');
try {
valuesList = JSON.parse(valuesHash);
} catch (err) {
reject(new Error(
'mongoose-beautiful-unique-validation error: ' +
'cannot parse duplicated values to a meaningful value. ' +
'Parsing error: ' + err.message
));
return;
}
// retrieve the index by name in the collection
// fail if we do not find such index
getIndexes(model.collection).then(function (indexes) {

@@ -93,19 +113,12 @@ var index = indexes[indexName];

// populate validation error with the index's fields
index.forEach(function (item) {
var value = valueRegex.exec(rawValues)[1],
path = item[0], props;
// the value is parsed directly from the error
// string. We try to guess the value type (in a basic way)
if (value[0] === '"' || value[0] === "'") {
value = value.substr(1, value.length - 2);
} else if (!isNaN(value)) {
value = parseFloat(value);
}
props = {
// the index contains the field keys in the same order
// (hopefully) as in the original error message. Create a
// duplication error for each field in the index, using
// valuesList to get the duplicated values
index.forEach(function (item, i) {
var path = item[0];
var props = {
type: 'Duplicate value',
path: path,
value: value
value: valuesList[i]
};

@@ -117,3 +130,3 @@

createdError.errors[item[0]] =
createdError.errors[path] =
new MongooseError.ValidatorError(props);

@@ -120,0 +133,0 @@ });

{
"name": "mongoose-beautiful-unique-validation",
"description": "Plug-in for Mongoose that turns duplicate errors into regular Mongoose validation errors",
"version": "3.0.1",
"version": "3.0.2",
"license": "MIT",

@@ -6,0 +6,0 @@ "main": "index.js",

@@ -6,66 +6,85 @@ 'use strict';

var mongoose = require('mongoose');
var Promise = require('promise');
var beautifulValidation = require('../');
// use global promise
// see http://mongoosejs.com/docs/promises.html
mongoose.Promise = Promise;
/**
* Create an unique model name
* (should be sufficiently unique for a few tests)
* Return a promise that is resolved
* nth milliseconds afterwards
*
* @return {string} Random model name
* @param {number} time Time to wait in milliseconds
* @return {Promise}
*/
function makeUniqueName() {
return crypto.randomBytes(8).toString('hex');
function wait(time) {
return new Promise(function (resolve) {
setTimeout(resolve, time);
});
}
/**
* Create a model with an unique "name"
* property and a random name
* Generate a 8-chars random string
* (should be sufficiently unique for a few tests)
*
* @param {string} custom Custom unique validation message
* @return {Model} Moongoose model
* @return {string} Random string
*/
function makeModel(custom, fieldName) {
var fields = {};
fields[fieldName || 'name'] = {
type: String,
unique: custom || true
};
var schema = new mongoose.Schema(fields);
schema.plugin(beautifulValidation);
return mongoose.model(makeUniqueName(), schema);
function uniqueString() {
return crypto.randomBytes(8).toString('hex');
}
/**
* Create a model with a compound unique index
* Create a randomly-named model with any number
* of fields. This model has an unique index over
* all created fields
*
* @param {string} custom Custom unique validation message
* @param {Array<string>|string} fieldNames Names of the fields
* @param {string} [message=default message] Custom unique validation message
* @return {Model} Mongoose model
*/
function makeCompoundModel(custom) {
var schema = new mongoose.Schema({
name: String,
email: String
});
function makeModel(fieldNames, message) {
var schema, fields = {};
schema.index({
name: 1,
email: 1
}, {unique: custom || true});
if (Array.isArray(fieldNames) && fieldNames.length > 1) {
// if there is more than one field,
// we create a compound index
var index = {};
fieldNames.forEach(function (fieldName) {
fields[fieldName] = String;
index[fieldName] = 1;
});
schema = new mongoose.Schema(fields);
schema.index(index, {
unique: message || true
});
} else {
// otherwise, we create a simple index
if (Array.isArray(fieldNames)) {
fieldNames = fieldNames[0];
}
fields[fieldNames] = {
type: String,
unique: message || true
};
schema = new mongoose.Schema(fields);
}
schema.plugin(beautifulValidation);
return mongoose.model(makeUniqueName(), schema);
return mongoose.model(uniqueString(), schema);
}
mongoose.connect('mongodb://127.0.0.1/test');
// connect to a database with a random name
mongoose.connect('mongodb://127.0.0.1/mongoose-buv-' + uniqueString());
mongoose.connection.on('open', function () {
test('should work like save', function (assert) {
var Model = makeModel(),
instance, name = 'testing';
var name = 'normal-test', Model = makeModel('name');
instance = new Model({
new Model({
name: name
});
instance.save(function (saveErr) {
}).save(function (saveErr) {
assert.error(saveErr, 'should save instance successfully');

@@ -84,10 +103,7 @@

test('should work with promises', function (assert) {
var Model = makeModel(),
instance, name = 'testing';
var name = 'promise-test', Model = makeModel('name');
instance = new Model({
new Model({
name: name
});
instance.save().then(function () {
}).save().then(function () {
Model.find({

@@ -100,3 +116,3 @@ name: name

});
}).catch(function (err) {
}, function (err) {
assert.error(err, 'should save instance successfully');

@@ -108,30 +124,30 @@ assert.end();

test('should emit duplicate validation error', function (assert) {
var Model = makeModel(),
originalInst, duplicateInst, name = 'duptest';
var name = 'duplicate-test', Model = makeModel('name');
originalInst = new Model({
// save the first instance
new Model({
name: name
});
originalInst.save().catch(function (err) {
assert.error(err, 'should save original instance successfully');
assert.end();
}).save().then(function () {
// ensure the unique index is rebuilt
return wait(500);
}).then(function () {
duplicateInst = new Model({
// try to save a duplicate (should not work)
new Model({
name: name
}).save().then(function () {
assert.fail('should not save duplicate successfully');
assert.end();
}, function (err) {
assert.ok(err, 'err should exist');
assert.equal(err.name, 'ValidationError', 'outer err should be of type ValidationError');
assert.equal(err.errors.name.name, 'ValidatorError', 'inner err should be ValidatorError');
assert.equal(err.errors.name.kind, 'Duplicate value', 'kind of err should be Duplicate value');
assert.equal(err.errors.name.properties.path, 'name', 'err should be on the correct path');
assert.equal(err.errors.name.properties.type, 'Duplicate value');
assert.equal(err.errors.name.properties.value, name, 'err should contain the problematic value');
assert.end();
});
return duplicateInst.save();
}).then(function () {
assert.fail('should not save duplicate successfully');
assert.end();
}, function (err) {
assert.ok(err, 'err should exist');
assert.equal(err.name, 'ValidationError', 'outer err should be of type ValidationError');
assert.equal(err.errors.name.name, 'ValidatorError', 'inner err should be ValidatorError');
assert.equal(err.errors.name.kind, 'Duplicate value', 'kind of err should be Duplicate value');
assert.equal(err.errors.name.properties.path, 'name', 'err should be on the correct path');
assert.equal(err.errors.name.properties.type, 'Duplicate value');
assert.equal(err.errors.name.properties.value, name, 'err should contain the problematic value');
assert.error(err, 'should save original instance successfully');
assert.end();

@@ -141,34 +157,24 @@ });

test('should work with compound unique indexes', function (assert) {
var Model = makeCompoundModel(),
name = 'duptest', email = 'duptest@example.com',
originalInst, duplicateInst;
test('should work with spaces in field name', function (assert) {
var name = 'duplicate-test-spaces', Model = makeModel('display name');
originalInst = new Model({
name: name,
email: email
});
originalInst.save().catch(function (err) {
assert.error(err, 'should save original instance successfully');
assert.end();
// save the first instance
new Model({
'display name': name
}).save().then(function () {
// ensure the unique index is rebuilt
return wait(500);
}).then(function () {
duplicateInst = new Model({
name: name,
email: email
// try to save a duplicate (should not work)
new Model({
'display name': name
}).save().then(function () {
assert.fail('should not save duplicate successfully');
assert.end();
}, function (err) {
assert.equal(err.errors['display name'].properties.path, 'display name', 'should keep the key with spaces');
assert.end();
});
return duplicateInst.save();
}).then(function () {
assert.fail('should not save duplicate successfully');
assert.end();
}, function (err) {
assert.ok(err, 'err should exist');
assert.equal(err.name, 'ValidationError', 'outer err should be of type ValidationError');
assert.equal(err.errors.name.name, 'ValidatorError', 'inner err should be ValidatorError');
assert.equal(err.errors.name.kind, 'Duplicate value', 'kind of err should be Duplicate value');
assert.equal(err.errors.name.properties.path, 'name', 'err should be on the correct path');
assert.equal(err.errors.name.properties.type, 'Duplicate value');
assert.equal(err.errors.name.properties.value, name, 'err should contain the problematic value');
assert.error(err, 'should save original instance successfully');
assert.end();

@@ -178,5 +184,5 @@ });

test('should use custom validation message', function (assert) {
var message = 'works!', Model = makeModel(message),
originalInst, duplicateInst, name = 'duptest';
test('should work with spaces in field value', function (assert) {
var message = 'works!', Model = makeModel('name', message),
originalInst, duplicateInst, name = 'dup test';

@@ -200,29 +206,70 @@ originalInst = new Model({

}, function (err) {
assert.equal(err.errors.name.message, message, 'message should be our custom value');
assert.equal(err.errors.name.message, message, 'should have correct error message');
assert.end();
}).catch(function (err) {
console.error(err.stack);
assert.fail('correct error message not found');
assert.end();
});
});
test('should work with spaces in field name', function (assert) {
var message = 'works!', Model = makeModel(message, 'display name'),
originalInst, duplicateInst, name = 'duptest';
test('should work with compound unique indexes', function (assert) {
var name = 'duplicate-test-compound', email = 'test@example.com',
Model = makeModel(['name', 'email']);
originalInst = new Model({
'display name': name
});
// save the first instance
new Model({
name: name,
email: email
}).save().then(function () {
// ensure the unique index is rebuilt
return wait(500);
}).then(function () {
// try to save a duplicate (should not work)
new Model({
name: name,
email: email
}).save().then(function () {
assert.fail('should not save duplicate successfully');
assert.end();
}, function (err) {
assert.ok(err, 'err should exist');
assert.equal(err.name, 'ValidationError', 'outer err should be of type ValidationError');
assert.equal(err.errors.name.name, 'ValidatorError', 'inner err should be ValidatorError');
assert.equal(err.errors.name.kind, 'Duplicate value', 'kind of err should be Duplicate value');
assert.equal(err.errors.name.properties.path, 'name', 'err should be on the correct path');
assert.equal(err.errors.name.properties.type, 'Duplicate value');
assert.equal(err.errors.name.properties.value, name, 'err should contain the problematic value');
duplicateInst = new Model({
'display name': name
assert.end();
});
}, function (err) {
assert.error(err, 'should save original instance successfully');
assert.end();
});
});
originalInst.save().catch(function (err) {
assert.error(err, 'should save original instance successfully');
assert.end();
test('should use custom validation message', function (assert) {
var name = 'duplicate-test-message', message = 'works!',
Model = makeModel('name', message);
// save the first instance
new Model({
name: name
}).save().then(function () {
// ensure the unique index is rebuilt
return wait(500);
}).then(function () {
return duplicateInst.save();
}).then(function () {
assert.fail('should not save duplicate successfully');
assert.end();
// try to save a duplicate (should not work)
new Model({
name: name
}).save().then(function () {
assert.fail('should not save duplicate successfully');
assert.end();
}, function (err) {
assert.equal(err.errors.name.message, message, 'message should be our custom value');
assert.end();
});
}, function (err) {
assert.equal(err.errors['display name'].properties.path, 'display name', 'should keep the key with spaces');
assert.error(err, 'should save original instance successfully');
assert.end();

@@ -233,23 +280,24 @@ });

test('should use custom validation message (compound index)', function (assert) {
var message = 'works!', Model = makeCompoundModel(message),
originalInst, duplicateInst, name = 'duptest';
var name = 'duplicate-test-message-compound', message = 'works!',
Model = makeModel(['name', 'email'], message);
originalInst = new Model({
// save the first instance
new Model({
name: name
});
duplicateInst = new Model({
name: name
});
originalInst.save().catch(function (err) {
assert.error(err, 'should save original instance successfully');
assert.end();
}).save().then(function () {
// ensure the unique index is rebuilt
return wait(500);
}).then(function () {
return duplicateInst.save();
}).then(function () {
assert.fail('should not save duplicate successfully');
assert.end();
// try to save a duplicate (should not work)
new Model({
name: name
}).save().then(function () {
assert.fail('should not save duplicate successfully');
assert.end();
}, function (err) {
assert.equal(err.errors.name.message, message, 'message should be our custom value (compound)');
assert.end();
});
}, function (err) {
assert.equal(err.errors.name.message, message, 'message should be our custom value (compound)');
assert.error(err, 'should save original instance successfully');
assert.end();

@@ -260,5 +308,11 @@ });

test('closing connection', function (assert) {
mongoose.disconnect();
assert.end();
// clean up the test database
mongoose.connection.db.dropDatabase().then(function () {
mongoose.disconnect();
assert.end();
}).catch(function (err) {
assert.error(err, 'should clean up the test database');
assert.end();
});
});
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc