ts-sql-query
Version:
Type-safe SQL query builder like QueryDSL or JOOQ in Java or Linq in .Net for TypeScript with MariaDB, MySql, Oracle, PostgreSql, Sqlite and SqlServer support.
336 lines (335 loc) • 14.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DynamicConditionBuilder = void 0;
const values_1 = require("../expressions/values");
const ValueSourceImpl_1 = require("../internal/ValueSourceImpl");
class DynamicConditionBuilder {
constructor(sqlBuilder, definition, extension) {
this.sqlBuilder = sqlBuilder;
this.definition = definition;
this.extension = extension;
}
withValues(filter) {
return this.processFilter(filter, this.definition, this.extension, '');
}
processFilter(filter, definition, extension, prefix) {
let result = new ValueSourceImpl_1.SqlOperationValueSourceIfValueAlwaysNoop();
if (filter === null || filter === undefined) {
return result;
}
if (typeof filter !== 'object' || filter instanceof Date) {
throw new Error('Invalid dynamic filter condition received; an object is expected. Received value: ' + filter);
}
for (const key in filter) {
const value = filter[key];
let condition;
if (key === 'and') {
if (value === null || value === undefined) {
continue;
}
if (!Array.isArray(value)) {
throw new Error('The and conjunction expect an array as value');
}
condition = this.processAndFilter(value, definition, extension, prefix);
}
else if (key === 'or') {
if (value === null || value === undefined) {
continue;
}
if (!Array.isArray(value)) {
throw new Error('The or conjunction expect an array as value');
}
condition = this.processOrFilter(value, definition, extension, prefix);
}
else if (key === 'not') {
if (value === null || value === undefined) {
continue;
}
condition = this.processFilter(value, definition, extension, prefix).negate();
}
else if (extension && typeof extension[key] === 'function') {
if (value === null || value === undefined) {
continue;
}
const extensionResult = extension[key](value);
if (!(0, values_1.isValueSource)(extensionResult)) {
const error = new Error('Invalid return type for the extension ' + prefix + key + '. Expected a boolean value source, but found ' + extensionResult + '. Processed value: ' + value);
error.key = prefix + key;
error.extensionResult = extensionResult;
error.processedValue = value;
throw error;
}
const valueSourcePrivate = (0, values_1.__getValueSourcePrivate)(extensionResult);
if (!(0, values_1.__isBooleanValueSource)(valueSourcePrivate)) {
const error = new Error('Invalid return type for the extension ' + prefix + key + '. Expected a boolean value source, but found a value source with type ' + valueSourcePrivate.__valueTypeName + '. Processed value: ' + value);
error.key = prefix + key;
error.extensionResult = extensionResult;
error.processedValue = value;
throw error;
}
condition = extensionResult;
}
else {
const column = definition[key];
if (!column) {
throw new Error('Unknown column with name "' + prefix + key + '" provided as dynamic filter condition');
}
if (value === null || value === undefined) {
continue;
}
if (typeof value !== 'object' || value instanceof Date) {
throw new Error('Invalid dynamic filter condition received for the column "' + prefix + key + '"; an object is expected. Received value: ' + value);
}
if ((0, values_1.isValueSource)(column)) {
condition = this.processColumnFilter(value, column, extension, prefix + key);
}
else if (prefix) {
condition = this.processFilter(value, column, extension ? extension[key] : undefined, prefix + ' .' + key);
}
else {
condition = this.processFilter(value, column, extension ? extension[key] : undefined, key);
}
}
result = result.and(condition);
}
return result;
}
processColumnFilter(filter, valueSource, extension, column) {
let result = new ValueSourceImpl_1.SqlOperationValueSourceIfValueAlwaysNoop();
const valueSourcePrivate = (0, values_1.__getValueSourcePrivate)(valueSource);
for (const key in filter) {
const value = filter[key];
if (extension && typeof extension[key] === 'function') {
if (value === null || value === undefined) {
continue;
}
const extensionResult = extension[key](value);
if (!(0, values_1.isValueSource)(extensionResult)) {
const error = new Error('Invalid return type for the rule ' + key + ' at ' + column + '. Expected a boolean value source, but found ' + extensionResult + '. Processed value: ' + value);
error.path = column;
error.rule = key;
error.extensionResult = extensionResult;
error.processedValue = value;
throw error;
}
const valueSourcePrivate = (0, values_1.__getValueSourcePrivate)(extensionResult);
if (!(0, values_1.__isBooleanValueSource)(valueSourcePrivate)) {
const error = new Error('Invalid return type for the rule ' + key + ' at ' + column + '. Expected a boolean value source, but found a value source with type ' + valueSourcePrivate.__valueTypeName + '. Processed value: ' + value);
error.path = column;
error.rule = key;
error.extensionResult = extensionResult;
error.processedValue = value;
throw error;
}
let condition = extensionResult;
result = result.and(condition);
continue;
}
if (extension && extension[key]) { // This allow to process additional inner properties in a value, allowing make the definition general (with no stop)
if (value === null || value === undefined) {
continue;
}
let condition = this.processAdditionalColumnFilter(filter, extension[key], column + '.' + key);
result = result.and(condition);
continue;
}
if (allowedOpreations[key] !== true || valueSourcePrivate.__aggregatedArrayColumns) { // keep the strict true comparison to avoid false positives
// aggregated arrays doesn't allows to use any operation
throw new Error('Invalid operation with name "' + key + '" for the column "' + column + '" provided as dynamic filter condition');
}
if (!this.sqlBuilder._isValue(value)) {
if (key !== 'is' && key !== 'isNot' && !key.endsWith('IfValue')) {
continue;
}
}
let condition;
if (key === 'isNull' || key === 'isNotNull') {
condition = valueSource[key]();
if (!value) {
condition = condition.negate();
}
}
else {
if ((0, values_1.__isUuidValueSource)(valueSourcePrivate) && useAsStringInUuid[key]) {
condition = valueSource.asString()[key](value);
}
else {
condition = valueSource[key](value);
}
}
result = result.and(condition);
}
return result;
}
processAndFilter(filter, definition, extension, prefix) {
let result = new ValueSourceImpl_1.SqlOperationValueSourceIfValueAlwaysNoop();
for (let i = 0, length = filter.length; i < length; i++) {
const condition = this.processFilter(filter[i], definition, extension, prefix);
result = result.and(condition);
}
return result;
}
processOrFilter(filter, definition, extension, prefix) {
let result = new ValueSourceImpl_1.SqlOperationValueSourceIfValueAlwaysNoop();
for (let i = 0, length = filter.length; i < length; i++) {
const condition = this.processFilter(filter[i], definition, extension, prefix);
result = result.or(condition);
}
return result;
}
processAdditionalColumnFilter(filter, extension, path) {
let result = new ValueSourceImpl_1.SqlOperationValueSourceIfValueAlwaysNoop();
for (const key in filter) {
const value = filter[key];
if (extension && typeof extension[key] === 'function') {
if (value === null || value === undefined) {
continue;
}
const extensionResult = extension[key](value);
if (!(0, values_1.isValueSource)(extensionResult)) {
const error = new Error('Invalid return type for the rule ' + key + ' at ' + path + '. Expected a boolean value source, but found ' + extensionResult + '. Processed value: ' + value);
error.path = path;
error.rule = key;
error.extensionResult = extensionResult;
error.processedValue = value;
throw error;
}
const valueSourcePrivate = (0, values_1.__getValueSourcePrivate)(extensionResult);
if (!(0, values_1.__isBooleanValueSource)(valueSourcePrivate)) {
const error = new Error('Invalid return type for the rule ' + key + ' at ' + path + '. Expected a boolean value source, but found a value source with type ' + valueSourcePrivate.__valueTypeName + '. Processed value: ' + value);
error.path = path;
error.rule = key;
error.extensionResult = extensionResult;
error.processedValue = value;
throw error;
}
let condition = extensionResult;
result = result.and(condition);
continue;
}
if (extension && extension[key]) {
if (value === null || value === undefined) {
continue;
}
let condition = this.processAdditionalColumnFilter(filter, extension[key], path + '.' + key);
result = result.and(condition);
continue;
}
}
return result;
}
}
exports.DynamicConditionBuilder = DynamicConditionBuilder;
const allowedOpreations = {
isNull: true,
isNotNull: true,
equalsIfValue: true,
equals: true,
notEqualsIfValue: true,
notEquals: true,
isIfValue: true,
is: true,
isNotIfValue: true,
isNot: true,
inIfValue: true,
in: true,
notInIfValue: true,
notIn: true,
lessThanIfValue: true,
lessThan: true,
greaterThanIfValue: true,
greaterThan: true,
lessOrEqualsIfValue: true,
lessOrEquals: true,
greaterOrEqualsIfValue: true,
greaterOrEquals: true,
/** @deprecated use lessThanIfValue instead */
smallerIfValue: true,
/** @deprecated use lessThan instead */
smaller: true,
/** @deprecated use greaterThanIfValue instead */
largerIfValue: true,
/** @deprecated use greaterThan instead */
larger: true,
/** @deprecated use lessOrEqualsIfValue instead */
smallAsIfValue: true,
/** @deprecated use lessOrEquals instead */
smallAs: true,
/** @deprecated use greaterOrEqualsIfValue instead */
largeAsIfValue: true,
/** @deprecated use greaterOrEquals instead */
largeAs: true,
equalsInsensitiveIfValue: true,
equalsInsensitive: true,
notEqualsInsensitiveIfValue: true,
notEqualsInsensitive: true,
likeIfValue: true,
like: true,
notLikeIfValue: true,
notLike: true,
likeInsensitiveIfValue: true,
likeInsensitive: true,
notLikeInsensitiveIfValue: true,
notLikeInsensitive: true,
startsWithIfValue: true,
startsWith: true,
notStartsWithIfValue: true,
notStartsWith: true,
endsWithIfValue: true,
endsWith: true,
notEndsWithIfValue: true,
notEndsWith: true,
startsWithInsensitiveIfValue: true,
startsWithInsensitive: true,
notStartsWithInsensitiveIfValue: true,
notStartsWithInsensitive: true,
endsWithInsensitiveIfValue: true,
endsWithInsensitive: true,
notEndsWithInsensitiveIfValue: true,
notEndsWithInsensitive: true,
containsIfValue: true,
contains: true,
notContainsIfValue: true,
notContains: true,
containsInsensitiveIfValue: true,
containsInsensitive: true,
notContainsInsensitiveIfValue: true,
notContainsInsensitive: true
};
const useAsStringInUuid = {
equalsInsensitiveIfValue: true,
equalsInsensitive: true,
notEqualsInsensitiveIfValue: true,
likeIfValue: true,
like: true,
notLikeIfValue: true,
notLike: true,
likeInsensitiveIfValue: true,
likeInsensitive: true,
notLikeInsensitiveIfValue: true,
notLikeInsensitive: true,
startsWithIfValue: true,
startsWith: true,
notStartsWithIfValue: true,
notStartsWith: true,
endsWithIfValue: true,
endsWith: true,
notEndsWithIfValue: true,
notEndsWith: true,
startsWithInsensitiveIfValue: true,
startsWithInsensitive: true,
notStartsWithInsensitiveIfValue: true,
notStartsWithInsensitive: true,
endsWithInsensitiveIfValue: true,
endsWithInsensitive: true,
notEndsWithInsensitiveIfValue: true,
notEndsWithInsensitive: true,
containsIfValue: true,
contains: true,
notContainsIfValue: true,
notContains: true,
containsInsensitiveIfValue: true,
containsInsensitive: true,
notContainsInsensitiveIfValue: true,
notContainsInsensitive: true
};