├── .gitignore
├── .jshintrc
├── .npmignore
├── .travis.yml
├── Gruntfile.js
├── README.md
├── api
├── controllers
│ └── AuthenticationController.js
└── models
│ ├── Authentication.js
│ └── User.js
├── config
└── locales
│ ├── en.json
│ └── sw.json
├── doc.html
├── index.js
├── lib
├── WLValidationError.js
├── create.js
├── createEach.js
├── findOrCreate.js
├── findOrCreateEach.js
├── update.js
├── validate.js
└── validateCustom.js
├── package.json
└── test
├── bootstrap.spec.js
├── validation.spec.js
├── validation.zcreate.spec.js
├── validation.zcreateEach.spec.js
├── validation.zfindOrCreate.spec.js
├── validation.zi18n.spec.js
└── validation.zupdate.spec.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | #Temporary data
6 | .tmp
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 |
13 | # Directory for instrumented libs generated by jscoverage/JSCover
14 | lib-cov
15 |
16 | # Coverage directory used by tools like istanbul
17 | coverage
18 |
19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
20 | .grunt
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # Deployed apps should consider commenting this line out:
27 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
28 | node_modules
29 | doc
30 | startup.sh
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "bitwise": true,
3 | "browser": true,
4 | "camelcase": true,
5 | "curly": true,
6 | "eqeqeq": true,
7 | "esnext": true,
8 | "immed": true,
9 | "latedef": true,
10 | "newcap": true,
11 | "noarg": true,
12 | "node": true,
13 | "mocha": true,
14 | "quotmark": "single",
15 | "strict": true,
16 | "undef": true,
17 | "unused": true,
18 | "expr": true,
19 | "ignore": true,
20 | "globals": {
21 | "User": true,
22 | "Authentication": true,
23 | "sails": true,
24 | "_": true
25 | }
26 | }
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | api
2 | config
3 | node_modules
4 | ssl
5 | .DS_STORE
6 | *~
7 | .idea
8 | nbproject
9 | test
10 | .git
11 | .gitignore
12 | .tmp
13 | *.swo
14 | *.swp
15 | *.swn
16 | *.swm
17 | *.log
18 | .jshintrc
19 | .editorconfig
20 | doc.html
21 | .travis.yml
22 | Gruntfile.js
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.10"
4 | before_script:
5 | - npm install -g grunt-cli
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(grunt) {
4 |
5 | // Add the grunt-mocha-test and jshint tasks.
6 | grunt.loadNpmTasks('grunt-mocha-test');
7 | grunt.loadNpmTasks('grunt-contrib-jshint');
8 |
9 | grunt.initConfig({
10 | // Configure a mochaTest task
11 | mochaTest: {
12 | test: {
13 | options: {
14 | reporter: 'spec',
15 | timeout: 20000
16 | },
17 | src: ['test/**/*.js']
18 | }
19 | },
20 | jshint: {
21 | options: {
22 | reporter: require('jshint-stylish'),
23 | jshintrc: '.jshintrc'
24 | },
25 | all: [
26 | 'Gruntfile.js',
27 | 'index.js',
28 | 'lib/**/*.js',
29 | 'test/**/*.js'
30 | ]
31 | }
32 | });
33 |
34 | //custom tasks
35 | grunt.registerTask('default', ['jshint', 'mochaTest']);
36 | grunt.registerTask('test', ['jshint', 'mochaTest']);
37 |
38 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | sails-hook-validation
2 | =====================
3 |
4 | [](https://travis-ci.org/lykmapipo/sails-hook-validation)
5 |
6 | Custom validation error messages for sails model with i18n support. Its works with `callback`, `deferred` and `promise` style `model API` provided with sails.
7 |
8 | *Note:*
9 | - *This requires Sails v0.11.0+. If v0.11.0+ isn't published to NPM yet, you'll need to install it via Github.*
10 | - *`sails-hook-validation` work by patch model static `validate()`, `create()`, `createEach()`, `findOrCreate()`, `findOrCreateEach()` and `update()`.*
11 | - *To have custom error messages at model instance level consider using [sails-model-new](https://github.com/lykmapipo/sails-model-new).*
12 | - *`sails-hook-validation` opt to use `error.Errors` and not to re-create or remove any properties of error object so as to remain with sails legacy options*
13 |
14 | ## Installation
15 | ```sh
16 | $ npm install --save sails-hook-validation
17 | ```
18 |
19 | ## Usage
20 |
21 | ### Use without i18n
22 | Add `validationMessages` static property in your sails model
23 | ```js
24 | //this is example
25 | module.exports = {
26 | attributes: {
27 | username: {
28 | type: 'string',
29 | required: true
30 | },
31 | email: {
32 | type: 'email',
33 | required: true,
34 | unique: true
35 | }
36 | },
37 | //model validation messages definitions
38 | validationMessages: { //hand for i18n & l10n
39 | email: {
40 | required: 'Email is required',
41 | email: 'Provide valid email address',
42 | unique: 'Email address is already taken'
43 | },
44 | username: {
45 | required: 'Username is required'
46 | }
47 | }
48 | };
49 | ```
50 |
51 | ### Use with i18n
52 | Add validation messages into `config/locale` files following `modelId.attribute.rule`. If model contains any `validationMessages` definition, they will take precedence over locale defined messages.
53 |
54 | Example
55 | ```javascript
56 | //define your model in api/model/Authentication.js
57 | module.exports = {
58 | attributes: {
59 | username: {
60 | type: 'string',
61 | required: true
62 | },
63 | email: {
64 | type: 'email',
65 | required: true,
66 | unique: true
67 | },
68 | birthday: {
69 | type: 'date',
70 | required: true
71 | }
72 | }
73 | };
74 | ```
75 |
76 | Then define validation messages per locale
77 |
78 | ```javascript
79 | //in config/locale/en.json
80 | {
81 | "authentication.email.required": "Email is required",
82 | "authentication.email.email": "Provide valid email address",
83 | "authentication.email.unique": "Email address is already taken",
84 | "authentication.username.required": "Username is required",
85 | "authentication.username.string": "Username is must be a valid string",
86 | "authentication.birthday.required": "Birthday is required",
87 | "authentication.birthday.date": "Birthday is not a valid date"
88 | }
89 |
90 | //in config/locale/sw.json
91 | {
92 | "authentication.email.required": "Barua pepe yahitajika",
93 | "authentication.email.email": "Toa barua pepe iliyo halali",
94 | "authentication.email.unique": "Barua pepe tayari ishachukuliwa",
95 | "authentication.username.required": "Jina lahitajika",
96 | "authentication.username.string": "Jina lazima liwe ni mchanganyiko wa herufi",
97 | "authentication.birthday.required": "Siku ya kuzaliwa yahitajika",
98 | "authentication.birthday.date": "Siku ya kuzaliwa sio tarehe"
99 | }
100 | ```
101 |
102 | Now you can call model static
103 | - `create()`
104 | - `createEach()`
105 | - `findOrCreate()`
106 | - `findOrCreateEach()`
107 | - `update()`
108 | - and other static model method that invoke `Model.validate()`.
109 |
110 | If there is any *validations or database errors* `sails-hook-validation` will put your custom errors message in `error.Errors` of the error object returned by those methods in your `callback` or `promise catch`.
111 |
112 | ### create()
113 | ```js
114 | //anywhere in your codes if
115 | //you invoke
116 | User
117 | .create({}, function(error, user) {
118 | //you will expect the following
119 | //error to exist on error.Errors based on
120 | //your custom validation messages
121 |
122 | expect(error.Errors.email).to.exist;
123 |
124 | expect(error.Errors.email[0].message)
125 | .to.equal(User.validationMessages.email.email);
126 |
127 | expect(error.Errors.email[1].message)
128 | .to.equal(User.validationMessages.email.required);
129 |
130 |
131 | expect(error.Errors.username).to.exist;
132 | expect(error.Errors.username[0].message)
133 | .to.equal(User.validationMessages.username.required);
134 |
135 | done();
136 | });
137 | ```
138 |
139 | ### createEach()
140 | ```js
141 | User
142 | .createEach([{
143 | email: faker.internet.email(),
144 | username: faker.internet.userName()
145 | }, {
146 | email: 'otherExistingModelEmail',
147 | username: username
148 | }])
149 | .catch(function(error) {
150 | //you will expect the following
151 | //error to exist on error.Errors based on
152 | //your custom validation messages
153 |
154 | expect(error.Errors.email).to.exist;
155 |
156 | expect(error.Errors.email[0].message)
157 | .to.equal(User.validationMessages.email.unique);
158 |
159 | done();
160 | });
161 | ```
162 |
163 | ### findOrCreate()
164 | ```js
165 | User
166 | .findOrCreate({
167 | email: faker.internet.email()
168 | }, {
169 | email: 'otherExistingModelEmail',
170 | username: username
171 | })
172 | .exec(function(error, user) {
173 | //you will expect the following
174 | //error to exist on error.Errors based on
175 | //your custom validation messages
176 |
177 | expect(error.Errors.email).to.exist;
178 |
179 | expect(error.Errors.email[0].message)
180 | .to.equal(User.validationMessages.email.unique);
181 |
182 | done();
183 | });
184 | ```
185 |
186 | ### findOrCreateEach()
187 | ```js
188 | User
189 | .findOrCreateEach(
190 | [{
191 | email: faker.internet.email()
192 | }], [{
193 | email: 'otheExistingModelEmail',
194 | username: username
195 | }]
196 | )
197 | .catch(function(error) {
198 | //you will expect the following
199 | //error to exist on error.Errors based on
200 | //your custom validation messages
201 |
202 | expect(error.Errors.email).to.exist;
203 |
204 | expect(error.Errors.email[0].message)
205 | .to.equal(User.validationMessages.email.unique);
206 |
207 | done();
208 | });
209 | ```
210 |
211 | ### update()
212 | ```js
213 | User
214 | .update({
215 | email: 'modelEmail'
216 | }, {
217 | email: 'otherExistingModelEmail'
218 | }, function(error, user) {
219 | //you will expect the following
220 | //error to exist on error.Errors based on
221 | //your custom validation messages
222 |
223 | expect(error.Errors.email).to.exist;
224 |
225 | expect(error.Errors.email[0].message)
226 | .to.equal(User.validationMessages.email.unique);
227 |
228 | done();
229 | });
230 | ```
231 |
232 | ## Testing
233 |
234 | * Clone this repository
235 |
236 | * Install all development dependencies
237 |
238 | ```sh
239 | $ npm install
240 | ```
241 | * Then run test
242 |
243 | ```sh
244 | $ npm test
245 | ```
246 |
247 | ## Contribute
248 |
249 | Fork this repo and push in your ideas.
250 | Do not forget to add a bit of test(s) of what value you adding.
251 |
252 | ## Licence
253 |
254 | The MIT License (MIT)
255 |
256 | Copyright (c) 2015 lykmapipo & Contributors
257 |
258 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
259 |
260 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
261 |
262 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/api/controllers/AuthenticationController.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * @description Authentication Controller
5 | * @type {Object}
6 | */
7 | module.exports = {
8 | create: function(request, response) {
9 | Authentication
10 | .create(request.body)
11 | .exec(function(error, authentication) {
12 |
13 | if (error) {
14 | response.status(400);
15 | response.json(error.Errors);
16 | } else {
17 | response.status(200);
18 | resposne.json(authentication);
19 | }
20 | });
21 | }
22 | }
--------------------------------------------------------------------------------
/api/models/Authentication.js:
--------------------------------------------------------------------------------
1 | /**
2 | * sample model
3 | * @type {Object}
4 | */
5 | module.exports = {
6 | attributes: {
7 | username: {
8 | type: 'string',
9 | required: true
10 | },
11 | email: {
12 | type: 'email',
13 | required: true,
14 | unique: true
15 | },
16 | birthday: {
17 | type: 'date',
18 | required: true
19 | }
20 | }
21 |
22 | //validation messages definitions
23 | //are taken from config/locales
24 | //based on request locale
25 | //otherwise using default locale
26 |
27 | };
--------------------------------------------------------------------------------
/api/models/User.js:
--------------------------------------------------------------------------------
1 | /**
2 | * sample model
3 | * @type {Object}
4 | */
5 | module.exports = {
6 | attributes: {
7 | username: {
8 | required: true,
9 | type: 'string'
10 | },
11 | email: {
12 | required: true,
13 | type: 'email',
14 | unique: true
15 | },
16 | birthday: {
17 | required: true,
18 | type: 'date'
19 | },
20 | nickname: {
21 | type: 'string',
22 | minLength: 2
23 | }
24 | },
25 |
26 | //validation messages definitions
27 | validationMessages: { //hand for i18n & l10n
28 | email: {
29 | required: 'Email address is required',
30 | email: 'Provide valid email address',
31 | unique: 'Email address is already taken'
32 | },
33 | username: {
34 | required: 'Username is required'
35 | },
36 | birthday: {
37 | required: 'Your birthday is required',
38 | date: 'Birthday is not a valid date'
39 | }
40 | }
41 |
42 | };
--------------------------------------------------------------------------------
/config/locales/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "authentication.email.required": "Email is required",
3 | "authentication.email.email": "Provide valid email address",
4 | "authentication.email.unique": "Email address is already taken",
5 | "authentication.username.required": "Username is required",
6 | "authentication.username.string": "Username is must be a valid string",
7 | "authentication.birthday.required": "Birthday is required",
8 | "authentication.birthday.date": "Birthday is not a valid date"
9 | }
--------------------------------------------------------------------------------
/config/locales/sw.json:
--------------------------------------------------------------------------------
1 | {
2 | "authentication.email.required": "Barua pepe yahitajika",
3 | "authentication.email.email": "Toa barua pepe iliyo halali",
4 | "authentication.email.unique": "Barua pepe tayari ishachukuliwa",
5 | "authentication.username.required": "Jina lahitajika",
6 | "authentication.username.string": "Jina lazima liwe ni mchanganyiko wa herufi",
7 | "authentication.birthday.required": "Siku ya kuzaliwa yahitajika",
8 | "authentication.birthday.date": "Siku ya kuzaliwa sio tarehe"
9 | }
--------------------------------------------------------------------------------
/doc.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | sails-hook-validation | Custom error messages for sails model.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
31 |
32 |
33 |
34 |
35 |
50 |
51 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | var path = require('path');
5 | var libPath = path.join(__dirname, 'lib');
6 |
7 | var validateCustom = require(path.join(libPath, 'validateCustom'));
8 |
9 | //patches
10 | var create = require(path.join(libPath, 'create'));
11 | var createEach = require(path.join(libPath, 'createEach'));
12 | var findOrCreate = require(path.join(libPath, 'findOrCreate'));
13 | var findOrCreateEach = require(path.join(libPath, 'findOrCreateEach'));
14 | var update = require(path.join(libPath, 'update'));
15 | var validate = require(path.join(libPath, 'validate'));
16 |
17 | //patch WLValidationError
18 | require(path.join(libPath, 'WLValidationError'));
19 |
20 |
21 | /**
22 | * @description allow model to define its custom validation error messages.
23 | * It hooks into model static methods that call `validate()`,
24 | * grab the `ValidationError` from the resulted error object,
25 | * process it and attach `Errors` as custom errors message into the
26 | * error object.
27 | * @param {Object} sails a sails application instance
28 | */
29 | module.exports = function(sails) {
30 | //patch sails model
31 | //to add custom errors message
32 | //logic
33 | function patch() {
34 | (_ || sails.util._)
35 | .forEach(sails.models, function(model) {
36 | //bind path validate
37 | //on concrete models
38 | //and left derived model
39 | //build from associations
40 | if (model.globalId) {
41 |
42 | //patch sails `create()` method
43 | create(model, validateCustom);
44 |
45 | //patch sails `createEach()` method
46 | createEach(model, validateCustom);
47 |
48 | //patch sails `findOrCreate()` method
49 | findOrCreate(model, validateCustom);
50 |
51 | //patch sails `findOrCreateEach()` method
52 | findOrCreateEach(model, validateCustom);
53 |
54 | //patch sails `update()` method
55 | update(model, validateCustom);
56 |
57 | //patch sails `validate()` method
58 | validate(model, validateCustom);
59 |
60 | }
61 | });
62 | }
63 |
64 | //export hook definition
65 | return {
66 | //intercent all request and current grab request locale
67 | routes: {
68 | before: {
69 | 'all /*': function grabLocale(request, response, next) {
70 | //configure i18n current request locale
71 | if(request && typeof request.getLocale === 'function'){
72 | sails.config.i18n.requestLocale = request.getLocale();
73 | }
74 |
75 | //continue
76 | next();
77 | }
78 | }
79 | },
80 |
81 | initialize: function(done) {
82 | var eventsToWaitFor = [];
83 |
84 | //wait for orm hook
85 | //to be loaded
86 | if (sails.hooks.orm) {
87 | eventsToWaitFor.push('hook:orm:loaded');
88 | }
89 |
90 | //wait for pub sub hook
91 | //to be loaded
92 | if (sails.hooks.pubsub) {
93 | eventsToWaitFor.push('hook:pubsub:loaded');
94 | }
95 |
96 | //apply validation hook
97 | sails
98 | .after(eventsToWaitFor, function() {
99 | //bind custom errors logic
100 | //and let sails to continue
101 | patch();
102 |
103 | done();
104 | });
105 | }
106 | };
107 |
108 | };
109 |
--------------------------------------------------------------------------------
/lib/WLValidationError.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var WLError = require('waterline/lib/waterline/error/WLError');
4 |
5 | module.exports.patch = function(error) {
6 | var toJSON = error.toJSON;
7 |
8 | error = new WLError(error);
9 |
10 | error.toJSON =
11 | error.toPOJO = function() {
12 |
13 | var errorKey = 'Errors';
14 |
15 | if (sails && sails.config && sails.config.errors &&
16 | typeof sails.config.errors.errorKey === 'string') {
17 | errorKey = sails.config.errors.errorKey;
18 | }
19 |
20 | if (!toJSON) {
21 | toJSON = WLError.prototype.toJSON;
22 | }
23 |
24 | var asJSON = toJSON.call(this);
25 | asJSON[errorKey] = this.Errors;
26 | return asJSON;
27 | };
28 |
29 | return error;
30 | };
31 |
--------------------------------------------------------------------------------
/lib/create.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | //import sails waterline Deferred
5 | var Deferred = require('waterline/lib/waterline/query/deferred');
6 | var WLValidationError = require('./WLValidationError');
7 |
8 | /**
9 | * @description path sails `create()` method to allow
10 | * custom error message definitions
11 | * @param {Object} model a valid sails model
12 | * @param {Function} validateCustom a function to transform sails `invalidAttributes`
13 | * to custome `Errors`
14 | */
15 | module.exports = function(model, validateCustom) {
16 | //remember sails defined create
17 | //method
18 | //See https://github.com/balderdashy/waterline/blob/master/lib/waterline/query/dql/create.js
19 | var sailsCreate = model.create;
20 |
21 | //prepare new create method
22 | //which wrap sailsCreate
23 | //with custom error message checking
24 | function create(values, callback) {
25 |
26 | // handle Deferred where
27 | // it passes criteria first
28 | // see https://github.com/balderdashy/waterline/blob/master/lib/waterline/query/dql/create.js#L26
29 | if (arguments.length === 3) {
30 | var args = Array.prototype.slice.call(arguments);
31 | callback = args.pop();
32 | values = args.pop();
33 | }
34 |
35 | // return Deferred
36 | // if no callback passed
37 | // See https://github.com/balderdashy/waterline/blob/master/lib/waterline/query/dql/create.js#L54
38 | if (typeof callback !== 'function') {
39 | //this refer to the
40 | //model context
41 | return new Deferred(model, model.create, {}, values);
42 | }
43 |
44 | //otherwise
45 | //call sails create
46 | sailsCreate
47 | .call(model, values, function(error, result) {
48 | //any create error
49 | //found?
50 | if (error) {
51 | //process sails invalidAttributes and
52 | //attach Errors key in error object
53 | //as a place to lookup for our
54 | //custom errors messages
55 | if (error.invalidAttributes) {
56 | var customError =
57 | validateCustom(model, error.invalidAttributes);
58 |
59 | // will return and override with empty object
60 | // when using associations
61 | if (Object.keys(customError).length !== 0) {
62 | error.Errors = customError;
63 | }
64 | }
65 |
66 | callback(WLValidationError.patch(error));
67 | } else {
68 | //no error
69 | //return
70 | callback(null, result);
71 | }
72 | });
73 | }
74 |
75 | //bind our new create
76 | //to our models
77 | model.create = create;
78 | };
79 |
--------------------------------------------------------------------------------
/lib/createEach.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | //import sails waterline Deferred
5 | var Deferred = require('waterline/lib/waterline/query/deferred');
6 | var WLValidationError = require('./WLValidationError');
7 |
8 | /**
9 | * @description path sails `createEach()` method to allow
10 | * custom error message definitions
11 | * @param {Object} model a valid sails model
12 | * @param {Function} validateCustom a function to transform sails `invalidAttributes`
13 | * to custome `Errors`
14 | */
15 | module.exports = function(model, validateCustom) {
16 | //remember sails defined createEach
17 | //method
18 | //See https://github.com/balderdashy/waterline/blob/master/lib/waterline/query/aggregate.js#L24
19 | var sailsCreate = model.createEach;
20 |
21 | //prepare new createEach method
22 | function createEach(values, callback) {
23 | // handle Deferred where
24 | // it passes criteria first
25 | // See https://github.com/balderdashy/waterline/blob/master/lib/waterline/query/aggregate.js#L27
26 | if (arguments.length === 3) {
27 | var args = Array.prototype.slice.call(arguments);
28 | callback = args.pop();
29 | values = args.pop();
30 | }
31 |
32 | // return Deferred
33 | // if no callback passed
34 | // See https://github.com/balderdashy/waterline/blob/master/lib/waterline/query/aggregate.js#L35
35 | if (typeof callback !== 'function') {
36 | //this refer to the
37 | //model context
38 | return new Deferred(model, model.createEach, {}, values);
39 | }
40 |
41 | //otherwise
42 | //call sails createEach
43 | sailsCreate
44 | .call(model, values, function(error, result) {
45 | //any createEach error
46 | //found?
47 | if (error) {
48 | //process sails invalidAttributes and
49 | //attach Errors key in error object
50 | //as a place to lookup for our
51 | //custom errors messages
52 | if (error.invalidAttributes) {
53 | var customError =
54 | validateCustom(model, error.invalidAttributes);
55 |
56 | // will return and override with empty object
57 | // when using associations
58 | if (Object.keys(customError).length !== 0) {
59 | error.Errors = customError;
60 | }
61 | }
62 |
63 | callback(WLValidationError.patch(error));
64 | } else {
65 | //no error
66 | //return
67 | callback(null, result);
68 | }
69 | });
70 | }
71 |
72 | //bind our new createEach
73 | //to our models
74 | model.createEach = createEach;
75 | };
76 |
--------------------------------------------------------------------------------
/lib/findOrCreate.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | //import sails waterline Deferred
5 | var Deferred = require('waterline/lib/waterline/query/deferred');
6 | var WLValidationError = require('./WLValidationError');
7 |
8 | /**
9 | * @description path sails `findOrCreate()` method to allow
10 | * custom error message definitions
11 | * @param {Object} model a valid sails model
12 | * @param {Function} validateCustom a function to transform sails `invalidAttributes`
13 | * to custome `Errors`
14 | */
15 | module.exports = function(model, validateCustom) {
16 | //remember sails defined findOrCreate
17 | //method
18 | //See https://github.com/balderdashy/waterline/blob/master/lib/waterline/query/composite.js#L24
19 | var sailsFindOrCreate = model.findOrCreate;
20 |
21 | //prepare new findOrCreate method
22 | function findOrCreate(criteria, values, callback) {
23 | // return Deferred
24 | // if no callback passed
25 | // See https://github.com/balderdashy/waterline/blob/master/lib/waterline/query/composite.js#L43
26 | if (typeof callback !== 'function') {
27 | //this refer to the
28 | //model context
29 | return new Deferred(model, model.findOrCreate, criteria, values);
30 | }
31 |
32 | //otherwise
33 | //call sails findOrCreate
34 | sailsFindOrCreate
35 | .call(model, criteria, values, function(error, result) {
36 | //any findOrCreate error
37 | //found?
38 | if (error) {
39 | //process sails invalidAttributes and
40 | //attach Errors key in error object
41 | //as a place to lookup for our
42 | //custom errors messages
43 | if (error.invalidAttributes) {
44 | var customError =
45 | validateCustom(model, error.invalidAttributes);
46 |
47 | // will return and override with empty object when using associations
48 | if (Object.keys(customError).length !== 0) {
49 | error.Errors = customError;
50 | }
51 | }
52 |
53 | callback(WLValidationError.patch(error));
54 | } else {
55 | //no error
56 | //return
57 | callback(null, result);
58 | }
59 | });
60 | }
61 |
62 | //bind our new findOrCreate
63 | //to our models
64 | model.findOrCreate = findOrCreate;
65 | };
66 |
--------------------------------------------------------------------------------
/lib/findOrCreateEach.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | //import sails waterline Deferred
5 | var Deferred = require('waterline/lib/waterline/query/deferred');
6 | var WLValidationError = require('./WLValidationError');
7 |
8 | /**
9 | * @deprecated See https://github.com/balderdashy/waterline/pull/843
10 | * @description path sails `findOrCreateEach()` method to allow
11 | * custom error message definitions
12 | * @param {Object} model a valid sails model
13 | * @param {Function} validateCustom a function to transform sails `invalidAttributes`
14 | * to custome `Errors`
15 | */
16 | module.exports = function(model, validateCustom) {
17 | //remember sails defined findOrCreateEach
18 | //method
19 | //See https://github.com/balderdashy/waterline/blob/master/lib/waterline/query/aggregate.js#L84
20 | var sailsFindOrCreateEach = model.findOrCreateEach;
21 |
22 | //prepare new findOrCreateEach method
23 | function findOrCreateEach(criterias, values, callback) {
24 | // return Deferred
25 | // if no callback passed
26 | // See https://github.com/balderdashy/waterline/blob/master/lib/waterline/query/aggregate.js#L96
27 | if (typeof callback !== 'function') {
28 | //this refer to the
29 | //model context
30 | return new Deferred(model, model.findOrCreateEach, criterias, values);
31 | }
32 |
33 | //otherwise
34 | //call sails findOrCreateEach
35 | sailsFindOrCreateEach
36 | .call(model, criterias, values, function(error, result) {
37 | //any findOrCreateEach error
38 | //found?
39 | if (error) {
40 | //process sails invalidAttributes and
41 | //attach Errors key in error object
42 | //as a place to lookup for our
43 | //custom errors messages
44 | if (error.invalidAttributes) {
45 | var customError =
46 | validateCustom(model, error.invalidAttributes);
47 |
48 | // will return and override with empty object
49 | // when using associations
50 | if (Object.keys(customError).length !== 0) {
51 | error.Errors = customError;
52 | }
53 | }
54 | //hand over error
55 | //to user callback
56 | callback(WLValidationError.patch(error));
57 | } else {
58 | //no error
59 | //
60 | //hand over result
61 | //to user callback
62 | callback(null, result);
63 | }
64 | });
65 | }
66 |
67 | //bind our new findOrCreateEach
68 | //to our models
69 | model.findOrCreateEach = findOrCreateEach;
70 | };
71 |
--------------------------------------------------------------------------------
/lib/update.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | //import sails waterline Deferred
5 | var Deferred = require('waterline/lib/waterline/query/deferred');
6 | var WLValidationError = require('./WLValidationError');
7 |
8 | /**
9 | * @description path sails `update()` method to allow
10 | * custom error message definitions
11 | * @param {Object} model a valid sails model
12 | * @param {Function} validateCustom a function to transform sails `invalidAttributes`
13 | * to custome `Errors`
14 | */
15 | module.exports = function(model, validateCustom) {
16 | //remember sails defined update
17 | //method
18 | //See https://github.com/balderdashy/waterline/blob/master/lib/waterline/query/dql/update.js
19 | var sailsUpdate = model.update;
20 |
21 | //prepare new update method
22 | //which wrap sailsUpdate
23 | //with custom error message checking
24 | function update(criterias, values, callback) {
25 |
26 | // return Deferred
27 | // if no callback passed
28 | // See https://github.com/balderdashy/waterline/blob/master/lib/waterline/query/dql/update.js#L34
29 | if (typeof callback !== 'function') {
30 | //this refer to the
31 | //model context
32 | return new Deferred(model, model.update, criterias, values);
33 | }
34 |
35 | //otherwise
36 | //call sails update
37 | sailsUpdate
38 | .call(model, criterias, values, function(error, result) {
39 | //any update error
40 | //found?
41 | if (error) {
42 | //process sails invalidAttributes and
43 | //attach Errors key in error object
44 | //as a place to lookup for our
45 | //custom errors messages
46 | if (error.invalidAttributes) {
47 | var customError =
48 | validateCustom(model, error.invalidAttributes);
49 |
50 | // will return and override with empty object
51 | // when using associations
52 | if (Object.keys(customError).length !== 0) {
53 | error.Errors = customError;
54 | }
55 | }
56 |
57 | callback(WLValidationError.patch(error));
58 | } else {
59 | //no error
60 | //return
61 | callback(null, result);
62 | }
63 | });
64 | }
65 |
66 | //bind our new update
67 | //to our models
68 | model.update = update;
69 | };
70 |
--------------------------------------------------------------------------------
/lib/validate.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | var WLValidationError = require('./WLValidationError');
5 |
6 | /**
7 | * @description path sails `validate()` method to allow
8 | * custom error message definitions
9 | * @param {Object} model a valid sails model
10 | * @param {Function} validateCustom a function to transform sails `invalidAttributes`
11 | * to custome `Errors`
12 | */
13 | module.exports = function(model, validateCustom) {
14 | //remember sails defined validation
15 | //method
16 | var sailsValidate = model.validate;
17 |
18 | //prepare new validation method
19 | function validate(values, presentOnly, callback) {
20 | if(typeof presentOnly === 'function'){
21 | callback = presentOnly;
22 | presentOnly = null;
23 | }
24 | //call sails validate
25 | sailsValidate
26 | .call(model, values, presentOnly, function(error) {
27 | //any validation error
28 | //found?
29 | if (error) {
30 | //process sails invalidAttributes and
31 | //attach Errors key in error object
32 | //as a place to lookup for our
33 | //custom errors messages
34 | if (error.invalidAttributes) {
35 | var customError =
36 | validateCustom(model, error.invalidAttributes);
37 |
38 | // will return and override with empty object
39 | // when using associations
40 | if (Object.keys(customError).length !== 0) {
41 | error.Errors = customError;
42 | }
43 | }
44 |
45 | callback(WLValidationError.patch(error));
46 | } else {
47 | //no error
48 | //return
49 | callback(null);
50 | }
51 | });
52 | }
53 |
54 | //bind our new validate
55 | //to our models
56 | model.validate = validate;
57 | };
58 |
--------------------------------------------------------------------------------
/lib/validateCustom.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 |
5 | /**
6 | *
7 | * @descriptions process validation error and mixin user defined errors
8 | * to produce friendly error messages.
9 | *
10 | * For this to work a model may either define `validationMessages`
11 | * hash as static property or adding validation messages into
12 | * locale files.
13 | *
14 | * @param {Object} model valid sails model definition
15 | * @param {Object} invalidAttributes a valid sails validation error object.
16 | *
17 | * @returns {Object} an object with friendly validation error conversions.
18 | */
19 | module.exports = function(model, invalidAttributes) {
20 |
21 | //grab model validations definitions
22 | var validations = model._validator.validations || {};
23 |
24 | //grab field names
25 | //from the messages
26 | var validationFields = Object.keys(validations);
27 |
28 | //custom validation error storage
29 | var customValidationMessages = {};
30 |
31 |
32 | //iterate over all model
33 | //defined validations
34 | //and process thrown sails invalidAttributes
35 | //to model custom defined errors
36 | validationFields
37 | .forEach(function(validationField) {
38 |
39 | //grab field errors from the
40 | //sails validation error hash
41 | //
42 | //if sails is connected to a database a user can declare a column name
43 | //the valid sails validation error not use the column name as property anymore
44 | var fieldErrors = invalidAttributes[validationField];
45 |
46 | //is there any field
47 | //error(s) found in invalidAttributes
48 | if (fieldErrors) {
49 |
50 | //iterate through each field of
51 | //sails validation error and
52 | //convert them
53 | //to custom model defined errors
54 | fieldErrors
55 | .forEach(function(fieldError) {
56 | //try
57 | //built custom error message from
58 | //from sails i18n
59 |
60 | //grab phrase to use to find custom message
61 | //from locales
62 | var phrase = [
63 | model.globalId.toLowerCase(),
64 | validationField,
65 | fieldError.rule
66 | ].join('.');
67 |
68 | var customMessage = phrase;
69 | var locale;
70 |
71 | if(sails.config.i18n){
72 | //deduce locale from request else
73 | //use default locale
74 | locale =
75 | sails.config.i18n.requestLocale ||
76 | sails.config.i18n.defaultLocale;
77 |
78 | if(locale){
79 | //grab custom error
80 | //message from config/locales/`locale`.json
81 | customMessage = sails.__({
82 | phrase: phrase,
83 | locale: locale
84 | });
85 | }
86 | }
87 |
88 | //make sure custom error message from i18n exists
89 | var i18nMessageExist =
90 | customMessage !== phrase &&
91 | sails.util._.isString(customMessage);
92 |
93 | //else
94 | //grab friedly error message of
95 | //the defined rule which has an error
96 | //from model defined
97 | //validation messages
98 | var messages = model.validationMessages;
99 |
100 | if (!i18nMessageExist && messages) {
101 | if (messages[validationField] && messages[validationField][fieldError.rule]) {
102 | customMessage = sails.__({
103 | phrase: messages[validationField][fieldError.rule],
104 | locale: locale
105 | });
106 | } else {
107 | //return default error message if user forgot to add custon validation message or not even specifying the field key in validationMessages
108 | customMessage = fieldError.message;
109 | }
110 | }
111 |
112 | if (customMessage) {
113 | //collect custom error messages
114 | if (!(customValidationMessages[validationField] instanceof Array)) {
115 | customValidationMessages[validationField] = [];
116 | }
117 |
118 | //build friendly error message
119 | var newMessage = {
120 | 'rule': fieldError.rule,
121 | 'message': customMessage
122 | };
123 |
124 | customValidationMessages[validationField].push(newMessage);
125 | }
126 | });
127 | }
128 | });
129 |
130 | return customValidationMessages;
131 | };
132 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sails-hook-validation",
3 | "version": "0.4.7",
4 | "description": "Custom validation error messages for sails model with i18n support",
5 | "main": "index.js",
6 | "sails": {
7 | "isHook": true
8 | },
9 | "scripts": {
10 | "pretest": "npm link && npm link sails-hook-validation",
11 | "test": "grunt test"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/lykmapipo/sails-hook-validation.git"
16 | },
17 | "keywords": [
18 | "sails",
19 | "sails-hook",
20 | "model",
21 | "validator",
22 | "validation",
23 | "validate",
24 | "custom",
25 | "messages",
26 | "define",
27 | "database",
28 | "exceptions",
29 | "constraints",
30 | "i18n",
31 | "internationalization",
32 | "hook",
33 | "plugin",
34 | "error",
35 | "module"
36 | ],
37 | "author": {
38 | "name": "lykmapipo",
39 | "email": "lallyelias87@gmail.com",
40 | "url": "https://github.com/lykmapipo"
41 | },
42 | "license": "MIT",
43 | "bugs": {
44 | "url": "https://github.com/lykmapipo/sails-hook-validation/issues"
45 | },
46 | "homepage": "https://github.com/lykmapipo/sails-hook-validation",
47 | "contributors": [{
48 | "name": "lykmapipo",
49 | "github": "https://github.com/lykmapipo"
50 | }, {
51 | "name": "isery",
52 | "github": "https://github.com/isery"
53 | }, {
54 | "name": "Alaneor",
55 | "github": "https://github.com/Alaneor"
56 | }, {
57 | "name": "VMBindraban",
58 | "github": "https://github.com/VMBindraban"
59 | }],
60 | "devDependencies": {
61 | "chai": "^2.3.0",
62 | "faker": "^2.1.5",
63 | "grunt": "^0.4.5",
64 | "grunt-contrib-jshint": "^0.11.3",
65 | "grunt-mocha-test": "^0.12.7",
66 | "jshint-stylish": "^1.0.2",
67 | "mocha": "^2.4.5",
68 | "sails": "^0.11.2",
69 | "sails-disk": "^0.10.8",
70 | "supertest": "^1.1.0"
71 | },
72 | "dependencies": {
73 | "waterline": "^0.10.31"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/test/bootstrap.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | var sails = require('sails');
5 |
6 | /**
7 | * Lifting sails before all tests
8 | */
9 | before(function(done) {
10 | sails
11 | .lift({ // configuration for testing purposes
12 | port: 7070,
13 | environment: 'test',
14 | log: {
15 | noShip: true
16 | },
17 | models: {
18 | migrate: 'drop'
19 | },
20 | hooks: {
21 | sockets: false,
22 | pubsub: false,
23 | grunt: false //we dont need grunt in test
24 | }
25 | }, function(error, sails) {
26 | if (error) {
27 | return done(error);
28 | } else {
29 | done(null, sails);
30 | }
31 | });
32 | });
33 |
34 |
35 | /**
36 | * Lowering sails after done testing
37 | */
38 | after(function(done) {
39 | User
40 | .destroy()
41 | .then(function() {
42 | return Authentication.destroy();
43 | })
44 | .then(function( /*result*/ ) {
45 | sails.lower(done);
46 | })
47 | .catch(function(error) {
48 | done(error);
49 | });
50 | });
--------------------------------------------------------------------------------
/test/validation.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | var expect = require('chai').expect;
5 | var faker = require('faker');
6 | var email = faker.internet.email();
7 | var username = faker.internet.userName();
8 |
9 | describe('Hook#validation', function() {
10 |
11 | it('should be loaded as installable hook', function(done) {
12 | expect(sails.hooks.validation).to.not.be.null;
13 | done();
14 | });
15 |
16 | it('should be able to serialize custom errors when toJSON is invoked', function(done) {
17 | User
18 | .create({}, function(error, user) {
19 |
20 | var asJSON = error.toJSON();
21 |
22 | expect(asJSON.Errors).to.exist;
23 |
24 | done(null, user);
25 | });
26 | });
27 |
28 |
29 | it('should be able to serialize custom errors when toPOJO is invoked', function(done) {
30 | User
31 | .create({}, function(error, user) {
32 |
33 | var asPOJO = error.toPOJO();
34 |
35 | expect(asPOJO.Errors).to.exist;
36 |
37 | done(null, user);
38 | });
39 | });
40 |
41 | it('should be able to serialize custom errors when toPOJO is invoked and use custom Errors key', function(done) {
42 | sails.config.errors = {
43 | errorKey: 'customErrors'
44 | };
45 |
46 | User
47 | .create({}, function(error, user) {
48 |
49 | var asPOJO = error.toPOJO();
50 |
51 | expect(asPOJO.customErrors).to.exist;
52 |
53 | delete sails.config.errors;
54 |
55 | done(null, user);
56 | });
57 | });
58 |
59 | it('should throw custom errors', function(done) {
60 | User
61 | .create({}, function(error, user) {
62 |
63 | expect(error.Errors.email).to.exist;
64 |
65 | expect(error.Errors.email[0].message)
66 | .to.equal(User.validationMessages.email.required);
67 | expect(error.Errors.email[1].message)
68 | .to.equal(User.validationMessages.email.email);
69 |
70 | expect(error.Errors.username).to.exist;
71 | expect(error.Errors.username[0].message)
72 | .to.equal(User.validationMessages.username.required);
73 |
74 | expect(error.Errors.birthday).to.exist;
75 |
76 | expect(error.Errors.birthday[0].message)
77 | .to.equal(User.validationMessages.birthday.required);
78 | expect(error.Errors.birthday[1].message)
79 | .to.equal(User.validationMessages.birthday.date);
80 |
81 | done(null, user);
82 | });
83 | });
84 |
85 | it('should throw error when the error message or field key is not specificied', function(done) {
86 |
87 | User
88 | .create({
89 | email: email,
90 | username: username,
91 | birthday: faker.date.past(),
92 | nickname: 'c'
93 | }, function(error, user) {
94 | expect(error.Errors.nickname).to.exist;
95 | done(null, user);
96 | });
97 | });
98 |
99 | it('should not throw error if all validation conditions passed', function(done) {
100 |
101 | User
102 | .create({
103 | email: email,
104 | username: username,
105 | birthday: faker.date.past()
106 | }, function(error, user) {
107 | expect(error).to.be.null;
108 | expect(user).to.not.be.null;
109 | done();
110 | });
111 | });
112 |
113 | });
--------------------------------------------------------------------------------
/test/validation.zcreate.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | var expect = require('chai').expect;
5 | var faker = require('faker');
6 | var email = faker.internet.email();
7 | var username = faker.internet.userName();
8 |
9 |
10 | describe('Hook#validation#create database errors', function() {
11 |
12 | before(function(done) {
13 | User
14 | .create({
15 | email: email,
16 | username: username,
17 | birthday: faker.date.past()
18 | })
19 | .exec(done);
20 | });
21 |
22 | it('should throw unique error message using node callback style', function(done) {
23 |
24 | User
25 | .create({
26 | email: email,
27 | username: username,
28 | birthday: faker.date.past()
29 | }, function(error, user) {
30 | expect(error.Errors.email).to.exist;
31 |
32 | expect(error.Errors.email[0].message)
33 | .to.equal(User.validationMessages.email.unique);
34 |
35 | done(null, user);
36 | });
37 | });
38 |
39 | it('should throw unique error message using deferred style', function(done) {
40 |
41 | User
42 | .create({
43 | email: email,
44 | username: username,
45 | birthday: faker.date.past()
46 | })
47 | .exec(function(error, user) {
48 | expect(error.Errors.email).to.exist;
49 |
50 | expect(error.Errors.email[0].message)
51 | .to.equal(User.validationMessages.email.unique);
52 |
53 | done(null, user);
54 | });
55 | });
56 |
57 |
58 | it('should throw unique error message using promise style', function(done) {
59 |
60 | User
61 | .create({
62 | email: email,
63 | username: username,
64 | birthday: faker.date.past()
65 | })
66 | .catch(function(error) {
67 | expect(error.Errors.email).to.exist;
68 |
69 | expect(error.Errors.email[0].message)
70 | .to.equal(User.validationMessages.email.unique);
71 |
72 | done();
73 | });
74 | });
75 |
76 | });
--------------------------------------------------------------------------------
/test/validation.zcreateEach.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | var expect = require('chai').expect;
5 | var faker = require('faker');
6 | var email = faker.internet.email();
7 | var username = faker.internet.userName();
8 |
9 | describe('Hook#validation#createEach database errors', function() {
10 |
11 | before(function(done) {
12 | User
13 | .create({
14 | email: email,
15 | username: username,
16 | birthday: faker.date.past()
17 | })
18 | .exec(done);
19 | });
20 |
21 | it('should throw unique error message using node callback style', function(done) {
22 |
23 | User
24 | .createEach([{
25 | email: faker.internet.email(),
26 | username: faker.internet.userName(),
27 | birthday: faker.date.past()
28 | }, {
29 | email: email,
30 | username: username,
31 | birthday: faker.date.past()
32 | }], function(error, users) {
33 | expect(error.Errors.email).to.exist;
34 |
35 | expect(error.Errors.email[0].message)
36 | .to.equal(User.validationMessages.email.unique);
37 |
38 | done(null, users);
39 | });
40 | });
41 |
42 | it('should throw unique error message using deferred style', function(done) {
43 |
44 | User
45 | .createEach([{
46 | email: faker.internet.email(),
47 | username: faker.internet.userName(),
48 | birthday: faker.date.past()
49 | }, {
50 | email: email,
51 | username: username,
52 | birthday: faker.date.past()
53 | }])
54 | .exec(function(error, users) {
55 | expect(error.Errors.email).to.exist;
56 |
57 | expect(error.Errors.email[0].message)
58 | .to.equal(User.validationMessages.email.unique);
59 |
60 | done(null, users);
61 | });
62 | });
63 |
64 | it('should throw unique error message using promise style', function(done) {
65 |
66 | User
67 | .createEach([{
68 | email: faker.internet.email(),
69 | username: faker.internet.userName(),
70 | birthday: faker.date.past()
71 | }, {
72 | email: email,
73 | username: username,
74 | birthday: faker.date.past()
75 | }])
76 | .catch(function(error) {
77 | expect(error.Errors.email).to.exist;
78 |
79 | expect(error.Errors.email[0].message)
80 | .to.equal(User.validationMessages.email.unique);
81 |
82 | done();
83 | });
84 | });
85 |
86 | });
--------------------------------------------------------------------------------
/test/validation.zfindOrCreate.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | var expect = require('chai').expect;
5 | var faker = require('faker');
6 | var email = faker.internet.email();
7 | var username = faker.internet.userName();
8 |
9 | describe('Hook#validation#findOrCreate database errors', function() {
10 |
11 | before(function(done) {
12 | User
13 | .create({
14 | email: email,
15 | username: username,
16 | birthday: faker.date.past()
17 | })
18 | .exec(done);
19 | });
20 |
21 | it('should throw unique error message using node callback style', function(done) {
22 |
23 | User
24 | .findOrCreate({
25 | email: faker.internet.email()
26 | }, {
27 | email: email,
28 | username: username,
29 | birthday: faker.date.past()
30 | }, function(error, user) {
31 | expect(error.Errors.email).to.exist;
32 |
33 | expect(error.Errors.email[0].message)
34 | .to.equal(User.validationMessages.email.unique);
35 |
36 | done(null, user);
37 | });
38 | });
39 |
40 |
41 | it('should throw unique error message using deferred style', function(done) {
42 |
43 | User
44 | .findOrCreate({
45 | email: faker.internet.email()
46 | }, {
47 | email: email,
48 | username: username,
49 | birthday: faker.date.past()
50 | })
51 | .exec(function(error, user) {
52 | expect(error.Errors.email).to.exist;
53 |
54 | expect(error.Errors.email[0].message)
55 | .to.equal(User.validationMessages.email.unique);
56 |
57 | done(null, user);
58 | });
59 | });
60 |
61 | it('should throw unique error message using promise style', function(done) {
62 |
63 | User
64 | .findOrCreate({
65 | email: faker.internet.email()
66 | }, {
67 | email: email,
68 | username: username,
69 | birthday: faker.date.past()
70 | })
71 | .catch(function(error) {
72 | expect(error.Errors.email).to.exist;
73 |
74 | expect(error.Errors.email[0].message)
75 | .to.equal(User.validationMessages.email.unique);
76 |
77 | done();
78 | });
79 | });
80 |
81 |
82 | });
--------------------------------------------------------------------------------
/test/validation.zi18n.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | var path = require('path');
5 | var expect = require('chai').expect;
6 | var request = require('supertest');
7 |
8 | //load messages from locales
9 | var enMessages = require(path.join(__dirname, '..', 'config', 'locales', 'en.json'));
10 | var swMessages = require(path.join(__dirname, '..', 'config', 'locales', 'sw.json'));
11 |
12 | describe('Hook#validation#i18n', function() {
13 |
14 | it('should throw custom errors messages from default locales', function(done) {
15 | Authentication
16 | .create({}, function(error, user) {
17 |
18 | expect(error.Errors.email).to.exist;
19 |
20 | expect(error.Errors.email[0].message)
21 | .to.equal(enMessages['authentication.email.email']);
22 |
23 | expect(error.Errors.email[1].message)
24 | .to.equal(enMessages['authentication.email.required']);
25 |
26 |
27 | expect(error.Errors.username).to.exist;
28 |
29 | expect(error.Errors.username[0].message)
30 | .to.equal(enMessages['authentication.username.string']);
31 |
32 | expect(error.Errors.username[1].message)
33 | .to.equal(enMessages['authentication.username.required']);
34 |
35 | expect(error.Errors.birthday).to.exist;
36 |
37 | expect(error.Errors.birthday[0].message)
38 | .to.equal(enMessages['authentication.birthday.date']);
39 | expect(error.Errors.birthday[1].message)
40 | .to.equal(enMessages['authentication.birthday.required']);
41 |
42 | done(null, user);
43 | });
44 | });
45 |
46 |
47 | it('should throw custom errors messages from request locales', function(done) {
48 | sails.config.i18n.requestLocale = 'sw';
49 |
50 | Authentication
51 | .create({}, function(error, user) {
52 |
53 | expect(error.Errors.email).to.exist;
54 |
55 | expect(error.Errors.email[0].message)
56 | .to.equal(swMessages['authentication.email.email']);
57 |
58 | expect(error.Errors.email[1].message)
59 | .to.equal(swMessages['authentication.email.required']);
60 |
61 |
62 | expect(error.Errors.username).to.exist;
63 |
64 | expect(error.Errors.username[0].message)
65 | .to.equal(swMessages['authentication.username.string']);
66 |
67 | expect(error.Errors.username[1].message)
68 | .to.equal(swMessages['authentication.username.required']);
69 |
70 | expect(error.Errors.birthday).to.exist;
71 |
72 | expect(error.Errors.birthday[0].message)
73 | .to.equal(swMessages['authentication.birthday.date']);
74 | expect(error.Errors.birthday[1].message)
75 | .to.equal(swMessages['authentication.birthday.required']);
76 |
77 | done(null, user);
78 | });
79 | });
80 |
81 |
82 | it('should respond with appropriate error using default locale', function(done) {
83 | var authentication = {};
84 | request(sails.hooks.http.app)
85 | .post('/authentication/create')
86 | .set('Content-Type', 'application/json')
87 | .set('Accept', 'application/json')
88 | .send(authentication)
89 | .end(function(error, response) {
90 |
91 | var _error = JSON.parse(response.text);
92 |
93 | expect(response.status).to.equal(400);
94 |
95 | expect(_error.email).to.exist;
96 |
97 | expect(_error.email[0].message)
98 | .to.equal(enMessages['authentication.email.email']);
99 |
100 | expect(_error.email[1].message)
101 | .to.equal(enMessages['authentication.email.required']);
102 |
103 |
104 | expect(_error.username).to.exist;
105 |
106 | expect(_error.username[0].message)
107 | .to.equal(enMessages['authentication.username.string']);
108 |
109 | expect(_error.username[1].message)
110 | .to.equal(enMessages['authentication.username.required']);
111 |
112 | expect(_error.birthday).to.exist;
113 |
114 | expect(_error.birthday[0].message)
115 | .to.equal(enMessages['authentication.birthday.date']);
116 | expect(_error.birthday[1].message)
117 | .to.equal(enMessages['authentication.birthday.required']);
118 |
119 | done();
120 | });
121 | });
122 |
123 |
124 | it('should respond with appropriate error using request locale', function(done) {
125 | var authentication = {};
126 | request(sails.hooks.http.app)
127 | .post('/authentication/create')
128 | .set('Content-Type', 'application/json')
129 | .set('Accept', 'application/json')
130 | .set('Accept-Language', 'sw;q=0.8')
131 | .send(authentication)
132 | .end(function(error, response) {
133 |
134 | var _error = JSON.parse(response.text);
135 |
136 | expect(response.status).to.equal(400);
137 |
138 | expect(_error.email).to.exist;
139 |
140 | expect(_error.email[0].message)
141 | .to.equal(swMessages['authentication.email.email']);
142 |
143 | expect(_error.email[1].message)
144 | .to.equal(swMessages['authentication.email.required']);
145 |
146 |
147 | expect(_error.username).to.exist;
148 |
149 | expect(_error.username[0].message)
150 | .to.equal(swMessages['authentication.username.string']);
151 |
152 | expect(_error.username[1].message)
153 | .to.equal(swMessages['authentication.username.required']);
154 |
155 | expect(_error.birthday).to.exist;
156 |
157 | expect(_error.birthday[0].message)
158 | .to.equal(swMessages['authentication.birthday.date']);
159 | expect(_error.birthday[1].message)
160 | .to.equal(swMessages['authentication.birthday.required']);
161 |
162 | done();
163 | });
164 | });
165 |
166 | });
--------------------------------------------------------------------------------
/test/validation.zupdate.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //dependencies
4 | var expect = require('chai').expect;
5 | var faker = require('faker');
6 |
7 | describe('Hook#validation#update database errors', function() {
8 | var _email = faker.internet.email();
9 | var __email = faker.internet.email();
10 |
11 | before(function(done) {
12 | User
13 | .createEach([{
14 | email: _email,
15 | username: faker.internet.userName(),
16 | birthday: faker.date.past()
17 | }, {
18 | email: __email,
19 | username: faker.internet.userName(),
20 | birthday: faker.date.past()
21 | }])
22 | .exec(done);
23 | });
24 |
25 | it('should throw unique error message using node callback style', function(done) {
26 |
27 | User
28 | .update({
29 | email: __email
30 | }, {
31 | email: _email
32 | }, function(error, user) {
33 | expect(error.Errors.email).to.exist;
34 |
35 | expect(error.Errors.email[0].message)
36 | .to.equal(User.validationMessages.email.unique);
37 |
38 | done(null, user);
39 | });
40 | });
41 |
42 | it('should throw unique error message using deferred style', function(done) {
43 |
44 | User
45 | .update({
46 | email: __email
47 | }, {
48 | email: _email
49 | })
50 | .exec(function(error, user) {
51 | expect(error.Errors.email).to.exist;
52 |
53 | expect(error.Errors.email[0].message)
54 | .to.equal(User.validationMessages.email.unique);
55 |
56 | done(null, user);
57 | });
58 | });
59 |
60 | it('should throw unique error message using promise style', function(done) {
61 |
62 | User
63 | .update({
64 | email: __email
65 | }, {
66 | email: _email
67 | })
68 | .catch(function(error) {
69 | expect(error.Errors.email).to.exist;
70 |
71 | expect(error.Errors.email[0].message)
72 | .to.equal(User.validationMessages.email.unique);
73 |
74 | done();
75 | });
76 | });
77 |
78 | });
--------------------------------------------------------------------------------