Skip to content

Commit

Permalink
better code, added express tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rusty1s committed Dec 6, 2015
1 parent aa81815 commit 72f5d02
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 278 deletions.
79 changes: 33 additions & 46 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,37 @@
/* jshint node: true */
'use strict';

var i18nError = module.exports = function(options) {
options = options || {};
this.prefix = options.prefix ||  this.prefix;
};

i18nError.prototype = {
prefix: 'error',
module.exports = function(options) {
this.prefix = (options || {}).prefix ||  'error.';

handler: function(handler) {
this.handler = function(handler) {
var self = this;

return function(err, req, res, next) {
if (!err) return next(err);
if (typeof res.__ !== 'function') {
console.error('no i18n.__ function found.');
return next(err);
}

var i18n;
if (typeof res.__ === 'function') i18n = res;
else if (req.i18n && typeof req.i18n.__ === 'function') i18n = req.i18n;
else throw 'no i18n.__ function found.';
var i18nErr = self.parseValidationError(err, res.__) ||  self.parseUniqueError(err, res.__);

var mongooseErr = self.parseValidationError(err, i18n);
if (!mongooseErr) mongooseErr = self.parseUniqueError(err, i18n);
if (mongooseErr) {
return handler(mongooseErr, req, res, next);
} else return next(err);
if (i18nErr) return handler(i18nErr, req, res, next);
else return next(err);
};
},
};

parseValidationError: function(err, i18n) {
if (!err ||  err.name !== 'ValidationError') return null;
this.parseValidationError = function(err, __) {
if (!err) return null;
if (err.name !== 'ValidationError') return null;

var self = this;
var result = {};

var errors = err.errors;
Object.keys(errors).forEach(function(key) {
var error = errors[key];
for (var key in err.errors) {
var error = err.errors[key];
var type = error.kind;
var condition;
var message = self.prefix + '.';
var value = error.value;
var message = this.prefix;

// cast error
if (error.name === 'CastError') {
Expand All @@ -50,14 +41,13 @@ i18nError.prototype = {

// match or enum error
else if (type === 'regexp' ||  type === 'enum') {
var model = err.message.substring(0, err.message.lastIndexOf(' validation failed')).toLowerCase();
var model = /(.*)\svalidation\sfailed/.exec(err.message)[1].toLowerCase();
message += model + '.' + key + '.' + type;
}

// custom validation error
else if (type.match(/user defined/)) {
var array = error.message.split(/\./g);
type = array[array.length - 1];
type = error.message.split(/\./g).reverse()[0];
message += error.message;
}

Expand All @@ -69,30 +59,27 @@ i18nError.prototype = {

result[key] = {
type: type,
message: i18n.__(message, condition),
value: value
message: __(message, condition),
value: error.value
};
});
}

return result;
},
};

parseUniqueError: function(err, i18n) {
if (!err ||  err.name !== 'MongoError' || !(err.code === 11000 ||  err.code === 11001)) return null;
this.parseUniqueError = function(err, __) {
if (!err) return null;
if (err.name !== 'MongoError') return null;
if (err.code !== 11000 && err.code !== 11001) return null;

var result = {};

var regex = /index:\s*.+?\.\$(\S*)_.+\s*dup key:\s*\{.*?:\s*"(.*)"\s*\}/;
var matches = regex.exec(err.message);
var key = matches[1];
var value = matches[2];
var matches = /index:\s.*\.\$(.*)_1\sdup key:\s\{\s:\s"(.*)"\s\}/.exec(err.message);

result[key] = {
var result = {};
result[matches[1]] = {
type: 'unique',
message: i18n.__(this.prefix + '.unique'),
value: value
message: __(this.prefix + 'unique'),
value: matches[2]
};

return result;
}
};
};
1 change: 0 additions & 1 deletion locales/de.json

This file was deleted.

10 changes: 0 additions & 10 deletions locales/en.js

This file was deleted.

13 changes: 13 additions & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"error.required": "is required",
"error.minlength": "needs a minimum length of %s",
"error.maxlength": "has a length greater than %s",
"error.model.value.regexp": "must consist of only letters",
"error.model.value.enum": "must be either \"true\" or \"false\"",
"error.min": "needs a minimum value of %s",
"error.max": "has a value greater than %s",
"error.model.value.custom": "must consist of only letters",
"error.unique": "already exists",
"err.required": "is not allowed to left blank",
"error.cast": "is not valid"
}
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"devDependencies": {
"mongoose": "^4.2.8",
"i18n": "^0.5.0",
"i18n-2": "^0.6.0",
"express": "^4.13.3",
"should": "^7.1.1",
"supertest": "1.1.0"
Expand Down
21 changes: 14 additions & 7 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
/* jshint node: true, mocha: true */
'use strict';

var express = require('express');
var mongoose = require('mongoose');

var app = express();
var server = app.listen(3000);
var express = require('express');
var i18n = require('i18n');

mongoose.connect('mongodb://localhost/mongoose-i18n-error');
mongoose.connection.on('error', function() {
throw new Error('Unable to connect to database.');
});

var app = express();
var server = app.listen(3000);

i18n.configure({
locales: ['en'],
directory: './locales'
});

app.use(i18n.init);

describe('Mongoose I18n Error', function() {
require('./tests/message')();
require('./tests/i18n')(app);
//require('./tests/i18n-2')(app);
require('./tests/message')(i18n);
require('./tests/express')(app);
});

server.close();
65 changes: 65 additions & 0 deletions test/tests/express.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* jshint node: true, mocha: true */
'use strict';

var mongoose = require('mongoose');
var request = require('supertest');
var should = require('should');

var helper = require('../helper');
var i18nMongooseError = new(require('../../index'))();

module.exports = function(app) {

describe('Express', function() {
afterEach(helper.afterEach);

it('should print a localized validation error message', function(done) {
var Model = mongoose.model('Model', helper.createStringSchema());
app.post('/1', function(req, res, next) {
new Model().save(function(err) {
if (err) return next(err);
res.sendStatus(200);
});
});

app.use(i18nMongooseError.handler(function(err, req, res) {
res.status(422).json(err);
}));

request(app).post('/1').expect(422).end(function(err, res) {
res.body.value.message.should.equal('is required');

done();
});
});

it('should print a localized unique error message', function(done) {
var Model = mongoose.model('Model', helper.createUniqueSchema());

app.post('/2', function(req, res, next) {
new Model({
value: 'ab'
}).save(function(err) {
if (err) return next(err);
res.sendStatus(200);
});
});

app.use(i18nMongooseError.handler(function(err, req, res) {
res.status(422).json(err);
}));

new Model({
value: 'ab'
}).save(function(err) {
should.not.exist(err);

request(app).post('/2').expect(422).end(function(err, res) {
res.body.value.message.should.equal('already exists');

done();
});
});
});
});
};
85 changes: 0 additions & 85 deletions test/tests/i18n-2.js

This file was deleted.

Loading

0 comments on commit 72f5d02

Please sign in to comment.