import { AttributeOption, ConditionBuilderOption } from "./options";
import {
Model,
ModelErrorDefinition,
required,
sf,
} from "@decaf-ts/decorator-validation";
import { GroupOperator, Operator } from "./constants";
import { QueryError } from "./errors";
/**
* @summary Condition Class
* @description Represents a logical condition
*
* @param {string | Condition} attr1
* @param {Operator | GroupOperator} operator
* @param {string | Condition} comparison
*
* @class Condition
* @implements Executor
*
* @category Query
* @subcategory Conditions
*/
export class Condition extends Model {
@required()
protected attr1?: string | Condition = undefined;
@required()
protected operator?: Operator | GroupOperator = undefined;
@required()
protected comparison?: any = undefined;
private constructor(
attr1: string | Condition,
operator: Operator | GroupOperator,
comparison: any
) {
super();
this.attr1 = attr1;
this.operator = operator;
this.comparison = comparison;
}
/**
* @summary Joins 2 {@link Condition}s on an {@link Operator#AND} operation
* @param {Condition} condition
*/
and(condition: Condition): Condition {
return Condition.and(this, condition);
}
/**
* @summary Joins 2 {@link Condition}s on an {@link Operator#OR} operation
* @param {Condition} condition
*/
or(condition: Condition): Condition {
return Condition.or(this, condition);
}
/**
* @summary excludes a valut from the result
* @param val
*/
not(val: any): Condition {
return new Condition(this, Operator.NOT, val);
}
/**
* @inheritDoc
*/
hasErrors(...exceptions: string[]): ModelErrorDefinition | undefined {
const errors = super.hasErrors(...exceptions);
if (errors) return errors;
if (typeof this.attr1 === "string") {
if (this.comparison instanceof Condition)
return {
comparison: {
condition: "Both sides of the comparison must be of the same type",
},
} as ModelErrorDefinition;
if (Object.values(Operator).indexOf(this.operator as Operator) === -1)
return {
operator: {
condition: sf("Invalid operator {0}", this.operator as string),
},
} as ModelErrorDefinition;
}
if (this.attr1 instanceof Condition) {
if (
!(this.comparison instanceof Condition) &&
this.operator !== Operator.NOT
)
return {
comparison: {
condition: sf("Invalid operator {0}", this.operator as string),
},
} as ModelErrorDefinition;
if (
Object.values(GroupOperator).indexOf(this.operator as GroupOperator) ===
-1 &&
this.operator !== Operator.NOT
)
return {
operator: {
condition: sf("Invalid operator {0}", this.operator as string),
},
} as ModelErrorDefinition;
// if (this.operator !== Operator.NOT && typeof this.attr1.attr1 !== "string")
// return {
// attr1: {
// condition: stringFormat("Parent condition attribute must be a string")
// }
// } as ModelErrorDefinition
}
}
/**
* @summary Joins 2 {@link Condition}s on an {@link Operator#AND} operation
* @param {Condition} condition1
* @param {Condition} condition2
*/
static and(condition1: Condition, condition2: Condition): Condition {
return Condition.group(condition1, GroupOperator.AND, condition2);
}
/**
* @summary Joins 2 {@link Condition}s on an {@link Operator#OR} operation
* @param {Condition} condition1
* @param {Condition} condition2
*/
static or(condition1: Condition, condition2: Condition): Condition {
return Condition.group(condition1, GroupOperator.OR, condition2);
}
/**
* @summary Groups 2 {@link Condition}s by the specified {@link GroupOperator}
* @param {Condition} condition1
* @param {GroupOperator} operator
* @param {Condition} condition2
*/
private static group(
condition1: Condition,
operator: GroupOperator,
condition2: Condition
): Condition {
return new Condition(condition1, operator, condition2);
}
static attribute(attr: string) {
return new Condition.Builder().attribute(attr);
}
/**
* @summary Condition Builder Class
* @description provides a simple API to build {@link Condition}s
*
* @class ConditionBuilder
* @implements Builder
* @implements AttributeOption
*
* @category Query
* @subcategory Conditions
*/
private static Builder = class ConditionBuilder
implements ConditionBuilderOption, AttributeOption
{
attr1?: string | Condition = undefined;
operator?: Operator | GroupOperator = undefined;
comparison?: any = undefined;
/**
* @inheritDoc
*/
attribute(attr: string): AttributeOption {
this.attr1 = attr;
return this;
}
/**
* @summary Creates an Equality Comparison
* @param {any} val
*/
eq(val: any) {
return this.setOp(Operator.EQUAL, val);
}
/**
* @summary Creates a Different Comparison
* @param {any} val
*/
dif(val: any) {
return this.setOp(Operator.DIFFERENT, val);
}
/**
* @summary Creates a Greater Than Comparison
* @param {any} val
*/
gt(val: any) {
return this.setOp(Operator.BIGGER, val);
}
/**
* @summary Creates a Lower Than Comparison
* @param {any} val
*/
lt(val: any) {
return this.setOp(Operator.SMALLER, val);
}
/**
* @summary Creates a Greater or Equal to Comparison
* @param {any} val
*/
gte(val: any) {
return this.setOp(Operator.BIGGER_EQ, val);
}
/**
* @summary Creates a Lower or Equal to Comparison
* @param {any} val
*/
lte(val: any) {
return this.setOp(Operator.SMALLER_EQ, val);
}
in(arr: any[]) {
return this.setOp(Operator.IN, arr);
}
/**
* @summary Creates a Regexpo Comparison
* @param {any} val
*/
regexp(val: any) {
return this.setOp(Operator.REGEXP, new RegExp(val).source);
}
/**
* @summary Creates an {@link Operator} based Comparison
* @param {Operator} op
* @param {any} val
*/
private setOp(op: Operator, val: any) {
this.operator = op;
this.comparison = val;
return this.build();
}
/**
* @summary Builds the Database Object
* @throws {QueryError} if it fails to build the {@link Condition}
* @private
*/
private build(): Condition {
try {
return new Condition(
this.attr1 as string | Condition,
this.operator as Operator,
this.comparison as any
);
} catch (e: any) {
throw new QueryError(e);
}
}
};
static get builder(): ConditionBuilderOption {
return new Condition.Builder();
}
}
Source