lib/common/utils/statementBuilder.util.ts (183 lines of code) (raw):

import type { Date, DateTime, Statement, String_ValueMapEntry, Value, } from "../../common/types"; import { InvalidOperationException } from "../handlers/exceptions.handler"; export class StatementBuilder { static SUGGESTED_PAGE_LIMIT = 500; private SELECT = "SELECT"; private FROM = "FROM"; private WHERE = "WHERE"; private LIMIT = "LIMIT"; private OFFSET = "OFFSET"; private ORDER_BY = "ORDER BY"; private _select?: string; private _from?: string; private _where?: string; private _limit?: number | undefined; private _offset?: number | undefined; private _orderBy?: string; private valueEntries: String_ValueMapEntry[] = []; private removeKeyword(clause: string, keyword: string): string { const formattedKeyword: string = keyword.trim() + " "; return clause.toUpperCase().startsWith(formattedKeyword) ? clause.substring(formattedKeyword.length) : clause; } public select(columns: string): StatementBuilder { this._select = this.removeKeyword(columns, this.SELECT); return this; } public from(table: string): StatementBuilder { this._from = this.removeKeyword(table, this.FROM); return this; } public where(table: string): StatementBuilder { this._where = this.removeKeyword(table, this.WHERE); return this; } public limit(count: number): StatementBuilder { this._limit = count; return this; } public offset(count: number): StatementBuilder { this._offset = count; return this; } public increaseOffsetBy(amount: number): StatementBuilder { if (this._offset === undefined) this._offset = 0; this._offset += amount; return this; } public getOffset(): number | undefined { return this._offset; } public removeLimitAndOffset(): StatementBuilder { this._offset = undefined; this._limit = undefined; return this; } public orderBy(orderBy: string): StatementBuilder { this._orderBy = this.removeKeyword(orderBy, this.ORDER_BY); return this; } public addValue(key: string, value: Value): StatementBuilder { this.valueEntries.push({ key, value: this.getValue(value), }); return this; } private getValue(value: Value): Record<string, any> { switch (typeof value) { case "string": return { value, attributes: { "xsi:type": "TextValue", }, }; case "number": return { value: value, attributes: { "xsi:type": "NumberValue", }, }; case "boolean": return { value: value, attributes: { "xsi:type": "BooleanValue", }, }; case "object": if (Array.isArray(value)) { return { values: value.map((v) => this.getValue(v)), attributes: { "xsi:type": "SetValue", }, }; } if (this.isDateTime(value)) { return { value: value, attributes: { "xsi:type": "DateTimeValue", }, }; } if (this.isDate(value)) { return { value: value, attributes: { "xsi:type": "DateValue", }, }; } return { value: value, attributes: { "xsi:type": "ObjectValue", }, }; default: throw new Error(typeof value); } } private isDateTime(arg: any): arg is DateTime { arg = arg as DateTime; if (!arg.date || !this.isDate(arg.date)) return false; if (!arg.hour || arg.hour === null) return false; if (!arg.minute || arg.minute === null) return false; if (!arg.second || arg.second === null) return false; return true; } private isDate(arg: any): arg is Date { arg = arg as Date; if (!arg.year || arg.year === null) return false; if (!arg.month || arg.month === null) return false; if (!arg.day || arg.day === null) return false; return true; } private validateQuery(): void { if (!this._limit && this._offset) { throw new InvalidOperationException( "OFFSET cannot be set if LIMIT is not set.", ); } } private buildQuery(): string { const stringBuilder = []; this.validateQuery(); if (typeof this.select !== "undefined" && this._select) { stringBuilder.push(`${this.SELECT} ${this._select}`); } if (typeof this.from !== "undefined" && this._from) { stringBuilder.push(`${this.FROM} ${this._from}`); } if (typeof this.where !== "undefined" && this._where) { stringBuilder.push(`${this.WHERE} ${this._where}`); } if (typeof this.orderBy !== "undefined" && this._orderBy) { stringBuilder.push(`${this.ORDER_BY} ${this._orderBy}`); } if (typeof this.limit !== "undefined" && this._limit) { stringBuilder.push(`${this.LIMIT} ${this._limit}`); } if (typeof this.offset !== "undefined" && this._offset) { stringBuilder.push(`${this.OFFSET} ${this._offset}`); } return stringBuilder.join(" "); } public toStatement(): Statement { return { query: this.buildQuery(), values: this.valueEntries, }; } }