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.2 to 4.0.0

tests/index.js

10

CHANGELOG.md
# Changelog
## v4.0.0
### Breaking changes
* Beautifies any kind of Mongoose field instead of passing
along the original error when working with `ObjectId`s, `Buffer`s
and `Date`s.
* The `trySave()` method, deprecated in v3.0.0, is removed.
## v3.0.2

@@ -4,0 +14,0 @@

43

index.js

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

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

@@ -73,24 +73,4 @@

var indexName = matches[1];
var valuesHash = matches[2].trim(), valuesList;
var valuesMap = error.getOperation() || {};
// 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

@@ -116,3 +96,3 @@ // fail if we do not find such index

// valuesList to get the duplicated values
index.forEach(function (item, i) {
index.forEach(function (item) {
var path = item[0];

@@ -122,3 +102,3 @@ var props = {

path: path,
value: valuesList[i]
value: valuesMap[path]
};

@@ -223,17 +203,2 @@

};
/**
* Deprecated, use #save instead
*/
schema.methods.trySave = function (callback) {
// /!\ Deprecation warning
console.warn(
'mongoose-beautiful-unique-validation: Model#trySave() is ' +
'deprecated, use Model#save() instead. To disable ' +
'beautifying on plugged-in models, set the "beautifyUnique" ' +
'option to false in this function\'s arguments'
);
return this.save({}, callback);
};
};
{
"name": "mongoose-beautiful-unique-validation",
"description": "Plug-in for Mongoose that turns duplicate errors into regular Mongoose validation errors",
"version": "3.0.2",
"description": "Plugin for Mongoose that turns duplicate errors into regular Mongoose validation errors",
"version": "4.0.0",
"license": "MIT",

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

# mongoose-beautiful-unique-validation
Plug-in for Mongoose that turns duplicate errors into regular Mongoose validation errors.
Plugin for Mongoose that turns duplicate errors into regular Mongoose
validation errors.

@@ -10,3 +11,4 @@ [![npm version](https://img.shields.io/npm/v/mongoose-beautiful-unique-validation.svg?style=flat-square)](https://www.npmjs.com/package/mongoose-beautiful-unique-validation)

Mongoose's unique constraint actually relies on MongoDB's `unique` indexes. It means that, if you have a schema like that one:
Mongoose's unicity constraint actually relies on MongoDB's `unique` indexes.
It means that, if you have a schema like this one:

@@ -22,3 +24,3 @@ ```js

Unique constraint failures will be reported with this kind of error:
Duplicates will be reported with this kind of error:

@@ -35,5 +37,7 @@ ```json

This is not the same kind of error as normal [Validation](http://mongoosejs.com/docs/validation.html)
errors: it will introduce more checks in your code if you want to handle them.
This plug-in solves this problem by converting uniqueness errors (E11000 and E11001) into regular Validation errors.
This is not the same kind of error as normal
[Validation](http://mongoosejs.com/docs/validation.html) errors, so you need
to handle that as a special case―and special cases allow room for bugs.
This plugin solves this problem by converting driver-level duplicate errors
(E11000 and E11001) into regular Validation errors.

@@ -117,6 +121,6 @@ ```json

`Validator failed for path xxx with value xxx`.
If you want to override it, add your custom message in the `unique` field (instead of `true`),
during the schema's creation.
If you want to override it, add your custom message in the `unique`
field (instead of `true`), during the schema's creation.
This plug-in overrides the `.save()` method to add beautifying
This plugin overrides the `.save()` method to add beautifying
behavior. If you do not want it, you can pass the

@@ -126,3 +130,3 @@ `beautifyUnique` option to `false`:

```js
document.save({
doc.save({
beautifyUnique: false

@@ -134,8 +138,18 @@ }).then(() => {

## Contributing
## Supported versions
Check out the [contribution guide.](https://github.com/matteodelabre/mongoose-beautiful-unique-validation/blob/master/CONTRIBUTING.md)
The latest version of this module supports all of the following Node.js versions.
If you find a bug while using one of these versions, you can
[fill a bug report](https://github.com/matteodelabre/mongoose-beautiful-unique-validation/issues/new)
and we will take care of it as soon as possible!
* 6.*
* 5.*
* 4.*
* 0.12.*
* 0.10.*
## License
See the [LICENSE.](https://github.com/matteodelabre/mongoose-beautiful-unique-validation/blob/master/LICENSE)
Released under the MIT license.
[See the full license text.](https://github.com/matteodelabre/mongoose-beautiful-unique-validation/blob/master/LICENSE)
'use strict';
var test = require('tape');
var crypto = require('crypto');
var mongoose = require('mongoose');
var Promise = require('promise');
var Schema = mongoose.Schema;
var assertDuplicateFailure = require('./utils').assertDuplicateFailure;
var beautifulValidation = require('../');
// use global promise
// see http://mongoosejs.com/docs/promises.html
mongoose.Promise = Promise;
/**
* Return a promise that is resolved
* nth milliseconds afterwards
*
* @param {number} time Time to wait in milliseconds
* @return {Promise}
*/
function wait(time) {
return new Promise(function (resolve) {
setTimeout(resolve, time);
test('should save documents', function (assert) {
var NormalSaveSchema = new Schema({
name: String
});
}
/**
* Generate a 8-chars random string
* (should be sufficiently unique for a few tests)
*
* @return {string} Random string
*/
function uniqueString() {
return crypto.randomBytes(8).toString('hex');
}
NormalSaveSchema.plugin(beautifulValidation);
/**
* Create a randomly-named model with any number
* of fields. This model has an unique index over
* all created fields
*
* @param {Array<string>|string} fieldNames Names of the fields
* @param {string} [message=default message] Custom unique validation message
* @return {Model} Mongoose model
*/
function makeModel(fieldNames, message) {
var schema, fields = {};
var NormalSave = mongoose.model('NormalSave', NormalSaveSchema);
var instance = new NormalSave({
name: 'Test for Normal Save'
});
if (Array.isArray(fieldNames) && fieldNames.length > 1) {
// if there is more than one field,
// we create a compound index
var index = {};
instance.save(function (saveError) {
assert.error(saveError, 'should save an instance successfully');
fieldNames.forEach(function (fieldName) {
fields[fieldName] = String;
index[fieldName] = 1;
NormalSave.find({
name: 'Test for Normal Save'
}, function (findError, result) {
assert.error(findError, 'should find back the saved instance');
assert.looseEqual(result[0].name, 'Test for Normal Save', 'should keep the document intact');
assert.end();
});
});
});
schema = new mongoose.Schema(fields);
schema.index(index, {
unique: message || true
test('should provide a promise', function (assert) {
var PromiseSaveSchema = new Schema({
name: String
});
PromiseSaveSchema.plugin(beautifulValidation);
var PromiseSave = mongoose.model('PromiseSave', PromiseSaveSchema);
var instance = new PromiseSave({
name: 'Test for Promise Save'
});
instance.save().then(function () {
PromiseSave.find({
name: 'Test for Promise Save'
}, function (findError, result) {
assert.error(findError, 'should find back the saved instance');
assert.looseEqual(result[0].name, 'Test for Promise Save', 'should keep the document intact');
assert.end();
});
} else {
// otherwise, we create a simple index
if (Array.isArray(fieldNames)) {
fieldNames = fieldNames[0];
}
}).catch(function (error) {
assert.error(error, 'should save an instance successfully');
});
});
fields[fieldNames] = {
test('should report duplicates', function (assert) {
var DuplicateSchema = new Schema({
address: {
type: String,
unique: message || true
};
unique: true
}
});
schema = new mongoose.Schema(fields);
}
DuplicateSchema.plugin(beautifulValidation);
var Duplicate = mongoose.model('Duplicate', DuplicateSchema);
schema.plugin(beautifulValidation);
return mongoose.model(uniqueString(), schema);
}
assertDuplicateFailure(assert, Duplicate, {
address: '123 Fake St.'
});
});
// 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 name = 'normal-test', Model = makeModel('name');
test('should report duplicates on fields containing spaces', function (assert) {
var SpacesSchema = new Schema({
'display name': {
type: String,
unique: true
}
});
new Model({
name: name
}).save(function (saveErr) {
assert.error(saveErr, 'should save instance successfully');
SpacesSchema.plugin(beautifulValidation);
var Spaces = mongoose.model('Spaces', SpacesSchema);
Model.find({
name: name
}, function (findErr, found) {
assert.error(findErr, 'should run find correctly');
assert.equal(found[0].name, name, 'should keep props & values');
assert.end();
});
});
assertDuplicateFailure(assert, Spaces, {
'display name': 'Testing display names'
});
});
test('should work with promises', function (assert) {
var name = 'promise-test', Model = makeModel('name');
test('should report duplicates on compound indexes', function (assert) {
var CompoundSchema = new Schema({
name: String,
age: Number
});
new Model({
name: name
}).save().then(function () {
Model.find({
name: name
}, function (err, found) {
assert.error(err, 'should run find correctly');
assert.equal(found[0].name, name, 'should keep props & values');
assert.end();
});
}, function (err) {
assert.error(err, 'should save instance successfully');
assert.end();
});
CompoundSchema.index({
name: 1,
age: 1
}, {
unique: true
});
test('should emit duplicate validation error', function (assert) {
var name = 'duplicate-test', Model = makeModel('name');
CompoundSchema.plugin(beautifulValidation);
var Compound = mongoose.model('Compound', CompoundSchema);
// save the first instance
new Model({
name: name
}).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
}).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();
});
}, function (err) {
assert.error(err, 'should save original instance successfully');
assert.end();
});
assertDuplicateFailure(assert, Compound, {
name: 'John Doe',
age: 42
});
});
test('should work with spaces in field name', function (assert) {
var name = 'duplicate-test-spaces', Model = makeModel('display name');
// save the first instance
new Model({
'display name': name
}).save().then(function () {
// ensure the unique index is rebuilt
return wait(500);
}).then(function () {
// 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();
});
}, function (err) {
assert.error(err, 'should save original instance successfully');
assert.end();
});
test('should report duplicates with the custom validation message', function (assert) {
var MessageSchema = new Schema({
address: {
type: String,
unique: 'this is our custom message!'
}
});
test('should work with spaces in field value', function (assert) {
var message = 'works!', Model = makeModel('name', message),
originalInst, duplicateInst, name = 'dup test';
MessageSchema.plugin(beautifulValidation);
var Message = mongoose.model('Message', MessageSchema);
originalInst = new Model({
name: name
});
assertDuplicateFailure(assert, Message, {
address: '123 Fake St.'
}, 'this is our custom message!');
});
duplicateInst = new Model({
name: name
});
test('should report duplicates on compound indexes with the custom validation message', function (assert) {
var CompoundMessageSchema = new Schema({
name: String,
age: Number
});
originalInst.save().catch(function (err) {
assert.error(err, 'should save original instance successfully');
assert.end();
}).then(function () {
return duplicateInst.save();
}).then(function () {
assert.fail('should not save duplicate successfully');
assert.end();
}, function (err) {
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();
});
CompoundMessageSchema.index({
name: 1,
age: 1
}, {
unique: 'yet another custom message'
});
test('should work with compound unique indexes', function (assert) {
var name = 'duplicate-test-compound', email = 'test@example.com',
Model = makeModel(['name', 'email']);
CompoundMessageSchema.plugin(beautifulValidation);
var CompoundMessage = mongoose.model('CompoundMessage', CompoundMessageSchema);
// 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');
assertDuplicateFailure(assert, CompoundMessage, {
name: 'John Doe',
age: 42
}, 'yet another custom message');
});
assert.end();
});
}, function (err) {
assert.error(err, 'should save original instance successfully');
assert.end();
});
test('should report duplicates on any mongoose type', function (assert) {
var AnyTypeSchema = new Schema({
name: String,
group: Schema.Types.ObjectId,
age: Number,
date: Date,
blob: Buffer,
isVerified: Boolean,
list: []
});
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 () {
// 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.error(err, 'should save original instance successfully');
assert.end();
});
AnyTypeSchema.index({
name: 1,
group: 1,
age: 1,
date: 1,
blob: 1,
isVerified: 1,
list: 1
}, {
unique: true
});
test('should use custom validation message (compound index)', function (assert) {
var name = 'duplicate-test-message-compound', message = 'works!',
Model = makeModel(['name', 'email'], message);
AnyTypeSchema.plugin(beautifulValidation);
var AnyType = mongoose.model('AnyType', AnyTypeSchema);
// save the first instance
new Model({
name: name
}).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
}).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.error(err, 'should save original instance successfully');
assert.end();
});
assertDuplicateFailure(assert, AnyType, {
name: 'test',
group: new mongoose.Types.ObjectId,
age: 42,
date: new Date,
blob: new Buffer('abc'),
isVerified: false,
list: [1, 2, 3]
});
test('closing connection', function (assert) {
// 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