ts-db-helper
Version:
Simple ORM based on TypeScript
317 lines • 12.4 kB
JavaScript
import { DefaultLogger } from '../internal/default-logger';
import { Subject } from 'rxjs/Subject';
import { QueryError } from '../errors/query.error';
import { ModelManager } from './model-manager';
import { UnsatisfiedRequirementError } from '../errors/unsatisfied-requirement.error';
import { PendingDbQuery } from '../models/pending-db-query.model';
import 'rxjs/add/operator/share';
/**
* @private
* @class QueryManager
*
* @description
* This class is a singleton manging query
* This manager has not to be exposed, it is used to handle queries
* with the connector. It stack it during connector is not ready and
* release it when connector ca query
*
* @author Olivier Margarit
* @since 0.1
*/
var QueryManager = /** @class */ (function () {
/**
* @private
* @constructor private constructor to preserve from other instance creation
*/
function QueryManager() {
/**
* @private
* @property {Array<PendingDbQuery>} pendingDbQueries stack of queries to fire when the
* connector is ready
*/
this.pendingDbQueries = [];
/**
* @private
* @property {Array<PendingDbQuery>} pendingBatchDbQueries stack of queries to fire in a single transaction
*/
this.pendingBatchDbQueries = [];
/**
* @private
* @property {boolean} isReady flag that should pas to true only when
* query can be sent
*/
this.isReady = false;
/**
* @private
* @property {boolean} isInitializationFailed flag to reject all queries
* in case of connector activation failure
*/
this.isInitializationFailed = false;
this.supportRowidValue = false;
}
Object.defineProperty(QueryManager.prototype, "logger", {
get: function () {
if (!this.loggerValue) {
this.loggerValue = new DefaultLogger();
}
return this.loggerValue;
},
enumerable: true,
configurable: true
});
Object.defineProperty(QueryManager.prototype, "supportRowid", {
get: function () {
return this.supportRowidValue;
},
enumerable: true,
configurable: true
});
/**
* @static
* @public
* @method init is a part of the private API, config is submitted to
* pass connector, model migration and other config. see {@link NgDbHelperModuleConfig}
* for more informations
*
* @param {NgDbHelperModuleConfig} config the module configuration with connector instance and model migration
*
* @return {QueryManager} the initialized instance
*/
QueryManager.init = function (config) {
var instance = QueryManager.getInstance();
instance.queryConnector = config.queryConnector;
instance.modelMigration = config.modelMigration;
instance.supportRowidValue = config.queryConnector.supportRowid;
if (config.logger) {
instance.loggerValue = config.logger;
}
ModelManager.version = config.version;
if (config.autoIncrementVersion && ModelManager.version) {
// compute auto upgrade version
ModelManager.version += '.' + ModelManager.getInstance().getModelCount();
}
instance.queryConnector.onReady().subscribe(function (ready) {
if (ready) {
instance.onQueryConnectorReady();
}
else {
instance.onInitializationFailure(null);
}
}, instance.onInitializationFailure);
return instance;
};
/**
* @static
* @public
* @method getInstance to get the unique QueryManager instance
*
* @return {QueryManager} the instance
*/
QueryManager.getInstance = function () {
return QueryManager.instance;
};
/**
* @private
* @method onQueryConnectorReady is called when query connector is ready to
* manage model migration then dequeuing each pending queries
*/
QueryManager.prototype.onQueryConnectorReady = function () {
var _this = this;
if (this.queryConnector && this.modelMigration) {
var modelMigration_1 = this.modelMigration;
this.queryConnector.getDbVersion().subscribe(function (version) {
_this.logger.info('old version: ' + version);
var dataModel = ModelManager.getInstance().getDataModel();
dataModel.version = ModelManager.version;
_this.logger.info('new version: ' + dataModel.version);
var isFailed = false;
if (!version) {
_this.logger.info('call init data model migration');
modelMigration_1.initModel(dataModel).subscribe(function () {
_this.logger.info('init data model migration successed');
}, function (err) {
isFailed = err;
_this.logger.warn('init data model migration failed');
}, function () {
if (isFailed) {
_this.onInitializationFailure(isFailed);
}
else {
_this.dequeuePendingRequest();
}
});
}
else if (version !== ModelManager.version) {
_this.logger.info('call upgrade data model migration');
modelMigration_1.upgradeModel(dataModel, version).subscribe(function () {
_this.logger.info('upgrade data model migration successed');
}, function (err) {
isFailed = err;
_this.logger.warn('upgrade data model migration failed');
}, function () {
if (isFailed) {
_this.onInitializationFailure(isFailed);
}
else {
_this.dequeuePendingRequest();
}
});
}
else {
_this.dequeuePendingRequest();
}
}, function (err) { return _this.onInitializationFailure(err); });
}
else {
throw (new UnsatisfiedRequirementError('ModelMigration or QueryConnector object is missing, check and fix NgDbHelperModule configuration !'));
}
};
/**
* @private
* @method onInitializationFailure is called on initilization failure to cancel
* all started queries and nexts
*
* @param {Error} err the error return by the initialization failure
*/
QueryManager.prototype.onInitializationFailure = function (err) {
this.logger.error(err);
this.isInitializationFailed = true;
this.dequeuePendingRequest();
};
/**
* @private
* @method dequeuePendingRequest is called to dequeue pending request
*/
QueryManager.prototype.dequeuePendingRequest = function () {
while (this.pendingDbQueries.length) {
var pendingDbQuery = this.pendingDbQueries.shift();
if (pendingDbQuery) {
this.executeQuery(pendingDbQuery.dbQuery, pendingDbQuery.observer);
}
}
this.isReady = true;
};
/**
* @public
* @method startBatch start transaction for multiple queries
* @param {any} locker the locker that can fire the transaction if none is set
*/
QueryManager.prototype.startBatch = function (locker) {
if (!this.batchLock) {
this.batchLock = locker;
}
};
/**
* @public
* @method query private API to start queries, it check if queries can be executed
* If initialization failed, error is sent back. If connector is not ready, query will
* be stack until connector ready signal
*
* @param {DbQuery} dbQuery query information with params
*
* @return {Observable<QueryResult<any>>} in case of success, {@link QueryResult<any>} is passed
* in case of failure, {@link QueryError} is passed
*/
QueryManager.prototype.query = function (dbQuery) {
var subject = new Subject();
if (this.batchLock) {
this.pendingBatchDbQueries.push({
dbQuery: dbQuery,
observer: subject
});
}
else if (this.isReady) {
this.executeQuery(dbQuery, subject);
}
else {
this.pendingDbQueries.push(new PendingDbQuery(dbQuery, subject));
}
return subject;
};
/**
* @public
* @method queryBatch should only be called from {@link QueryBatch} function.
*
* @param locker the locker passed to startBatch that allow queries releasing
*
* @return {Observable<QueryResult<any>>} in case of success, {@link QueryResult<any>} is passed
* in case of failure, {@link QueryError} is passed
*
* @since 0.2
*/
QueryManager.prototype.execBatch = function (locker) {
var queries = [];
var observers = [];
if (!this.batchSubject) {
this.batchSubject = new Subject();
}
if (this.batchLock === locker) {
this.batchLock = null;
}
else {
return this.batchSubject;
}
while (this.pendingBatchDbQueries.length) {
var pendingQuery = this.pendingBatchDbQueries.shift();
queries.push(pendingQuery.dbQuery);
observers.push(pendingQuery.observer);
}
var obs = this.queryConnector.queryBatch(queries);
obs.map(function (res) {
for (var _i = 0, observers_1 = observers; _i < observers_1.length; _i++) {
var observer = observers_1[_i];
observer.next({
insertId: undefined,
rowsAffected: 0,
rows: {
length: 0,
item: function (index) {
return null;
},
toArray: function () {
return [];
}
}
});
observer.complete();
}
return res;
});
obs.share().subscribe(this.batchSubject);
this.batchSubject = undefined;
return obs;
};
/**
* @private
* @method executeQuery should be called only when connector is ready
*
* @param {DbQuery} dbQuery query information with params
* @param {Observer} observer observer to manage query callback
*
* @return {Observable<QueryResult<any>>} in case of success, {@link QueryResult<any>} is passed
* in case of failure, {@link QueryError} is passed
*/
QueryManager.prototype.executeQuery = function (dbQuery, observer) {
if (this.isInitializationFailed) {
var error = new QueryError('query manager initialization did failed', dbQuery.query, dbQuery.params ? dbQuery.params.join(', ') : '');
observer.error(error);
}
else {
if (this.queryConnector) {
this.queryConnector.query(dbQuery).subscribe(observer);
}
else {
throw (new UnsatisfiedRequirementError('QueryConnector object is missing, check and fix NgDbHelperModule configuration !'));
}
}
};
/**
* @static
* @private
* @property {QueryManager} instance private reference to the model manager instance
*/
QueryManager.instance = new QueryManager();
return QueryManager;
}());
export { QueryManager };
//# sourceMappingURL=query-manager.js.map