UNPKG

@themost/data

Version:

MOST Web Framework Codename Blueshift - Data module

406 lines (381 loc) 12.4 kB
// MOST Web Framework 2.0 Codename Blueshift BSD-3-Clause license Copyright (c) 2017-2022, THEMOST LP All rights reserved var _ = require('lodash'); var {TraceUtils} = require('@themost/common'); var {LangUtils} = require('@themost/common'); var {DataContext} = require('./types'); var {DataConfigurationStrategy} = require('./data-configuration'); var cfg = require('./data-configuration'); var Symbol = require('symbol'); var nameProperty = Symbol('name'); var { DataModel } = require('./data-model'); /** * @classdesc Represents the default data context of MOST Data Applications. * The default data context uses the adapter which is registered as the default adapter in application configuration. * @description ``` adapters: [ ... { "name":"development", "invariantName":"...", "default":false, "options": { "server":"localhost", "user":"user", "password":"password", "database":"test" } }, { "name":"development_with_pool", "invariantName":"pool", "default":true, "options": { "adapter":"development" } } ... ] ``` * @class * @constructor * @augments {DataContext} * @property {DataAdapter} db - Gets a data adapter based on the current configuration settings. */ function DefaultDataContext() { DefaultDataContext.super_.bind(this)(); /** * @type {import('./types').DataAdapter} */ var _db= null; /** * @name DataAdapter#hasConfiguration * @type {Function} * @param {Function} getConfigurationFunc */ /** * @private */ this._finalize = function(cb) { if (_db) { return _db.close(function(err) { // destroy db context _db = null; return cb(err); }); } return cb(); }; var self = this; // set data context name with respect to DataContext implementation var _name = 'default'; Object.defineProperty(this, 'name', { enumerable: false, configurable: true, get: function() { return _name; } }); self.getDb = function() { if (_db) return _db; var er; //otherwise load database options from configuration var strategy = self.getConfiguration().getStrategy(DataConfigurationStrategy); var adapter = _.find(strategy.adapters, function(x) { return x['default']; }); if (_.isNil(adapter)) { er = new Error('The default data adapter is missing.'); er.code = 'EADAPTER'; throw er; } /** * @type {*} */ var adapterType = strategy.adapterTypes.get(adapter.invariantName); //validate data adapter type if (adapterType == null) { er = new Error('Invalid adapter type.'); er.code = 'ERR_ADAPTER_TYPE'; throw er; } // get data adapter instance var createInstance = adapterType.createInstance; // get adapter type constructor if any var AdapterTypeCtor = adapterType.type; // create adapter instance if (typeof AdapterTypeCtor === 'function') { _db = new AdapterTypeCtor(adapter.options); } else if (typeof createInstance === 'function') { _db = createInstance(adapter.options); } else { // throw error var err = new Error('The given adapter type is invalid. Adapter type constructor is undefined.'); err.code = 'ERR_ADAPTER_TYPE'; throw err; } if (typeof _db.hasConfiguration === 'function') { _db.hasConfiguration(function() { return self.getConfiguration(); }); } return _db; }; self.setDb = function(value) { /** * @type {DataAdapter|*} */ _db = value; if (_db) { if (typeof _db.hasConfiguration === 'function') { _db.hasConfiguration(function() { return self.getConfiguration(); }); } } }; delete self.db; Object.defineProperty(self, 'db', { get: function() { return self.getDb(); }, set: function(value) { self.setDb(value); }, configurable: true, enumerable:false }); } LangUtils.inherits(DefaultDataContext, DataContext); /** * Gets an instance of DataConfiguration class which is associated with this data context * @returns {import('@themost/common').ConfigurationBase} */ DefaultDataContext.prototype.getConfiguration = function() { return cfg.getCurrent(); }; /** * Gets an instance of DataModel class based on the given name. * @param {*} name - A variable that represents the model name. * @returns {DataModel} - An instance of DataModel class associated with this data context. */ DefaultDataContext.prototype.model = function(name) { var self = this; if (name == null) { return null; } var modelName = name; // if model is a function (is a constructor) if (typeof name === 'function') { // try to get EdmMapping.entityType() decorator if (Object.prototype.hasOwnProperty.call(name, 'entityTypeDecorator')) { // if entityTypeDecorator is string if (typeof name.entityTypeDecorator === 'string') { // get model name modelName = name.entityTypeDecorator; } } else { // get function name as the requested model modelName = name.name; } } var obj = self.getConfiguration().getStrategy(DataConfigurationStrategy).model(modelName); if (_.isNil(obj)) return null; var model = new DataModel(obj); //set model context model.context = self; //return model return model; }; /** * Finalizes the current data context * @param {Function} callback - A callback function where the first argument will contain the Error object if an error occurred, or null otherwise. */ DefaultDataContext.prototype.finalize = function(callback) { callback = callback || function () {}; try { // noinspection JSPotentiallyInvalidConstructorUsage const superFinalize = DefaultDataContext.super_.prototype.finalize; void superFinalize.call(this, () => { void this._finalize(function(err) { if (err) { TraceUtils.error('An error occurred while finalizing the underlying database context.'); TraceUtils.error(err); } return callback(); }); }); } catch (err) { return callback(err); } }; /** * @classdesc Represents a data context based on a data adapter's name. * The specified adapter name must be registered in application configuration. * @class * @constructor * @augments DataContext * @property {DataAdapter} db - Gets a data adapter based on the given adapter's name. */ function NamedDataContext(name) { NamedDataContext.super_.bind(this)(); /** * @type {DataAdapter} * @private */ var _db; /** * @private */ this._finalize = function(cb) { if (_db) { return _db.close(function(err) { // destroy db context _db = null; return cb(err); }); } return cb(); }; var self = this; self[nameProperty] = name; self.getDb = function() { if (_db) return _db; var strategy = self.getConfiguration().getStrategy(DataConfigurationStrategy); //otherwise load database options from configuration var adapter = strategy.adapters.find(function(x) { return x.name === self[nameProperty]; }); var er; if (typeof adapter ==='undefined' || adapter===null) { er = new Error('The specified data adapter is missing.'); er.code = 'EADAPTER'; throw er; } //get data adapter type var adapterType = strategy.adapterTypes.get(adapter.invariantName); // get data adapter instance var createInstance = adapterType.createInstance; // get adapter type constructor if any var AdapterTypeCtor = adapterType.type; // create adapter instance if (typeof AdapterTypeCtor === 'function') { _db = new AdapterTypeCtor(adapter.options); } else if (typeof createInstance === 'function') { _db = createInstance(adapter.options); } else { // throw error var err = new Error('The given adapter type is invalid. Adapter type constructor is undefined.'); err.code = 'ERR_ADAPTER_TYPE'; throw err; } if (typeof _db.hasConfiguration === 'function') { _db.hasConfiguration(function() { return self.getConfiguration(); }); } return _db; }; /** * @param {DataAdapter|*} value */ self.setDb = function(value) { _db = value; if (_db) { if (typeof _db.hasConfiguration === 'function') { _db.hasConfiguration(function() { return self.getConfiguration(); }); } } }; /** * @name NamedDataContext#db * @type {DataAdapter} */ Object.defineProperty(self, 'db', { get : function() { return self.getDb(); }, set : function(value) { self.setDb(value); }, configurable : true, enumerable:false }); /** * @name NamedDataContext#name * @type {string} */ Object.defineProperty(self, 'name', { get: function () { return self[nameProperty]; } }); } LangUtils.inherits(NamedDataContext, DataContext); /** * Gets a string which represents the name of this context * @returns {string} */ NamedDataContext.prototype.getName = function() { return this[nameProperty]; }; /** * Gets an instance of DataConfiguration class which is associated with this data context * @returns {DataConfiguration} */ NamedDataContext.prototype.getConfiguration = function() { return cfg.getNamedConfiguration(this.name); }; /** * Gets an instance of DataModel class based on the given name. * @param name {string} - A string that represents the model name. * @returns {DataModel} - An instance of DataModel class associated with this data context. */ NamedDataContext.prototype.model = function(name) { var self = this; if (name == null) { return null; } var modelName = name; // if model is a function (is a constructor) if (typeof name === 'function') { // try to get EdmMapping.entityType() decorator if (Object.prototype.hasOwnProperty.call(name, 'entityTypeDecorator')) { // if entityTypeDecorator is string if (typeof name.entityTypeDecorator === 'string') { // get model name modelName = name.entityTypeDecorator; } } else { // get function name as the requested model modelName = name.name; } } var obj = self.getConfiguration().getStrategy(DataConfigurationStrategy).model(modelName); if (_.isNil(obj)) return null; var model = new DataModel(obj); //set model context model.context = self; //return model return model; }; NamedDataContext.prototype.finalize = function(callback) { callback = callback || function () {}; try { // noinspection JSPotentiallyInvalidConstructorUsage const superFinalize = NamedDataContext.super_.prototype.finalize; void superFinalize.call(this, () => { void this._finalize(function(err) { if (err) { TraceUtils.error('An error occurred while finalizing the underlying database context.'); TraceUtils.error(err); } return callback(); }); }); } catch (err) { return callback(err); } }; module.exports = { DefaultDataContext, NamedDataContext }