UNPKG

@sqb/connect

Version:

Multi-dialect database connection framework written with TypeScript

291 lines (290 loc) 11.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Repository = void 0; const strict_typed_events_1 = require("strict-typed-events"); const sqb_connection_js_1 = require("../client/sqb-connection.js"); const count_command_js_1 = require("./commands/count.command.js"); const create_command_js_1 = require("./commands/create.command.js"); const delete_command_js_1 = require("./commands/delete.command.js"); const find_command_js_1 = require("./commands/find.command.js"); const update_command_js_1 = require("./commands/update.command.js"); const entity_metadata_js_1 = require("./model/entity-metadata.js"); const extract_keyvalues_js_1 = require("./util/extract-keyvalues.js"); /** * @class Repository * @template T - The data type class type of the record */ class Repository extends (0, strict_typed_events_1.TypedEventEmitterClass)(strict_typed_events_1.AsyncEventEmitter) { constructor(entityDef, executor, schema) { super(); this._executor = executor; this._entity = entityDef; this._schema = schema; } get entity() { return this._entity; } get type() { return this._entity.ctor; } create(input, options) { return this._execute(async (connection) => { const keyValue = await this._create(input, { ...options, connection, returning: true, }); const result = keyValue && (await this._find(keyValue, { ...options, connection })); if (!result) throw new Error('Unable to insert new row'); return result; }, options); } /** * Creates a new resource but returns nothing * * @param {PartialDTO<T>} input - The input data * @param {Repository.CreateOptions} [options] - The options object * @returns {Promise<any>} A promise that resolves key value(s) of the created record * @throws {Error} if an unknown error occurs while creating the resource */ createOnly(input, options) { return this._execute(async (connection) => { const r = await this._create(input, { ...options, connection, returning: true, }); const primaryIndex = entity_metadata_js_1.EntityMetadata.getPrimaryIndex(this.entity); if (!primaryIndex || primaryIndex.columns.length > 1) return r; return r[primaryIndex.columns[0]]; }, options); } /** * Returns the count of records based on the provided options * * @param {Repository.CountOptions} options - The options for the count operation. * @return {Promise<number>} - A promise that resolves to the count of records */ count(options) { return this._execute(async (connection) => this._count({ ...options, connection }), options); } /** * Deletes a record from the collection. * * @param {any} keyValue - The ID of the resource to delete. * @param {Repository.DeleteOptions} [options] - Optional delete options. * @return {Promise<boolean>} - A Promise that resolves true or false. True when resource deleted. */ delete(keyValue, options) { return this._execute(async (connection) => this._delete(keyValue, { ...options, connection }), options); } /** * Deletes multiple documents from the collection that meet the specified filter criteria. * * @param {Repository.DeleteManyOptions} options - The options for the delete operation. * @return {Promise<number>} - A promise that resolves to the number of resources deleted. */ deleteMany(options) { return this._execute(async (connection) => this._deleteMany({ ...options, connection }), options); } /** * Checks if a record with the given id exists. * * @param {any} keyValue - The id of the object to check. * @param {Repository.ExistsOptions} [options] - The options for the query (optional). * @return {Promise<boolean>} - A Promise that resolves to a boolean indicating whether the record exists or not. */ exists(keyValue, options) { return this._execute(async (connection) => this._exists(keyValue, { ...options, connection }), options); } /** * Checks if a record with the given arguments exists. * * @param {Repository.ExistsOptions} [options] - The options for the query (optional). * @return {Promise<boolean>} - A Promise that resolves to a boolean indicating whether the record exists or not. */ existsOne(options) { return this._execute(async (connection) => this._existsOne({ ...options, connection }), options); } findById(keyValue, options) { return this._execute(async (connection) => this._find(keyValue, { ...options, connection }), options); } findOne(options) { return this._execute(async (connection) => await this._findOne({ ...options, connection, }), options); } findMany(options) { return this._execute(async (connection) => this._findMany({ ...options, connection }), options); } update(keyValue, input, options) { return this._execute(async (connection) => { const opts = { ...options, connection }; const keyValues = await this._update(keyValue, input, opts); if (keyValues) return this._find(keyValues, opts); }, options); } /** * Updates a record in the collection with the specified ID and returns updated record count * * @param {any} keyValue - The ID of the document to update. * @param {PatchDTO<T>} input - The partial input data to update the document with. * @param {Repository.UpdateOptions} options - The options for updating the document. * @returns {Promise<number>} - A Promise that resolves true or false. True when resource updated. */ updateOnly(keyValue, input, options) { return this._execute(async (connection) => !!(await this._update(keyValue, input, { ...options, connection })), options); } /** * Updates multiple records in the collection based on the specified input and options. * * @param {PatchDTO<T>} input - The partial input to update the documents with. * @param {Repository.UpdateManyOptions} options - The options for updating the documents. * @return {Promise<number>} - A promise that resolves to the number of documents matched and modified. */ updateMany(input, options) { return this._execute(async (connection) => this._updateMany(input, { ...options, connection })); } async _execute(fn, opts) { let connection = opts?.connection; if (!connection && this._executor instanceof sqb_connection_js_1.SqbConnection) connection = this._executor; if (connection) { if (this._schema) await connection.setSchema(this._schema); return fn(connection); } return this._executor.acquire(async (conn) => { if (this._schema) await conn.setSchema(this._schema); await this.emitAsyncSerial('acquire', conn); return fn(conn); }); } async _create(values, options) { if (!values) throw new TypeError('You must provide values'); const keyValues = await create_command_js_1.CreateCommand.execute({ ...options, entity: this._entity, values, }); if (options.returning && !keyValues) throw new Error('Unable to insert new row'); return keyValues; } async _exists(keyValue, options) { const filter = [(0, extract_keyvalues_js_1.extractKeyValues)(this._entity, keyValue, true)]; if (options && options.filter) { if (Array.isArray(options.filter)) filter.push(...options.filter); else filter.push(options.filter); } return this._existsOne({ ...options, filter, }); } async _existsOne(options) { const filter = []; if (options && options.filter) { if (Array.isArray(options.filter)) filter.push(...options.filter); else filter.push(options.filter); } const resp = await find_command_js_1.FindCommand.execute({ ...options, filter, entity: this._entity, }); return resp.length > 0; } async _count(options) { return count_command_js_1.CountCommand.execute({ ...options, entity: this._entity, }); } async _findMany(options) { return await find_command_js_1.FindCommand.execute({ ...options, entity: this._entity, }); } async _findOne(options) { const rows = await this._findMany({ ...options, limit: 1, }); return rows && rows[0]; } async _find(keyValue, options) { const filter = [(0, extract_keyvalues_js_1.extractKeyValues)(this._entity, keyValue, true)]; if (options && options.filter) { if (Array.isArray(options.filter)) filter.push(...options.filter); else filter.push(options.filter); } return await this._findOne({ ...options, filter, offset: 0 }); } async _delete(keyValue, options) { const filter = [(0, extract_keyvalues_js_1.extractKeyValues)(this._entity, keyValue, true)]; if (options && options.filter) { if (Array.isArray(options.filter)) filter.push(...options.filter); else filter.push(options.filter); } return !!(await delete_command_js_1.DeleteCommand.execute({ ...options, filter, entity: this._entity, })); } async _deleteMany(options) { return delete_command_js_1.DeleteCommand.execute({ ...options, entity: this._entity, filter: options?.filter, params: options?.params, }); } async _update(keyValue, values, options) { if (!values) throw new TypeError('You must provide values'); const keyValues = (0, extract_keyvalues_js_1.extractKeyValues)(this._entity, keyValue, true); const filter = [keyValues]; if (options.filter) { if (Array.isArray(options.filter)) filter.push(...options.filter); else filter.push(options.filter); } const updateValues = { ...values }; Object.keys(keyValues).forEach(k => delete updateValues[k]); const rowsAffected = await update_command_js_1.UpdateCommand.execute({ ...options, entity: this._entity, values: updateValues, filter, }); return rowsAffected ? keyValues : undefined; } async _updateMany(values, options) { if (!values) throw new TypeError('You must provide values'); return await update_command_js_1.UpdateCommand.execute({ ...options, entity: this._entity, values, }); } } exports.Repository = Repository;