UNPKG

3.76 kBJavaScriptView Raw
1/**
2 * @copyright Copyright (c) 2019 Maxim Khorin <maksimovichu@gmail.com>
3 */
4'use strict';
5
6const Base = require('./Validator');
7
8module.exports = class RelationValidator extends Base {
9
10 constructor (config) {
11 super({
12 required: false,
13 min: null,
14 max: null,
15 unique: null,
16 allow: null, // allow changes ['links', 'unlinks', 'deletes']
17 deny: null, // deny changes ['links', 'unlinks', 'deletes']
18 filter: null, // handler(value, model, attr)
19 behavior: 'relationChange',
20 ...config
21 });
22 if (this.allow && this.deny) {
23 throw new Error('Allowed only one permission');
24 }
25 this.skipOnEmpty = false;
26 }
27
28 getMessage () {
29 return this.createMessage(this.message, 'Invalid relation request');
30 }
31
32 getRequiredMessage () {
33 return this.createMessage(this.requiredMessage, 'Value cannot be blank');
34 }
35
36 getExistMessage () {
37 return this.createMessage(this.existMessage, 'Value does not exist');
38 }
39
40 getUniqueMessage () {
41 return this.createMessage(this.uniqueMessage, 'Value has already been taken');
42 }
43
44 getTooFewMessage () {
45 return this.createMessage(this.tooFew, 'Relation should contain at least {min} objects', {
46 min: this.min
47 });
48 }
49
50 getTooManyMessage () {
51 return this.createMessage(this.tooMany, 'Relation should contain at most {max} objects', {
52 max: this.max
53 });
54 }
55
56 async validateAttr (attr, model) {
57 const behavior = model.getBehavior(this.behavior);
58 if (!behavior) {
59 throw new Error('Relation behavior not found');
60 }
61 const data = behavior.getChanges(attr);
62 if (data === false) {
63 return this.addError(model, attr, this.getMessage());
64 }
65 if (data) {
66 this.filterChanges(data);
67 }
68 if (data && this.filter) {
69 await this.filter(data, attr, model);
70 }
71 await this.checkCounter(behavior, attr, model);
72 if (this.unique !== null) {
73 await this.checkUnique(behavior, attr, model);
74 }
75 }
76
77 filterChanges (data) {
78 if (Array.isArray(this.allow)) {
79 for (const key of Object.keys(data)) {
80 if (!this.allow.includes(key)) {
81 data[key] = [];
82 }
83 }
84 }
85 if (Array.isArray(this.deny)) {
86 for (const key of Object.keys(data)) {
87 if (this.deny.includes(key)) {
88 data[key] = [];
89 }
90 }
91 }
92 }
93
94 async checkCounter (behavior, attr, model) {
95 if (!this.required && !this.min && !this.max) {
96 return;
97 }
98 const docs = await behavior.getLinkedDocs(attr);
99 if (this.required && docs.length < 1) {
100 this.addError(model, attr, this.getRequiredMessage());
101 }
102 if (this.min && docs.length < this.min) {
103 this.addError(model, attr, this.getTooFewMessage());
104 }
105 if (this.max && docs.length > this.max) {
106 this.addError(model, attr, this.getTooManyMessage());
107 }
108 }
109
110 async checkUnique (behavior, attr, model) {
111 const exist = await behavior.checkExists(attr);
112 if (this.unique === true && exist === true) {
113 this.addError(model, attr, this.getUniqueMessage());
114 }
115 if (this.unique === false && exist === false) {
116 this.addError(model, attr, this.getExistMessage());
117 }
118 }
119};
\No newline at end of file