@js-ak/db-manager
Version:
440 lines (439 loc) • 18.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseModel = void 0;
const Helpers = __importStar(require("../helpers/index.js"));
const SharedHelpers = __importStar(require("../../../shared-helpers/index.js"));
const connection = __importStar(require("../connection.js"));
const index_js_1 = require("../query-builder/index.js");
const queries_js_1 = __importDefault(require("./queries.js"));
const index_js_2 = require("../helpers/index.js");
class BaseModel {
#insertOptions;
#sortingOrders = new Set(["ASC", "DESC"]);
#tableFieldsSet;
#isLoggerEnabled;
#logger;
#executeSql;
/**
* The PostgreSQL executor.
* - pg.Pool
* - pg.PoolClient
* - pg.Client
*/
#executor;
createField;
primaryKey;
tableName;
tableFields;
updateField;
constructor(data, dbCreds, options) {
this.createField = data.createField;
this.#executor = connection.getStandardPool(dbCreds);
this.primaryKey = data.primaryKey;
this.tableName = data.tableName;
this.tableFields = [...data.tableFields];
this.updateField = data.updateField;
this.#tableFieldsSet = new Set([
...this.tableFields,
...(data.additionalSortingFields || []),
]);
const { insertOptions, isLoggerEnabled, logger } = options || {};
const preparedOptions = (0, index_js_2.setLoggerAndExecutor)(this.#executor, { isLoggerEnabled, logger });
this.#insertOptions = insertOptions;
this.#executeSql = preparedOptions.executeSql;
this.#isLoggerEnabled = preparedOptions.isLoggerEnabled;
this.#logger = preparedOptions.logger;
}
/**
* Gets the database client for the model.
*
* @returns The database client for the model.
*/
get pool() {
return this.#executor;
}
/**
* Gets the PostgreSQL executor for the model.
*
* @returns The PostgreSQL executor for the model.
*/
get executor() {
return this.#executor;
}
/**
* Sets the logger for the model.
*
* @param logger - The logger to use for the model.
*/
setLogger(logger) {
const preparedOptions = (0, index_js_2.setLoggerAndExecutor)(this.#executor, { isLoggerEnabled: true, logger });
this.#executeSql = preparedOptions.executeSql;
this.#isLoggerEnabled = preparedOptions.isLoggerEnabled;
this.#logger = preparedOptions.logger;
}
/**
* Sets the executor for the model.
*
* @param executor - The executor to use for the model.
*/
setExecutor(executor) {
const preparedOptions = (0, index_js_2.setLoggerAndExecutor)(executor, { isLoggerEnabled: this.#isLoggerEnabled, logger: this.#logger });
this.#executeSql = preparedOptions.executeSql;
this.#isLoggerEnabled = preparedOptions.isLoggerEnabled;
this.#logger = preparedOptions.logger;
this.#executor = executor;
}
get isLoggerEnabled() {
return this.#isLoggerEnabled;
}
get executeSql() {
return this.#executeSql;
}
compareFields = Helpers.compareFields;
getFieldsToSearch = Helpers.getFieldsToSearch;
compareQuery = {
deleteAll: () => {
return { query: queries_js_1.default.deleteAll(this.tableName) };
},
deleteByParams: ({ $and = {}, $or }) => {
const { queryArray, queryOrArray, values } = this.compareFields($and, $or);
const { searchFields } = this.getFieldsToSearch({ queryArray, queryOrArray });
return {
query: queries_js_1.default.deleteByParams(this.tableName, searchFields),
values,
};
},
deleteOneByPk: (primaryKey) => {
if (!this.primaryKey) {
throw new Error("Primary key not specified");
}
return {
query: queries_js_1.default.deleteByPk(this.tableName, this.primaryKey),
values: Array.isArray(primaryKey) ? primaryKey : [primaryKey],
};
},
getArrByParams: ({ $and = {}, $or }, selected = ["*"], pagination, order) => {
if (order?.length) {
for (const o of order) {
if (!this.#tableFieldsSet.has(o.orderBy)) {
const allowedFields = Array.from(this.#tableFieldsSet).join(", ");
throw new Error(`Invalid orderBy: ${o.orderBy}. Allowed fields are: ${allowedFields}`);
}
if (!this.#sortingOrders.has(o.ordering)) {
throw new Error("Invalid ordering");
}
}
}
if (!selected.length)
selected.push("*");
const { queryArray, queryOrArray, values } = this.compareFields($and, $or);
const { orderByFields, paginationFields, searchFields, selectedFields } = this.getFieldsToSearch({ queryArray, queryOrArray }, selected, pagination, order);
return {
query: queries_js_1.default.getByParams(this.tableName, selectedFields, searchFields, orderByFields, paginationFields),
values,
};
},
getCountByParams: ({ $and = {}, $or }) => {
const { queryArray, queryOrArray, values } = this.compareFields($and, $or);
const { searchFields } = this.getFieldsToSearch({ queryArray, queryOrArray });
return {
query: queries_js_1.default.getCountByParams(this.tableName, searchFields),
values,
};
},
getCountByPks: (pks) => {
if (!this.primaryKey) {
throw new Error("Primary key not specified");
}
if (Array.isArray(pks[0])) {
if (!Array.isArray(this.primaryKey)) {
throw new Error("invalid primary key type");
}
return {
query: queries_js_1.default.getCountByCompositePks(this.primaryKey, this.tableName, pks.length),
values: [pks.flat()],
};
}
if (Array.isArray(this.primaryKey)) {
throw new Error("invalid primary key type");
}
return {
query: queries_js_1.default.getCountByPks(this.primaryKey, this.tableName),
values: [pks],
};
},
getCountByPksAndParams: (pks, { $and = {}, $or }) => {
if (!this.primaryKey) {
throw new Error("Primary key not specified");
}
const { queryArray, queryOrArray, values } = this.compareFields($and, $or);
const { orderNumber, searchFields } = this.getFieldsToSearch({ queryArray, queryOrArray });
if (Array.isArray(pks[0])) {
if (!Array.isArray(this.primaryKey)) {
throw new Error("invalid primary key type");
}
return {
query: queries_js_1.default.getCountByCompositePksAndParams(this.primaryKey, this.tableName, searchFields, orderNumber, pks.length),
values: pks.flat(),
};
}
if (Array.isArray(this.primaryKey)) {
throw new Error("invalid primary key type");
}
return {
query: queries_js_1.default.getCountByPksAndParams(this.primaryKey, this.tableName, searchFields, orderNumber),
values: [...values, pks],
};
},
getOneByParams: ({ $and = {}, $or }, selected = ["*"]) => {
if (!selected.length)
selected.push("*");
const { queryArray, queryOrArray, values } = this.compareFields($and, $or);
const { orderByFields, paginationFields, searchFields, selectedFields } = this.getFieldsToSearch({ queryArray, queryOrArray }, selected, { limit: 1, offset: 0 });
return {
query: queries_js_1.default.getByParams(this.tableName, selectedFields, searchFields, orderByFields, paginationFields),
values,
};
},
getOneByPk: (pk) => {
if (!this.primaryKey) {
throw new Error("Primary key not specified");
}
return {
query: queries_js_1.default.getOneByPk(this.tableName, this.primaryKey),
values: [pk],
};
},
save: (recordParams = {}, saveOptions) => {
const clearedParams = SharedHelpers.clearUndefinedFields(recordParams);
const fields = Object.keys(clearedParams);
const onConflict = this.#insertOptions?.onConflict || "";
if (!fields.length) {
throw new Error("No one save field arrived");
}
return {
query: queries_js_1.default.createOne(this.tableName, fields, this.createField, onConflict, saveOptions?.returningFields),
values: Object.values(clearedParams),
};
},
updateByParams: (queryConditions, updateFields = {}) => {
const { queryArray, queryOrArray, values } = this.compareFields(queryConditions.$and, queryConditions.$or);
const { orderNumber, searchFields } = this.getFieldsToSearch({ queryArray, queryOrArray });
const clearedUpdate = SharedHelpers.clearUndefinedFields(updateFields);
const fieldsToUpdate = Object.keys(clearedUpdate);
if (!queryArray.length)
throw new Error("No one update field arrived");
return {
query: queries_js_1.default.updateByParams(this.tableName, fieldsToUpdate, searchFields, this.updateField, orderNumber + 1, queryConditions?.returningFields),
values: [...values, ...Object.values(clearedUpdate)],
};
},
updateOneByPk: (primaryKeyValue, updateFields = {}, updateOptions) => {
if (!this.primaryKey) {
throw new Error("Primary key not specified");
}
const clearedParams = SharedHelpers.clearUndefinedFields(updateFields);
const fields = Object.keys(clearedParams);
if (!fields.length)
throw new Error("No one update field arrived");
return {
query: queries_js_1.default.updateByPk(this.tableName, fields, this.primaryKey, this.updateField, updateOptions?.returningFields),
values: [...Object.values(clearedParams), primaryKeyValue],
};
},
};
async deleteAll() {
const sql = this.compareQuery.deleteAll();
await this.#executeSql(sql);
return;
}
async deleteOneByPk(primaryKey) {
const sql = this.compareQuery.deleteOneByPk(primaryKey);
const { rows } = await this.#executeSql(sql);
const entity = rows[0];
if (!entity)
return null;
if (Array.isArray(this.primaryKey)) {
return this.primaryKey.map((e) => entity[e]);
}
else {
return entity[this.primaryKey];
}
}
async deleteByParams(params) {
const sql = this.compareQuery.deleteByParams(params);
await this.#executeSql(sql);
return null;
}
async getArrByParams(params, selected = ["*"], pagination, order) {
const sql = this.compareQuery.getArrByParams(params, selected, pagination, order);
const { rows } = await this.#executeSql(sql);
return rows;
}
async getCountByPks(pks) {
const sql = this.compareQuery.getCountByPks(pks);
const { rows } = await this.#executeSql(sql);
return Number(rows[0]?.count) || 0;
}
async getCountByPksAndParams(pks, params) {
const sql = this.compareQuery.getCountByPksAndParams(pks, params);
const { rows } = await this.#executeSql(sql);
return Number(rows[0]?.count) || 0;
}
async getCountByParams(params) {
const sql = this.compareQuery.getCountByParams(params);
const { rows } = await this.#executeSql(sql);
return Number(rows[0]?.count) || 0;
}
async getOneByParams(params, selected = ["*"]) {
const sql = this.compareQuery.getOneByParams(params, selected);
const { rows } = await this.#executeSql(sql);
return rows[0];
}
async getOneByPk(pk) {
const sql = this.compareQuery.getOneByPk(pk);
const { rows } = await this.#executeSql(sql);
return rows[0];
}
async save(recordParams = {}, saveOptions) {
const sql = this.compareQuery.save(recordParams, saveOptions);
const { rows } = await this.#executeSql(sql);
return rows[0];
}
async updateByParams(queryConditions, updateFields = {}) {
const sql = this.compareQuery.updateByParams(queryConditions, updateFields);
const { rows } = await this.#executeSql(sql);
return rows;
}
async updateOneByPk(primaryKeyValue, updateFields = {}, updateOptions) {
const sql = this.compareQuery.updateOneByPk(primaryKeyValue, updateFields, updateOptions);
const { rows } = await this.#executeSql(sql);
return rows[0];
}
/**
* @experimental
*/
queryBuilder(options) {
const { client, isLoggerEnabled, logger, tableName } = options || {};
return new index_js_1.QueryBuilder(tableName ?? this.tableName, client ?? this.#executor, {
isLoggerEnabled: isLoggerEnabled ?? this.#isLoggerEnabled,
logger: logger ?? this.#logger,
});
}
// STATIC METHODS
static getStandardPool(creds, poolName) {
return connection.getStandardPool(creds, poolName);
}
static async removeStandardPool(creds, poolName) {
return connection.removeStandardPool(creds, poolName);
}
static getTransactionPool(creds, poolName) {
return connection.getTransactionPool(creds, poolName);
}
static async removeTransactionPool(creds, poolName) {
return connection.removeTransactionPool(creds, poolName);
}
static getInsertFields(data) {
const { params: paramsRaw, returning, tableName, } = data;
if (Array.isArray(paramsRaw)) {
const v = [];
const k = [];
const headers = new Set();
const example = paramsRaw[0];
if (!example)
throw new Error("Invalid parameters");
const params = SharedHelpers.clearUndefinedFields(example);
Object.keys(params).forEach((e) => headers.add(e));
for (const pR of paramsRaw) {
const params = SharedHelpers.clearUndefinedFields(pR);
const keys = Object.keys(params);
k.push(keys);
v.push(...Object.values(params));
if (!k.length) {
throw new Error(`Invalid params, all fields are undefined - ${Object.keys(paramsRaw).join(", ")}`);
}
for (const key of keys) {
if (!headers.has(key)) {
throw new Error(`Invalid params, all fields are undefined - ${Object.keys(pR).join(", ")}`);
}
}
}
const returningSQL = returning?.length
? `RETURNING ${returning.join(",")}`
: "";
let idx = 0;
const query = `
INSERT INTO ${tableName}(${Array.from(headers).join(",")})
VALUES(${k.map((e) => e.map(() => "$" + ++idx)).join("),(")})
${returningSQL}
`;
return { query, values: v };
}
const params = SharedHelpers.clearUndefinedFields(paramsRaw);
const k = Object.keys(params);
const v = Object.values(params);
if (!k.length)
throw new Error(`Invalid params, all fields are undefined - ${Object.keys(paramsRaw).join(", ")}`);
const returningSQL = returning?.length
? `RETURNING ${returning.join(",")}`
: "";
const query = `INSERT INTO ${tableName}(${k.join(",")}) VALUES(${k.map((_, idx) => "$" + ++idx).join(",")}) ${returningSQL};`;
return { query, values: v };
}
static getUpdateFields(data) {
const { params: paramsRaw, primaryKey, returning, tableName, updateField, } = data;
const params = SharedHelpers.clearUndefinedFields(paramsRaw);
const k = Object.keys(params);
const v = Object.values(params);
if (!k.length)
throw new Error(`Invalid params, all fields are undefined - ${Object.keys(paramsRaw).join(", ")}`);
let updateFields = k.map((e, idx) => `${e} = $${idx + 2}`).join(",");
if (updateField) {
switch (updateField.type) {
case "timestamp": {
updateFields += `, ${updateField.title} = NOW()`;
break;
}
case "unix_timestamp": {
updateFields += `, ${updateField.title} = ROUND((EXTRACT(EPOCH FROM NOW()) * (1000)::NUMERIC))`;
break;
}
default: {
throw new Error("Invalid type: " + updateField.type);
}
}
}
const returningSQL = returning?.length
? `RETURNING ${returning.join(",")}`
: "";
const query = `UPDATE ${tableName} SET ${updateFields} WHERE ${primaryKey.field} = $1 ${returningSQL};`;
return { query, values: [primaryKey.value, ...v] };
}
}
exports.BaseModel = BaseModel;