UNPKG

globalstorage

Version:

Global Storage is a Global Distributed Data Warehouse

833 lines (718 loc) 29 kB
'use strict'; function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var _require = require('@metarhia/common'), Uint64 = _require.Uint64, iter = _require.iter; var _require2 = require('./errors'), GSError = _require2.GSError, errorCodes = _require2.codes; var _require3 = require('./remote.provider.jstp.api.js'), createRemoteProviderJstpApi = _require3.createRemoteProviderJstpApi; var _require4 = require('./utils'), runIfFn = _require4.runIfFn; var _require5 = require('./log'), prepareForLogging = _require5.prepareForLogging, LogStatus = _require5.LogStatus; var requireLogging = new Set(['get', 'getDetails', 'set', 'create', 'update', 'delete', 'linkDetails', 'unlinkDetails', 'select', 'execute']); var opToUserAccess = { get: 'Read', getDetails: 'Read', set: 'Update', create: 'Insert', update: 'Update', delete: 'Delete', linkDetails: 'Update', unlinkDetails: 'Update', select: 'Read', execute: null }; var fillLogData = { get: function get(args, dest) { dest.Identifier = args[0]; }, getDetails: function getDetails(args, dest) { dest.Category = args[0]; dest.Identifier = args[1]; dest.Query = { getDetails: args[2] }; }, set: function set(args, dest) { dest.Identifier = args[0].Id; dest.Patch = args[0]; }, create: function create(args, dest) { dest.Category = args[0]; dest.Query = args[1]; }, update: function update(args, dest) { dest.Category = args[0]; dest.Query = args[1]; dest.Patch = args[2]; }, delete: function _delete(args, dest) { dest.Category = args[0]; dest.Query = args[1]; }, linkDetails: function linkDetails(args, dest) { dest.Category = args[0]; dest.Query = { linkDetails: args[1], from: args[2], to: args[3] }; }, unlinkDetails: function unlinkDetails(args, dest) { dest.Category = args[0]; dest.Query = { unlinkDetails: args[1], from: args[2], to: args[3] }; }, select: function select(args, dest) { dest.Category = args[0]; dest.Query = args[1]; }, execute: function execute(args, dest) { dest.Category = args[0]; dest.Action = args[1]; dest.Query = args[2][1]; } }; var originalProvider = Symbol('originalProvider'); // Abstract Storage Provider var StorageProvider = /*#__PURE__*/ function () { // Create StorageProvider // options <Object> // serverSuffix <Uint64> optional // serverBitmask <Uint64> optional // systemSuffix <Uint64> optional // systemBitmas <Uint64> optional function StorageProvider(_ref) { var _ref$serverSuffix = _ref.serverSuffix, serverSuffix = _ref$serverSuffix === void 0 ? new Uint64(0) : _ref$serverSuffix, _ref$serverBitmask = _ref.serverBitmask, serverBitmask = _ref$serverBitmask === void 0 ? new Uint64(0xffffff) : _ref$serverBitmask, _ref$systemSuffix = _ref.systemSuffix, systemSuffix = _ref$systemSuffix === void 0 ? null : _ref$systemSuffix, _ref$systemBitmask = _ref.systemBitmask, systemBitmask = _ref$systemBitmask === void 0 ? null : _ref$systemBitmask, _ref$systemLogger = _ref.systemLogger, systemLogger = _ref$systemLogger === void 0 ? function () { var _console; return (_console = console).error.apply(_console, arguments); } : _ref$systemLogger; _classCallCheck(this, StorageProvider); this.active = false; this.cursorFactory = null; this.schema = null; this.systemLogger = systemLogger || function () {}; this.serverSuffix = serverSuffix; this.serverBitmask = serverBitmask; this.systemSuffix = systemSuffix; this.systemBitmask = systemBitmask; this.serverBitmaskSize = this.serverBitmask.toString(2).length; } // Open StorageProvider // options - <Object> // schema - <Metaschema> // callback - <Function> // err - <Error> | <null> // provider - <StorageProvider> _createClass(StorageProvider, [{ key: "open", value: function open(_ref2, callback) { var schema = _ref2.schema; this.schema = schema; callback(); } // Close StorageProvider // callback - <Function> // err - <Error> | <null> // eslint-disable-next-line no-unused-vars }, { key: "close", value: function close(callback) { throw new GSError(errorCodes.NOT_IMPLEMENTED); } // Setup StorageProvider // callback - <Function> // options - <Object> // err - <Error> | <null> // eslint-disable-next-line no-unused-vars }, { key: "setup", value: function setup(options, callback) { throw new GSError(errorCodes.NOT_IMPLEMENTED); } }, { key: "enableLogging", value: function enableLogging(ctx) { return new Proxy(this, { get: function get(provider, prop, proxy) { if (prop === originalProvider) return provider; if (prop === 'loggingEnabled') return true; if (typeof provider[prop] !== 'function' || !requireLogging.has(prop)) { return provider[prop]; } if (prop === 'select') { return function () { var _provider$select; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } provider.log('select', args, ctx); return (_provider$select = provider.select).call.apply(_provider$select, [proxy].concat(args)).enableLogging(proxy, ctx, args); }; } return function () { var _provider$prop; for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } var callback, permissionChecker; if (args.length === provider[prop].length) { permissionChecker = args.pop(); callback = args.pop(); } else { callback = args.pop(); } provider.log(prop, args, ctx); (_provider$prop = provider[prop]).call.apply(_provider$prop, [proxy].concat(args, [function (err, result) { provider.log(prop, args, ctx, err || result); callback(LogStatus.strip(err), LogStatus.strip(result)); }, permissionChecker])); }; } }); } // Generate globally unique id // callback - <Function> // err - <Error> | <null> // id - <string> // eslint-disable-next-line no-unused-vars }, { key: "takeId", value: function takeId(callback) { throw new GSError(errorCodes.NOT_IMPLEMENTED); } // Get object from GlobalStorage // id - <string>, globally unique object id // callback - <Function> // err - <Error> | <null> // obj - <Object> // permissionChecker - <Function>, optional // category - <string> // options - <Object> // callback - <Function> // err - <Error> | <null> // eslint-disable-next-line no-unused-vars }, { key: "get", value: function get(id, callback, permissionChecker) { throw new GSError(errorCodes.NOT_IMPLEMENTED); } // Get details for many-to-many link from GlobalStorage // category - <string>, category to get details in // id - <string>, object id // fieldName - <string>, field with the Many decorator // callback - <Function> // err - <Error> | <null> // details - <Object[]> // permissionChecker - <Function>, optional // category - <string> // options - <Object> // callback - <Function> // err - <Error> | <null> // eslint-disable-next-line no-unused-vars }, { key: "getDetails", value: function getDetails(category, id, fieldName, callback, permissionChecker) { throw new GSError(errorCodes.NOT_IMPLEMENTED); } // Set object in GlobalStorage // obj - <Object>, to be stored // callback - <Function> // err - <Error> | <null> // permissionChecker - <Function>, optional // category - <string> // options - <Object> // callback - <Function> // err - <Error> | <null> // eslint-disable-next-line no-unused-vars }, { key: "set", value: function set(obj, callback, permissionChecker) { throw new GSError(errorCodes.NOT_IMPLEMENTED); } // Create object in GlobalStorage // category - <string>, category to store the object in // obj - <Object>, to be stored // callback - <Function> // err - <Error> | <null> // id - <string> // permissionChecker - <Function>, optional // category - <string> // options - <Object> // callback - <Function> // err - <Error> | <null> // eslint-disable-next-line no-unused-vars }, { key: "create", value: function create(category, obj, callback, permissionChecker) { throw new GSError(errorCodes.NOT_IMPLEMENTED); } // Update object in GlobalStorage // category - <string>, category to update the records in // query - <Object>, example: { Id } // patch - <Object>, fields to update // callback - <Function> // err - <Error> | <null> // count - <number> // permissionChecker - <Function>, optional // category - <string> // options - <Object> // callback - <Function> // err - <Error> | <null> // eslint-disable-next-line no-unused-vars }, { key: "update", value: function update(category, query, patch, callback, permissionChecker) { throw new GSError(errorCodes.NOT_IMPLEMENTED); } // Delete object in GlobalStorage // category - <string>, category to delete the records from // query - <Object>, example: { Id } // callback - <Function> // err - <Error> | <null> // count - <number> // permissionChecker - <Function>, optional // category - <string> // options - <Object> // callback - <Function> // err - <Error> | <null> // eslint-disable-next-line no-unused-vars }, { key: "delete", value: function _delete(category, query, callback, permissionChecker) { throw new GSError(errorCodes.NOT_IMPLEMENTED); } // Link records with Many relation between them // category - <string>, category with field having the Many decorator // field - <string>, field with the Many decorator // fromId - <Uint64>, Id of the record in category specified in the first // argument // toIds - <Uint64> | <Uint64[]>, Id(s) of the record(s) in category // specified in the Many decorator of the specified field // callback - <Function> // err - <Error> | <null> // permissionChecker - <Function>, optional // category - <string> // options - <Object> // callback - <Function> // err - <Error> | <null> // eslint-disable-next-line no-unused-vars }, { key: "linkDetails", value: function linkDetails(category, field, fromId, toIds, callback, permissionChecker) { throw new GSError(errorCodes.NOT_IMPLEMENTED); } // Unlink records with Many relation between them // category - <string>, category with field having the Many decorator // field - <string>, field with the Many decorator // fromId - <Uint64>, Id of the record in category specified in the first // argument // toIds - <Uint64> | <Uint64[]>, Id(s) of the record(s) in category // specified in the Many decorator of the specified field // callback - <Function> // err - <Error> | <null> // permissionChecker - <Function>, optional // category - <string> // options - <Object> // callback - <Function> // err - <Error> | <null> // eslint-disable-next-line no-unused-vars }, { key: "unlinkDetails", value: function unlinkDetails(category, field, fromId, toIds, callback, permissionChecker) { throw new GSError(errorCodes.NOT_IMPLEMENTED); } // Select objects from GlobalStorage // category - <string>, category to select the records from // query - <Object>, fields conditions // permissionChecker - <Function>, optional // category - <string> // options - <Object> // callback - <Function> // err - <Error> | <null> // // Returns: <Cursor> // eslint-disable-next-line no-unused-vars }, { key: "select", value: function select(category, query, permissionChecker) { throw new GSError(errorCodes.NOT_IMPLEMENTED); } // Execute an action // category <string> | <null> category name or null to execute public action // action <string> action name // actionArgs <Array> // session <jstp.Session> // args <Object> // callback <Function> // error <Error> | <null> // result <any> // permissionChecker - <Function>, optional // category - <string> // action - <string> // callback - <Function> // err - <Error> | <null> }, { key: "execute", value: function execute(category, action, actionArgs, callback, permissionChecker) { var _this = this; runIfFn(permissionChecker, category, action, function (err) { if (err) { callback(err); return; } var foundAction; if (category === null) { foundAction = _this.schema.actions.get(action); } else { var categorySchema = _this.schema.categories.get(category); if (!categorySchema) { callback(new GSError(errorCodes.NOT_FOUND)); return; } foundAction = categorySchema.actions.get(action); } if (!foundAction) { callback(new GSError(errorCodes.NOT_FOUND)); return; } var error; var _this$schema$createAn = _this.schema.createAndValidate('action', foundAction, actionArgs[1]); var _this$schema$createAn2 = _slicedToArray(_this$schema$createAn, 2); error = _this$schema$createAn2[0]; actionArgs[1] = _this$schema$createAn2[1]; if (error) { process.nextTick(callback, new GSError(errorCodes.INVALID_SCHEMA, "Invalid arguments provided: ".concat(error))); return; } var execute = foundAction.definition.Execute; execute.apply(void 0, [_this].concat(_toConsumableArray(actionArgs), [_this.loggingEnabled ? callback : function () { for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } callback.apply(void 0, _toConsumableArray(args.map(LogStatus.strip))); }])); }); } }, { key: "log", value: function log(op, args, ctx, response) { var _this2 = this; var hasStatus = response instanceof LogStatus; var record = _objectSpread({}, ctx, { DateTime: new Date(), Operation: opToUserAccess[op], Response: !hasStatus ? response : response.value, Status: !hasStatus ? 'info' : response.status, ServerId: this.serverSuffix }); fillLogData[op](args, record); var provider = this[originalProvider] || this; prepareForLogging(provider, record); provider.create('Log', record, function (err) { if (err) { _this2.systemLogger(err); } }); } // Get system suffix for given id // id - <common.Uint64> // // Returns: <common.Uint64> }, { key: "getSystemSuffix", value: function getSystemSuffix(id) { return Uint64.and(id, this.systemBitmask); } // Check whether data with given id is stored on this system // id - <common.Uint64> // // Returns: <boolean> }, { key: "curSystem", value: function curSystem(id) { return Uint64.cmp(this.getSystemSuffix(id), this.systemSuffix) === 0; } // Get server suffix for given id // id - <common.Uint64> // // Returns: <common.Uint64> }, { key: "getServerSuffix", value: function getServerSuffix(id) { return Uint64.and(id, this.serverBitmask); } // Check whether data with given id is stored on this server // id - <common.Uint64> // // Returns: <boolean> }, { key: "curServer", value: function curServer(id) { return Uint64.cmp(this.getServerSuffix(id), this.serverSuffix) === 0; } // Get id without system and server suffix // id - <common.Uint64> // // Returns: <common.Uint64> }, { key: "getLocalId", value: function getLocalId(id) { return Uint64.and(Uint64.not(this.serverBitmask), id); } // Parse id // id - <common.Uint64> // // Returns: <Object> // systemSuffix - <common.Uint64>, system suffix for given id // serverSuffix - <common.Uint64>, server suffix for given id // localId - <common.Uint64>, id without system and server suffix }, { key: "parseId", value: function parseId(id) { return { systemSuffix: this.getSystemSuffix(id), serverSuffix: this.getServerSuffix(id), localId: this.getLocalId(id) }; } // List all available applications // callback - <Function> // err - <Error> | <null> // applications - <string[]> // filtererByRoles - <Function> optional // applications - <string[]> // callback - <Function> // err - <Error> | <null> // applications - <string[]> }, { key: "listApplications", value: function listApplications(callback, filtererByRoles) { var result = _toConsumableArray(this.schema.applications.keys()); if (filtererByRoles) { filtererByRoles(result, callback); } else { process.nextTick(callback, null, result); } } // List all available categories // callback - <Function> // err - <Error> | <null> // categories - <string[]> // filtererByPermission - <Function> optional // categories - <string[]> // callback - <Function> // err - <Error> | <null> // categories - <string[]> }, { key: "listCategories", value: function listCategories(callback, filtererByPermission) { var result = _toConsumableArray(this.schema.categories.keys()); if (filtererByPermission) { filtererByPermission(result, callback); } else { process.nextTick(callback, null, result); } } // List all available actions // callback - <Function> // err - <Error> | <null> // actions - <Object> // public - <string[]> // private - <Object> // [categoryName] - <string[]> // filtererByPermission - <Function> optional // actions - <Object> // callback - <Function> // err - <Error> | <null> // actions - <Object> }, { key: "listActions", value: function listActions(callback, filtererByPermission) { var result = { public: _toConsumableArray(this.schema.actions.keys()), private: {} }; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = this.schema.categories[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _step$value = _slicedToArray(_step.value, 2), key = _step$value[0], value = _step$value[1]; if (value.actions.size !== 0) { result.private[key] = _toConsumableArray(value.actions.keys()); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } if (filtererByPermission) { filtererByPermission(result, callback); } else { process.nextTick(callback, null, result); } } }, { key: "createJstpApi", value: function createJstpApi() { return createRemoteProviderJstpApi(this, this.cursorFactory); } }, { key: "getSchemaSources", value: function getSchemaSources(callback) { var _this3 = this; var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, categoryList = _ref3.categoryList, actionLists = _ref3.actionLists, appList = _ref3.appList; process.nextTick(callback, null, iter(this.schema.schemas).filter(function (schema) { if (!schema.source) return false; if (categoryList && schema.type === 'category') { return categoryList.includes(schema.name); } if (appList && schema.type === 'application') { return appList.includes(schema.name); } if (actionLists) { if (schema.type === 'action') { if (!schema.category) { return actionLists.public.includes(schema.name); } else { var categoryActions = actionLists.private[schema.category]; return categoryActions && categoryActions.includes(schema.name); } } else if (schema.type === 'form') { var _categoryActions = actionLists.private[schema.category]; if (!_categoryActions) return false; var _this3$schema$categor = _this3.schema.categories.get(schema.category).forms.get(schema.name), usedIn = _this3$schema$categor.usedIn; return usedIn.some(function (action) { return _categoryActions.includes(action); }); } } return true; }).map(function (schema) { return { type: schema.type, module: schema.module, name: schema.name, source: schema.source, category: schema.category }; }).toArray()); } }, { key: "getCategoryL10n", value: function getCategoryL10n(langTag, category, callback) { var categorySchema = this.schema.categories.get(category); if (!categorySchema) { process.nextTick(callback, new GSError(errorCodes.NOT_FOUND, "No category ".concat(category, " found"))); return; } var l10n = categorySchema.resources.get(langTag); if (!l10n) { process.nextTick(callback, new GSError(errorCodes.NOT_FOUND, "No ".concat(langTag, " localization data for ").concat(category, " category"))); return; } process.nextTick(callback, null, l10n); } }, { key: "getDomainsL10n", value: function getDomainsL10n(langTag, callback) { var l10n = this.schema.resources.domains.get(langTag); if (!l10n) { process.nextTick(callback, new GSError(errorCodes.NOT_FOUND, "No ".concat(langTag, " localization data for domains"))); return; } process.nextTick(callback, null, l10n); } }, { key: "getCommonL10n", value: function getCommonL10n(langTag, callback) { var l10n = this.schema.resources.common.get(langTag); if (!l10n) { process.nextTick(callback, new GSError(errorCodes.NOT_FOUND, "No ".concat(langTag, " localization data for common"))); return; } process.nextTick(callback, null, l10n); } }, { key: "getFormL10n", value: function getFormL10n(langTag, category, form, callback) { var categorySchema = this.schema.categories.get(category); if (!categorySchema) { process.nextTick(callback, new GSError(errorCodes.NOT_FOUND, "No category ".concat(category, " found"))); return; } var formSchema = categorySchema.forms.get(form); if (!formSchema) { process.nextTick(callback, new GSError(errorCodes.NOT_FOUND, "No form ".concat(category, ".").concat(form, " found"))); return; } var l10n = formSchema.resources.get(langTag); if (!l10n) { process.nextTick(callback, new GSError(errorCodes.NOT_FOUND, "No ".concat(langTag, " localization data for ").concat(category, ".").concat(form, " form"))); return; } process.nextTick(callback, null, l10n); } }, { key: "getActionL10n", value: function getActionL10n(langTag, category, action, callback) { if (category === null) { var _actionSchema = this.schema.actions.get(action); if (!_actionSchema) { process.nextTick(callback, new GSError(errorCodes.NOT_FOUND, "No public action ".concat(action, " found"))); return; } var _l10n = _actionSchema.resources.get(langTag); if (!_l10n) { process.nextTick(callback, new GSError(errorCodes.NOT_FOUND, "No ".concat(langTag, " localization data for public action ").concat(action))); return; } process.nextTick(callback, null, _l10n); return; } var categorySchema = this.schema.categories.get(category); if (!categorySchema) { process.nextTick(callback, new GSError(errorCodes.NOT_FOUND, "No category ".concat(category, " found"))); return; } var actionSchema = categorySchema.actions.get(action); if (!actionSchema) { process.nextTick(callback, new GSError(errorCodes.NOT_FOUND, "No action ".concat(category, ".").concat(action, " found"))); return; } var l10n = actionSchema.resources.get(langTag); if (!l10n) { process.nextTick(callback, new GSError(errorCodes.NOT_FOUND, "No ".concat(langTag, " localization data for ").concat(category, ".").concat(action, " action"))); return; } process.nextTick(callback, null, l10n); } }]); return StorageProvider; }(); module.exports = { StorageProvider: StorageProvider };