UNPKG

7.24 kBJavaScriptView Raw
1"use strict";
2/*
3 * @adonisjs/lucid
4 *
5 * (c) Harminder Virk <virk@adonisjs.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10Object.defineProperty(exports, "__esModule", { value: true });
11exports.extendValidator = void 0;
12const luxon_1 = require("luxon");
13const utils_1 = require("@poppinss/utils");
14/**
15 * Checks for database rows for `exists` and `unique` rule.
16 */
17class DbRowCheck {
18 constructor(ruleName, database, helpers) {
19 Object.defineProperty(this, "ruleName", {
20 enumerable: true,
21 configurable: true,
22 writable: true,
23 value: ruleName
24 });
25 Object.defineProperty(this, "database", {
26 enumerable: true,
27 configurable: true,
28 writable: true,
29 value: database
30 });
31 Object.defineProperty(this, "helpers", {
32 enumerable: true,
33 configurable: true,
34 writable: true,
35 value: helpers
36 });
37 }
38 /**
39 * Applies user defined where constraints on the query builder
40 */
41 applyWhere(query, constraints, refs) {
42 if (!constraints.length) {
43 return;
44 }
45 constraints.forEach(({ key, operator, value, ref }) => {
46 const val = ref ? refs[ref].value : value;
47 if (operator === 'in') {
48 query.whereIn(key, val);
49 }
50 else {
51 query.where(key, val);
52 }
53 });
54 }
55 /**
56 * Applies user defined where not constraints on the query builder
57 */
58 applyWhereNot(query, constraints, refs) {
59 if (!constraints.length) {
60 return;
61 }
62 constraints.forEach(({ key, operator, value, ref }) => {
63 const val = ref ? refs[ref].value : value;
64 if (operator === 'in') {
65 query.whereNotIn(key, val);
66 }
67 else {
68 query.whereNot(key, val);
69 }
70 });
71 }
72 /**
73 * Normalizes constraints
74 */
75 normalizeConstraints(constraints) {
76 const normalized = [];
77 if (!constraints) {
78 return normalized;
79 }
80 /**
81 * Normalize object into an array of objects
82 */
83 return Object.keys(constraints).reduce((result, key) => {
84 const value = constraints[key];
85 if (this.helpers.isRef(value)) {
86 result.push({ key, ref: value.key, operator: Array.isArray(value.value) ? 'in' : 'eq' });
87 }
88 else {
89 result.push({ key, value, operator: Array.isArray(value) ? 'in' : 'eq' });
90 }
91 return result;
92 }, normalized);
93 }
94 /**
95 * Compile validation options
96 */
97 compile(options) {
98 /**
99 * Ensure options are defined with table and column name
100 */
101 if (!options || !options.table || !options.column) {
102 throw new utils_1.Exception(`"${this.ruleName}" rule expects a "table" and a "column" name`);
103 }
104 /**
105 * Emit warning
106 */
107 if (options.constraints) {
108 process.emitWarning('DeprecationWarning', '"options.constraints" have been depreciated. Use "options.where" instead.');
109 }
110 return {
111 table: options.table,
112 column: options.column,
113 caseInsensitive: !!options.caseInsensitive,
114 connection: options.connection,
115 dateFormat: options.dateFormat,
116 where: this.normalizeConstraints(options.where || options.constraints),
117 whereNot: this.normalizeConstraints(options.whereNot),
118 };
119 }
120 /**
121 * Validate value
122 */
123 async validate(value, { table, column, where, whereNot, connection, caseInsensitive, dateFormat }, { pointer, errorReporter, arrayExpressionPointer, refs }) {
124 const client = this.database.connection(connection);
125 const query = client.from(table).select(1);
126 /**
127 * Convert datetime to a string
128 */
129 if (luxon_1.DateTime.isDateTime(value)) {
130 const format = dateFormat || client.dialect.dateTimeFormat;
131 value = value.toFormat(format);
132 }
133 /**
134 * https://www.sqlite.org/lang_corefunc.html#lower
135 * https://docs.aws.amazon.com/redshift/latest/dg/r_LOWER.html
136 * https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_lower
137 * https://www.postgresql.org/docs/9.1/functions-string.html
138 * https://docs.microsoft.com/en-us/sql/t-sql/functions/lower-transact-sql?view=sql-server-ver15
139 * https://coderwall.com/p/6yhsuq/improve-case-insensitive-queries-in-postgres-using-smarter-indexes
140 */
141 if (caseInsensitive) {
142 query.whereRaw(`lower(${column}) = ?`, [this.database.raw(`lower(?)`, [value])]);
143 }
144 else {
145 query.where(column, value);
146 }
147 this.applyWhere(query, where, refs);
148 this.applyWhereNot(query, whereNot, refs);
149 const row = await query.first();
150 if (this.ruleName === 'exists') {
151 if (!row) {
152 errorReporter.report(pointer, this.ruleName, `${this.ruleName} validation failure`, arrayExpressionPointer);
153 }
154 return;
155 }
156 if (this.ruleName === 'unique') {
157 if (row) {
158 errorReporter.report(pointer, this.ruleName, `${this.ruleName} validation failure`, arrayExpressionPointer);
159 }
160 return;
161 }
162 }
163}
164/**
165 * Extends the validator by adding `unique` and `exists`
166 */
167function extendValidator(validator, database, logger) {
168 /**
169 * Exists rule to ensure the value exists in the database
170 */
171 const existsChecker = new DbRowCheck('exists', database, validator.helpers);
172 validator.rule('exists', async (value, compiledOptions, options) => {
173 try {
174 await existsChecker.validate(value, compiledOptions, options);
175 }
176 catch (error) {
177 logger.fatal({ err: error }, '"exists" validation rule failed');
178 options.errorReporter.report(options.pointer, 'exists', 'exists validation failure', options.arrayExpressionPointer);
179 }
180 }, (options) => {
181 return {
182 compiledOptions: existsChecker.compile(options[0]),
183 async: true,
184 };
185 });
186 /**
187 * Unique rule to check if value is unique or not
188 */
189 const uniqueChecker = new DbRowCheck('unique', database, validator.helpers);
190 validator.rule('unique', async (value, compiledOptions, options) => {
191 try {
192 await uniqueChecker.validate(value, compiledOptions, options);
193 }
194 catch (error) {
195 logger.fatal({ err: error }, '"unique" validation rule failed');
196 options.errorReporter.report(options.pointer, 'unique', 'unique validation failure', options.arrayExpressionPointer);
197 }
198 }, (options) => {
199 return {
200 compiledOptions: uniqueChecker.compile(options[0]),
201 async: true,
202 };
203 });
204}
205exports.extendValidator = extendValidator;