server/features/server_administration/middlewares/main/middleware.js

/* eslint-disable security/detect-object-injection */

'use strict';

/**
 * Module dependencies, required for ALL Twyr' modules
 * @ignore
 */

/**
 * Module dependencies, required for this module
 * @ignore
 */
const TwyrBaseMiddleware = require('twyr-base-middleware').TwyrBaseMiddleware;
const TwyrMiddlewareError = require('twyr-middleware-error').TwyrMiddlewareError;

/**
 * @class   Main
 * @extends {TwyrBaseMiddleware}
 * @classdesc The Twyr Web Application Server Tenant Administration Feature Main middleware - manages CRUD for account data.
 *
 *
 */
class Main extends TwyrBaseMiddleware {
	// #region Constructor
	constructor(parent, loader) {
		super(parent, loader);
	}
	// #endregion

	// #region startup/teardown code
	/**
	 * @async
	 * @function
	 * @override
	 * @instance
	 * @memberof ApiService
	 * @name     _setup
	 *
	 * @returns  {null} Nothing.
	 *
	 * @summary  Sets up the broker to manage API exposed by other modules.
	 */
	async _setup() {
		try {
			await super._setup();

			const dbSrvc = this.$dependencies.DatabaseService;
			const self = this; // eslint-disable-line consistent-this

			Object.defineProperty(this, '$ModuleModel', {
				'__proto__': null,
				'configurable': true,

				'value': dbSrvc.Model.extend({
					'tableName': 'modules',
					'idAttribute': 'module_id',
					'hasTimestamps': true,

					'parent': function() {
						return this.belongsTo(self.$ModuleModel, 'parent_module_id');
					},

					'modules': function() {
						return this.hasMany(self.$ModuleModel, 'parent_module_id');
					},

					'permissions': function() {
						return this.hasMany(self.$FeaturePermissionModel, 'module_id');
					},

					'tenant_features': function() {
						return this.hasMany(self.$TenantFeatureModel, 'module_id');
					}
				})
			});

			Object.defineProperty(this, '$FeaturePermissionModel', {
				'__proto__': null,
				'configurable': true,

				'value': dbSrvc.Model.extend({
					'tableName': 'feature_permissions',
					'idAttribute': 'feature_permission_id',
					'hasTimestamps': true,

					'module': function() {
						return this.belongsTo(self.$ModuleModel, 'module_id');
					}
				})
			});

			Object.defineProperty(this, '$TenantModel', {
				'__proto__': null,
				'configurable': true,

				'value': dbSrvc.Model.extend({
					'tableName': 'tenants',
					'idAttribute': 'tenant_id',
					'hasTimestamps': true,

					'tenant_features': function() {
						return this.hasMany(self.$TenantFeatureModel, 'module_id');
					}
				})
			});

			Object.defineProperty(this, '$TenantFeatureModel', {
				'__proto__': null,
				'configurable': true,

				'value': dbSrvc.Model.extend({
					'tableName': 'tenants_features',
					'idAttribute': 'tenant_feature_id',
					'hasTimestamps': true,

					'tenant': function() {
						return this.belongsTo(self.$TenantModel, 'tenant_id');
					},

					'feature': function() {
						return this.belongsTo(self.$ModuleModel, 'module_id');
					}
				})
			});

			return null;
		}
		catch(err) {
			throw new TwyrMiddlewareError(`${this.name}::_setup error`, err);
		}
	}

	/**
	 * @async
	 * @function
	 * @override
	 * @instance
	 * @memberof ApiService
	 * @name     _teardown
	 *
	 * @returns  {undefined} Nothing.
	 *
	 * @summary  Deletes the broker that manages API.
	 */
	async _teardown() {
		try {
			delete this.$FeaturePermissionModel;
			delete this.$ModuleModel;

			await super._teardown();
			return null;
		}
		catch(err) {
			throw new TwyrMiddlewareError(`${this.name}::_teardown error`, err);
		}
	}
	// #endregion

