UNPKG

adba

Version:
337 lines (336 loc) 14.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const status_codes_1 = __importDefault(require("./status-codes")); /** * @class Controller * * @description * A controller class for handling database queries using Objection.js models. */ class Controller { /** * @constructor * * @param {typeof Model} SqliteModel - The Objection.js model. * @param {Object} options - Additional options for the controller. * @param {string[]} [options.searchIn] - Columns to search in by default. */ constructor(SqliteModel, options = {}) { this.Model = SqliteModel; this.qcolumns = options === null || options === void 0 ? void 0 : options.searchIn; } /** * Finds string type columns in the model's JSON schema. * * @param {typeof Model} ModelIn - The model to inspect. Defaults to this.Model. * @returns {string[]} A list of string type column names. */ findTypeString(ModelIn = this.Model) { const properties = ModelIn.jsonSchema.properties; const stringFields = []; for (const key in properties) { const field = properties[key]; if (field.type === 'string') { stringFields.push(key); } } return stringFields; } /** * Lists records based on search criteria. * * @param {ISearch} dataSearch - The search criteria. * @param {QueryBuilderType<any>} [queryBuilder] - Optional query builder instance. * @returns {Promise<object>} The promise of the resulting data. */ list(dataSearch, queryBuilder) { const queryBuilderIn = queryBuilder ? queryBuilder.clone() : this.Model.query(); queryBuilderIn.clear(true); const ModelInUse = (queryBuilder === null || queryBuilder === void 0 ? void 0 : queryBuilder._modelClass) || this.Model; const { filters, orderBy = {}, fields: rawFields, limit: rawLimit, offset: rawOffset, page: rawPage, q } = dataSearch; let limit; let offset; let page; // Set pagination defaults if (rawLimit !== false) { limit = !rawLimit || rawLimit === true ? 20 : rawLimit; offset = rawOffset || ((rawPage || 0) * limit) || 0; page = rawPage || Math.trunc(offset / limit) || 0; } // Columns to return const fields = new Set(); const fieldsReq = !rawFields ? [] : Array.isArray(rawFields) ? rawFields : rawFields.split(','); fieldsReq.forEach((f) => fields.add(f.trim())); if (fields.size) { fields.add(ModelInUse.tableName + '.id'); const finalFields = Array.from(fields); queryBuilderIn.select(finalFields); } // Omni search if (typeof q === 'string') { const tableName = ModelInUse.tableName + '.'; const query = q.replace(/(['\\])/g, '\\$1'); const columns = Array.isArray(this.qcolumns) ? [...this.qcolumns] : this.findTypeString(ModelInUse); const locateOrder = `WHEN {{column}} = '${query}' THEN 0 WHEN {{column}} LIKE '${query}%' THEN 1 WHEN {{column}} LIKE '%${query}' THEN 4`; const regexColumn = /\{\{column\}\}/g; const order = []; const orWhereClauses = []; columns.map((column) => { const orderClause = [ column.includes('.') ? column : tableName + column, 'like', `%${query}%` ]; orWhereClauses.push(orderClause); const orderString = locateOrder.replace(regexColumn, (column.includes('.') ? column : tableName + column)); order.push(orderString); }); queryBuilderIn.where((builder) => { orWhereClauses.forEach(w => builder.orWhere(...w)); }); if (!Object.keys(orderBy).length) { queryBuilderIn.orderByRaw('CASE ' + order.join(' ') + ' ELSE 3 END'); } } const entriesOrderBy = Object.keys(orderBy); if (entriesOrderBy.length) { const [col, dir] = entriesOrderBy.pop(); if (col.includes('.')) queryBuilderIn.orderBy(col, dir); else queryBuilderIn.orderBy(`${ModelInUse.tableName}.${col}`, dir); } // Filtering by columns if (typeof filters === 'object') { queryBuilderIn.where((builder) => { Object.keys(filters).forEach((col) => { const tableColumn = col.includes('.') ? col : ModelInUse.tableName + '.' + col; const search = filters[col]; if (Array.isArray(search)) { const where = ModelInUse.jsonSchema .properties[col].type === 'string' || search.length > 2 ? 'whereIn' : 'whereBetween'; builder[where](tableColumn, search); } else if (ModelInUse.jsonSchema.properties[col]) { if (ModelInUse.jsonSchema.properties[col].type === 'string') { builder.where(tableColumn, 'like', '%' + search + '%'); } else if (['number', 'integer', 'float', 'decimal'] .includes(ModelInUse.jsonSchema.properties[col].type)) { builder.where(tableColumn, Number(search)); } else { builder.where(tableColumn, search); } } else { builder.where(tableColumn, 'like', '%' + search + '%'); } }); }); } if (queryBuilder) queryBuilderIn.copyFrom(queryBuilder, true); let response; if (!!limit) { response = queryBuilderIn.page(page, limit) .then((r) => ({ total: r.total, data: r.results, limit, offset, page })); } else { response = queryBuilderIn.then((data) => ({ total: data.length, data, limit, offset: offset || 0, page })); } return response .then((resp) => this.successMerge(resp)) .catch((error) => this.error(error)); } /** * Selects a record by its ID. * @param {IIds} param0 - The ID or IDs to select. * @param {QueryBuilderType<any>} [queryBuilder] - Optional query builder instance. * @returns {Promise<object>} The promise of the resulting data. */ selectById({ id }, queryBuilder) { const queryBuilderIn = queryBuilder ? queryBuilder.clone() : this.Model.query(); queryBuilderIn.clear(true); queryBuilderIn.findById(id); if (queryBuilder) queryBuilderIn.copyFrom(queryBuilder, true); return queryBuilderIn .then((row) => { if (!row) return this.error(id, 404, 0); return this.success(row); }) .catch((error) => this.error(error)); } /** * Selects one active record based on given criteria. * @param {object} find - Criteria to find the record. * @param {QueryBuilderType<any>} [queryBuilder] - Optional query builder instance. * @returns {Promise<object>} The promise of the resulting data. */ selectOneActive(find, queryBuilder) { const queryBuilderIn = queryBuilder ? queryBuilder.clone() : this.Model.query(); queryBuilderIn.clear(true); queryBuilderIn.findOne(Object.assign(Object.assign({}, find), { active: true })); if (queryBuilder) queryBuilderIn.copyFrom(queryBuilder, true); return queryBuilderIn .then((row) => { if (!row) return this.error(find, 404, 0); return this.success(row); }) .catch((error) => this.error(error)); } /** * Selects one record based on given criteria without considering active state. * @param {object} find - Criteria to find the record. * @param {QueryBuilderType<any>} [queryBuilder] - Optional query builder instance. * @returns {Promise<object>} The promise of the resulting data. */ selectOne(find, queryBuilder) { const queryBuilderIn = queryBuilder ? queryBuilder.clone() : this.Model.query(); queryBuilderIn.clear(true); queryBuilderIn.findOne(find); if (queryBuilder) queryBuilderIn.copyFrom(queryBuilder, true); return queryBuilderIn .then((row) => { if (!row) return this.error(find, 404, 0); return this.success(row); }) .catch((error) => this.error(error)); } /** * Inserts one or multiple records. * * @param {object | object[]} data - The data to insert. * @param {QueryBuilderType<any>} [queryBuilder] - Optional query builder instance. * @returns {Promise<object>} The promise of the resulting data. */ insert(data, queryBuilder) { const queryBuilderIn = queryBuilder ? queryBuilder.clone() : this.Model.query(); queryBuilderIn.clear(true); queryBuilderIn.insertGraph(data, { allowRefs: true }); if (queryBuilder) queryBuilderIn.copyFrom(queryBuilder, true); return queryBuilderIn .then((resp) => this.success(resp)) .catch((error) => this.error(error)); } /** * Updates records using an upsert operation. * * @param {object | object[]} data - The data for upsert. * @param {QueryBuilderType<any>} [queryBuilder] - Optional query builder instance. * @returns {Promise<object>} The promise of the resulting data. */ update(data, queryBuilder) { const queryBuilderIn = queryBuilder ? queryBuilder.clone() : this.Model.query(); queryBuilderIn.clear(true); queryBuilderIn.upsertGraph(data, { allowRefs: true }); if (queryBuilder) queryBuilderIn.copyFrom(queryBuilder, true); return queryBuilderIn .then((resp) => this.success(resp)) .catch((error) => this.error(error)); } /** * Deletes records based on where clause. * * @param {WhereMethod<any>} whereData - Criteria for delete operation. * @param {QueryBuilderType<any>} [queryBuilder] - Optional query builder instance. * @returns {Promise<object>} The promise of the resulting data. */ deleteWhere(whereData, queryBuilder) { const queryBuilderIn = queryBuilder ? queryBuilder.clone() : this.Model.query(); queryBuilderIn.clear(true); queryBuilderIn.delete().where(whereData); if (queryBuilder) queryBuilderIn.copyFrom(queryBuilder, true); return queryBuilderIn .then((resp) => this.success(resp)) .catch((error) => this.error(error)); } /** * Deletes records by ID(s). * * @param {IIds} param0 - The ID or IDs of records to delete. * @param {QueryBuilderType<any>} [queryBuilder] - Optional query builder instance. * @returns {Promise<object>} The promise of the resulting data. */ delete({ id, ids }, queryBuilder) { const queryBuilderIn = queryBuilder ? queryBuilder.clone() : this.Model.query(); queryBuilderIn.clear(true); queryBuilderIn.delete().whereIn('id', [id, ids].flat().filter(Boolean)); if (queryBuilder) queryBuilderIn.copyFrom(queryBuilder, true); return queryBuilderIn .then((resp) => this.success(resp)) .catch((error) => this.error(error)); } /** * Formats a successful response object. * * @param {any} data - The data to return in the response. * @param {number} [status=200] - The HTTP status code. * @param {number} [code=0] - Additional status code. * @returns {object} The success response object. */ success(data, status = 200, code = 0) { const toReturn = Object.assign({ error: false, success: true }, (0, status_codes_1.default)(status, code), { data }); return toReturn; } /** * Merges additional data into a successful response object. * * @param {any} data - The data to return in the response. * @param {number} [status=200] - The HTTP status code. * @param {number} [code=0] - Additional status code. * @returns {object} The merged success response object. */ successMerge(data, status = 200, code = 0) { const toReturn = Object.assign({ error: false, success: true }, (0, status_codes_1.default)(status, code), data); return toReturn; } /** * Formats an error response object. * * @param {any} errorObj - The error object or message. * @param {number} [status=500] - The HTTP status code. * @param {number} [code=0] - Additional status code. * @returns {object} The error response object. */ error(errorObj, status = 500, code = 0) { let toReturn; if (errorObj instanceof Error) { console.error(errorObj); toReturn = Object.assign({ error: true, success: false, }, (0, status_codes_1.default)(500, 0), { data: errorObj.message }); } else { toReturn = Object.assign({ error: true, success: false }, (0, status_codes_1.default)(status, code), { data: errorObj }); } return toReturn; } } exports.default = Controller;