UNPKG

slim-ef

Version:

An implementation of basic entity framework functionnalities in typescript

266 lines 10.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.UnitOfWork = exports.DbContext = void 0; const repository_1 = require("../repository"); const db_set_1 = require("../repository/db-set"); const specification_interface_1 = require("../specification/specification.interface"); const metadata_1 = require("./metadata"); const model_builder_1 = require("./model-builder"); const options_builder_1 = require("./options-builder"); class DbContext { constructor(_connection, evaluator) { this._connection = _connection; this.evaluator = evaluator; this._new = []; this._dirty = []; this._deleted = []; this._entitySets = new WeakMap(); this._modelBuilder = new model_builder_1.DbContextModelBuilder(); this._optionsBuilder = new options_builder_1.DbContextOptionsBuilder(); this._initialise(); } _initialise() { this._setUnderlyingEntityType(); this.onModelCreation(this._modelBuilder); this.onConfiguring(this._optionsBuilder); } set(type) { const dbSet = new db_set_1.DbSet(this); dbSet[db_set_1.UnderlyingType] = type; return dbSet; } add(...entities) { this._new.push(...this._throwIfNullFound(entities, 'add')); } unTrack(entity) { this._new = [...this._new.filter(n => n !== entity)]; this._dirty = [...this._dirty.filter(n => n !== entity)]; this._deleted = [...this._deleted.filter(n => n !== entity)]; } update(...entities) { this._dirty.push(...this._throwIfNullFound(entities, 'attach')); } remove(...entities) { this._deleted.push(...this._throwIfNullFound(entities, 'remove')); } _throwIfNullFound(entities, method) { if (entities.some(e => !e)) { throw new Error('Entities can not be null when call ' + method); } return entities; } async find(type, id) { await this._tryOpenConnection(); const repo = this._getRepository(type); if (repo) { const res = (await repo.findOne(id)); return res; } return void 0; } rollback(entityType = null) { if (entityType !== null) { const type = typeof entityType; this._new = [...this._new.filter(n => typeof n !== type)]; this._dirty = [...this._dirty.filter(n => typeof n === type)]; this._deleted = [...this._deleted.filter(n => typeof n === type)]; } else { this._dispose(); } } async saveChanges(withoutRefresh = false) { await this._tryOpenConnection(); const transIsOpened = this.transactionIsOpen(); if (!transIsOpened) { this._queryRunner = this._connection.createQueryRunner(); await this._queryRunner.startTransaction(); } try { const added = await this._commitNew(); const toUpdate = await this._commitDirty(); const deleted = await this._commitDeleted(); if (!transIsOpened) await this._queryRunner.commitTransaction(); const updated = withoutRefresh ? toUpdate : await this._getUpdates(toUpdate); return { added, updated, deleted }; } catch (e) { if (transIsOpened) await this._queryRunner.rollbackTransaction(); throw e; } finally { if (!this._queryRunner.isReleased && !transIsOpened) this._queryRunner.release(); this._dispose(); } } transactionIsOpen() { var _a; return !!((_a = this._queryRunner) === null || _a === void 0 ? void 0 : _a.isTransactionActive) && this._isUserTransaction; } async openTransaction() { if (this.transactionIsOpen()) { throw new Error('A transaction is already open, please release the later before opening a new one'); } await this._tryOpenConnection(); this._isUserTransaction = true; this._queryRunner = this._connection.createQueryRunner(); await this._queryRunner.startTransaction(); } async commitTransaction() { if (this.transactionIsOpen()) { await this._tryOpenConnection(); await this._queryRunner.commitTransaction(); await this._queryRunner.release(); this._queryRunner = null; this._isUserTransaction = false; } } async rollbackTransaction() { if (this.transactionIsOpen()) { await this._queryRunner.rollbackTransaction(); await this._queryRunner.release(); this._queryRunner = null; this._isUserTransaction = false; } } async query(query, parameters) { await this._tryOpenConnection(); const result = await this._connection.query(query, parameters); // await this._tryCloseConenction(); return result; } loadRelatedData(type, entity) { const manager = this._connection.manager; return manager.preload(type, entity); } //#region IInternalDbContext Implementation async execute(queryable, type = specification_interface_1.QueryType.ALL, ignoreFilters = false) { await this._tryOpenConnection(); if (!ignoreFilters) { // applying filters const filters = this._modelBuilder.getFilters(queryable[db_set_1.UnderlyingType]); filters.forEach(f => { f(queryable); }); } const initializer = this._getSQLBuilder(queryable); const specEval = new this.evaluator(initializer, queryable.asSpecification()); const logger = this._optionsBuilder.createLogger('query'); logger === null || logger === void 0 ? void 0 : logger.log('info', await specEval.getQuery()); logger === null || logger === void 0 ? void 0 : logger.log('info', await specEval.getParams()); const result = await specEval.executeQuery(type); // CLosing the connection here prevents typeorm lazyloading. // So commenting this, may be I will get e better solution for // managing these resources // await this._tryCloseConenction(); return result; } async getMetadata(type, includePaths) { await this._tryOpenConnection(); const md = metadata_1.getMetaData(this._connection, type, includePaths); // await this._tryCloseConenction(); return md; } //#endregion dispose() { this._dispose(); this._tryCloseConenction(); } //#region Private methods async _tryOpenConnection() { if (!this._connection.isConnected) await this._connection.connect(); } async _tryCloseConenction() { if (this._connection.isConnected) await this._connection.close(); } _dispose() { this._new = []; this._dirty = []; this._deleted = []; } _setUnderlyingEntityType() { const underType = repository_1.getEntitySetKeys(this); if (underType) { for (const t of underType) { const dbset = new db_set_1.DbSet(this); const entity = repository_1.getEntitySet(this, t); if (dbset && entity) { Object.defineProperty(this, t, { configurable: false, enumerable: false, value: dbset, writable: false }); dbset[db_set_1.UnderlyingType] = entity; } } } } async _getUpdates(toUpdate) { const updated = []; for (const n of toUpdate) { const repo = this._getRepository(n); updated.push(await repo.findOne(n.id)); } return updated; } async _commitDeleted() { const deleted = []; for (const n of this._deleted) { const repo = this._getRepository(n); deleted.push(await repo.delete(n)); } return deleted; } async _commitDirty() { return await this._commitDirtyAll(this._dirty); } async _commitDirtyAll(dirty) { const updated = []; for (const n of dirty) { const repo = this._getRepository(n); updated.push(await repo.save(n)); } return updated; } async _commitNew() { const added = []; for (const n of this._new) { const repo = this._getRepository(n); added.push(await repo.save(n)); } return added; } _getSQLBuilder(type) { const repo = this._getRepository(type); const initializer = (alias) => repo.createQueryBuilder(alias); return initializer; } _getRepository(type) { var _a; if (type instanceof db_set_1.DbSet) { type = type[db_set_1.UnderlyingType]; } const schema = repository_1.getEntitySchema(type); if (this._entitySets.has(type)) return this._entitySets.get(type); if (!schema) throw new Error(`Schema [${(_a = type === null || type === void 0 ? void 0 : type.constructor) === null || _a === void 0 ? void 0 : _a.name}] not found. You may not have set EntityRepository decorator correctly`); const repo = this._connection.getRepository(schema); this._entitySets.set(type, repo); return repo; } } exports.DbContext = DbContext; // tslint:disable-next-line: max-classes-per-file class UnitOfWork extends DbContext { } exports.UnitOfWork = UnitOfWork; //# sourceMappingURL=db-context.js.map