	// #region Protected methods
	async _registerApis() {
		try {
			const ApiService = this.$dependencies.ApiService;

			await ApiService.add(`${this.name}::getModuleTree`, this._getModuleTree.bind(this));
			await ApiService.add(`${this.name}::getModule`, this._getModule.bind(this));
			await ApiService.add(`${this.name}::getModulePermission`, this._getModulePermission.bind(this));

			await super._registerApis();
			return null;
		}
		catch(err) {
			throw new TwyrMiddlewareError(`${this.name}::_registerApis`, err);
		}
	}

	async _deregisterApis() {
		try {
			const ApiService = this.$dependencies.ApiService;

			await ApiService.remove(`${this.name}::getModulePermission`, this._getModulePermission.bind(this));
			await ApiService.remove(`${this.name}::getModule`, this._getModule.bind(this));
			await ApiService.remove(`${this.name}::getModuleTree`, this._getModuleTree.bind(this));

			await super._deregisterApis();
			return null;
		}
		catch(err) {
			throw new TwyrMiddlewareError(`${this.name}::_deregisterApis`, err);
		}
	}
	// #endregion

	// #region API
	async _getModuleTree(ctxt) {
		try {
			let serverModule = this.$parent;
			while(serverModule.$parent) serverModule = serverModule.$parent;

			const configSrvc = this.$dependencies.ConfigurationService;
			const serverModuleId = await configSrvc.getModuleID(serverModule);

			let query = `SELECT module_id AS id, COALESCE(CAST(parent_module_id AS text), '#') AS parent, display_name AS text FROM modules WHERE module_id IN (SELECT module_id FROM fn_get_module_descendants(?) WHERE (type = 'server' OR type = 'feature'))`;
			if(ctxt.state.tenant['sub_domain'] !== 'www') query += ` AND deploy <> 'admin'`;

			const dbSrvc = this.$dependencies.DatabaseService;
			const serverFeatures = await dbSrvc.knex.raw(query, [serverModuleId]);

			return serverFeatures.rows;
		}
		catch(err) {
			throw new TwyrMiddlewareError(`${this.name}::_getModuleTree`, err);
		}
	}

	async _getModule(ctxt) {
		try {
			const ModuleRecord = new this.$ModuleModel({
				'module_id': ctxt.params.feature_id
			});

			let moduleData = await ModuleRecord.fetch({
				'withRelated': ['parent', 'permissions', 'modules', 'tenant_features']
			});

			moduleData = this.$jsonApiMapper.map(moduleData, 'server_administration/features', {
				'typeForModel': {
					'parent': 'server_administration/features',
					'permissions': 'server_administration/feature_permissions',
					'modules': 'server_administration/features',
					'tenant_features': 'tenant_administration/feature_manager/tenant_feature'
				},

				'enableLinks': false
			});

			moduleData.included = moduleData.included.filter((inclData) => {
				return inclData['type'] === 'server_administration/feature_permissions';
			});

			moduleData.data.relationships.features = JSON.parse(JSON.stringify(moduleData.data.relationships.modules));
			return moduleData;
		}
		catch(err) {
			throw new TwyrMiddlewareError(`${this.name}::_getModule`, err);
		}
	}

	async _getModulePermission(ctxt) {
		try {
			const ModulePermissionRecord = new this.$FeaturePermissionModel({
				'feature_permission_id': ctxt.params.feature_permission_id
			});

			let modulePermissionData = await ModulePermissionRecord.fetch({
				'withRelated': ['module']
			});

			modulePermissionData = this.$jsonApiMapper.map(modulePermissionData, 'server_administration/feature-permissions', {
				'typeForModel': {
					'module': 'server_administration/features'
				},

				'enableLinks': false
			});

			delete modulePermissionData.included;
			return modulePermissionData;
		}
		catch(err) {
			throw new TwyrMiddlewareError(`${this.name}::_getModulePermission`, err);
		}
	}
	// #endregion

	// #region Properties
	/**
	 * @override
	 */
	get basePath() {
		return __dirname;
	}
	// #endregion
}

exports.middleware = Main;