Skip to content

Commit ec5bc97

Browse files
Merge pull request #134 from RobinBuschmann/issue-109
closes #109
2 parents f71072c + 8ea4142 commit ec5bc97

File tree

4 files changed

+152
-4
lines changed

4 files changed

+152
-4
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import 'reflect-metadata';
2+
import {addOptions} from "../../services/models";
3+
4+
export const Validator: MethodDecorator = (target: Object,
5+
propertyName: string,
6+
descriptor: TypedPropertyDescriptor<any>) => {
7+
addOptions(target, {
8+
validate: {
9+
[propertyName]: descriptor.value
10+
}
11+
});
12+
};

lib/services/models.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,10 @@ export function addOptions(target: any, options: DefineOptions<any>): void {
149149
if (!_options) {
150150
_options = {};
151151
}
152-
153-
setOptions(target, {..._options, ...options});
152+
setOptions(target, {..._options, ...options, validate: {
153+
...(_options.validate || {}),
154+
...(options.validate || {}),
155+
}});
154156
}
155157

156158
/**

test/specs/validation.spec.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {Table} from "../../lib/annotations/Table";
1515
import {Column} from "../../lib/annotations/Column";
1616
import {Length} from "../../lib/annotations/validation/Length";
1717
import {NotEmpty} from "../../lib/annotations/validation/NotEmpty";
18+
import {Validator} from '../../lib/annotations/validation/Validator';
1819

1920
use(chaiAsPromised);
2021

@@ -209,6 +210,7 @@ describe('validation', () => {
209210
class User extends Model<User> {
210211
@Length({min: 0, max: 5}) @Column name: string;
211212
}
213+
212214
const sequelizeValidationOnly = createSequelizeValidationOnly(false);
213215
sequelizeValidationOnly.addModels([User]);
214216
const user = new User({name: 'elisa'});
@@ -225,6 +227,7 @@ describe('validation', () => {
225227
class User extends Model<User> {
226228
@Length({min: 0, max: 5}) @Column name: string;
227229
}
230+
228231
const sequelizeValidationOnly = createSequelizeValidationOnly(false);
229232
sequelizeValidationOnly.addModels([User]);
230233
const user = new User({name: 'elisa tree'});
@@ -241,6 +244,7 @@ describe('validation', () => {
241244
class User extends Model<User> {
242245
@Length({min: 5, max: 5}) @Column name: string;
243246
}
247+
244248
const sequelizeValidationOnly = createSequelizeValidationOnly(false);
245249
sequelizeValidationOnly.addModels([User]);
246250
const user = new User({name: 'elli'});
@@ -257,6 +261,7 @@ describe('validation', () => {
257261
class User extends Model<User> {
258262
@Length({max: 5}) @Column name: string;
259263
}
264+
260265
const sequelizeValidationOnly = createSequelizeValidationOnly(false);
261266
sequelizeValidationOnly.addModels([User]);
262267
const user = new User({name: 'elisa'});
@@ -273,6 +278,7 @@ describe('validation', () => {
273278
class User extends Model<User> {
274279
@Length({max: 5}) @Column name: string;
275280
}
281+
276282
const sequelizeValidationOnly = createSequelizeValidationOnly(false);
277283
sequelizeValidationOnly.addModels([User]);
278284
const user = new User({name: 'elisa tree'});
@@ -289,6 +295,7 @@ describe('validation', () => {
289295
class User extends Model<User> {
290296
@Length({min: 4}) @Column name: string;
291297
}
298+
292299
const sequelizeValidationOnly = createSequelizeValidationOnly(false);
293300
sequelizeValidationOnly.addModels([User]);
294301
const user = new User({name: 'elisa'});
@@ -305,6 +312,7 @@ describe('validation', () => {
305312
class User extends Model<User> {
306313
@Length({min: 5}) @Column name: string;
307314
}
315+
308316
const sequelizeValidationOnly = createSequelizeValidationOnly(false);
309317
sequelizeValidationOnly.addModels([User]);
310318
const user = new User({name: 'elli'});
@@ -325,6 +333,7 @@ describe('validation', () => {
325333
class User extends Model<User> {
326334
@NotEmpty @Column name: string;
327335
}
336+
328337
const sequelizeValidationOnly = createSequelizeValidationOnly(false);
329338
sequelizeValidationOnly.addModels([User]);
330339
const user = new User({name: 'elisa'});
@@ -341,6 +350,7 @@ describe('validation', () => {
341350
class User extends Model<User> {
342351
@NotEmpty @Column name: string;
343352
}
353+
344354
const sequelizeValidationOnly = createSequelizeValidationOnly(false);
345355
sequelizeValidationOnly.addModels([User]);
346356
const user = new User({name: ''});
@@ -357,6 +367,7 @@ describe('validation', () => {
357367
class User extends Model<User> {
358368
@NotEmpty({msg: 'NotEmpty'}) @Column name: string;
359369
}
370+
360371
const sequelizeValidationOnly = createSequelizeValidationOnly(false);
361372
sequelizeValidationOnly.addModels([User]);
362373
const user = new User({name: 'elisa'});
@@ -373,6 +384,7 @@ describe('validation', () => {
373384
class User extends Model<User> {
374385
@NotEmpty({msg: 'NotEmpty'}) @Column name: string;
375386
}
387+
376388
const sequelizeValidationOnly = createSequelizeValidationOnly(false);
377389
sequelizeValidationOnly.addModels([User]);
378390
const user = new User({name: ''});
@@ -385,6 +397,114 @@ describe('validation', () => {
385397
});
386398
});
387399

400+
describe('Validator', () => {
401+
402+
describe('simple model, one validator', () => {
403+
404+
const VALID_NAME = 'bob';
405+
const ERROR_MESSAGE = `Invalid name: Only '${VALID_NAME}' is valid`;
406+
const _sequelize = createSequelize({modelPaths: []});
407+
408+
@Table
409+
class User extends Model<User> {
410+
@Column name: string;
411+
@Validator userValidator(): void {
412+
if (this.name !== VALID_NAME) {
413+
throw new Error(ERROR_MESSAGE);
414+
}
415+
}
416+
}
417+
418+
_sequelize.addModels([User]);
419+
420+
it('should throw', () => {
421+
const user = new User({name: 'will'});
422+
423+
if (majorVersion === 3) {
424+
return user.validate().then(err => expect(err.errors[0].message).to.eq(ERROR_MESSAGE));
425+
} else if (majorVersion === 4) {
426+
return expect(user.validate()).to.be.rejected;
427+
}
428+
});
429+
430+
it('should not throw', () => {
431+
const user = new User({name: VALID_NAME});
432+
433+
if (majorVersion === 3) {
434+
return user.validate().then(err => expect(err).to.be.null);
435+
} else if (majorVersion === 4) {
436+
return expect(user.validate()).to.be.fulfilled;
437+
}
438+
});
439+
440+
});
441+
442+
describe('simple model, multiple validators', () => {
443+
444+
const VALID_NAME = 'bob';
445+
const NAME_ERROR_MESSAGE = `Invalid name: Only '${VALID_NAME}' is valid`;
446+
const VALID_AGE = 99;
447+
const AGE_ERROR_MESSAGE = `Invalid age: Only '${VALID_AGE}' is valid`;
448+
const _sequelize = createSequelize({modelPaths: []});
449+
450+
@Table
451+
class User extends Model<User> {
452+
@Column name: string;
453+
@Column age: number;
454+
@Validator nameValidator(): void {
455+
if (this.name !== VALID_NAME) {
456+
throw new Error(NAME_ERROR_MESSAGE);
457+
}
458+
}
459+
@Validator ageValidator(): void {
460+
if (this.age !== VALID_AGE) {
461+
throw new Error(AGE_ERROR_MESSAGE);
462+
}
463+
}
464+
}
465+
466+
_sequelize.addModels([User]);
467+
468+
it('should have metadata for multiple validators', () => {
469+
const {validate} = Reflect.getMetadata('sequelize:options', User.prototype);
470+
expect(validate).to.have.property('nameValidator');
471+
expect(validate).to.have.property('ageValidator');
472+
});
473+
474+
it('should throw due to wrong name', () => {
475+
const user = new User({name: 'will', age: VALID_AGE});
476+
477+
if (majorVersion === 3) {
478+
return user.validate().then(err => expect(err.errors[0].message).to.eq(NAME_ERROR_MESSAGE));
479+
} else if (majorVersion === 4) {
480+
return expect(user.validate()).to.be.rejectedWith(NAME_ERROR_MESSAGE);
481+
}
482+
});
483+
484+
it('should throw due to wrong age', () => {
485+
const user = new User({name: VALID_NAME, age: 1});
486+
487+
if (majorVersion === 3) {
488+
return user.validate().then(err => expect(err.errors[0].message).to.eq(AGE_ERROR_MESSAGE));
489+
} else if (majorVersion === 4) {
490+
return expect(user.validate()).to.be.rejectedWith(AGE_ERROR_MESSAGE);
491+
}
492+
});
493+
494+
// it('should not throw', () => {
495+
// const user = new User({name: VALID_NAME});
496+
//
497+
// if (majorVersion === 3) {
498+
// return user.validate().then(err => expect(err).to.be.null);
499+
// } else if (majorVersion === 4) {
500+
// return expect(user.validate()).to.be.fulfilled;
501+
// }
502+
// });
503+
504+
});
505+
506+
});
507+
388508
});
389509

390510
describe('only', () => {

test/utils/sequelize.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
import {Sequelize} from "../../lib/models/Sequelize";
22
import * as OriginSequelize from "sequelize";
33
import {DefineOptions, Sequelize as SequelizeType} from "sequelize";
4+
import {SequelizeConfig} from '../../lib/types/SequelizeConfig';
45

5-
export function createSequelize(useModelsInPath: boolean = true, define: DefineOptions<any> = {}): Sequelize {
6+
export function createSequelize(partialOptions: Partial<SequelizeConfig>): Sequelize;
7+
export function createSequelize(useModelsInPath?: boolean,
8+
define?: DefineOptions<any>): Sequelize;
9+
export function createSequelize(useModelsInPathOrPartialOptions?: boolean | Partial<SequelizeConfig>,
10+
define: DefineOptions<any> = {}): Sequelize {
11+
12+
let useModelsInPath = true;
13+
let partialOptions = {};
14+
if (typeof useModelsInPathOrPartialOptions === 'object') {
15+
partialOptions = useModelsInPathOrPartialOptions;
16+
} else if (typeof useModelsInPathOrPartialOptions === 'boolean') {
17+
useModelsInPath = useModelsInPathOrPartialOptions;
18+
}
619

720
return new Sequelize({
821
database: '__',
@@ -12,7 +25,8 @@ export function createSequelize(useModelsInPath: boolean = true, define: DefineO
1225
define,
1326
storage: ':memory:',
1427
logging: !('SEQ_SILENT' in process.env),
15-
modelPaths: useModelsInPath ? [__dirname + '/../models'] : []
28+
modelPaths: useModelsInPath ? [__dirname + '/../models'] : [],
29+
...partialOptions,
1630
});
1731
}
1832

0 commit comments

Comments
 (0)