UNPKG

globalstorage

Version:

Global Storage is a Global Distributed Data Warehouse

1,067 lines (897 loc) 38.5 kB
'use strict'; function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(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 _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 _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; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); } function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } var fs = require('fs'); var path = require('path'); var common = require('@metarhia/common'); var _require = require('metaschema'), extractDecorator = _require.extractDecorator; var metasync = require('metasync'); var pg = require('pg'); var _require2 = require('./pg.ddl'), generateDDL = _require2.generateDDL; var _require3 = require('./errors'), GSError = _require3.GSError, errorCodes = _require3.codes; var _require4 = require('./provider'), StorageProvider = _require4.StorageProvider; var _require5 = require('./pg.utils'), generateQueryParams = _require5.generateQueryParams, generateLinkQueryParams = _require5.generateLinkQueryParams, escapeIdentifier = _require5.escapeIdentifier, buildWhere = _require5.buildWhere, generateDeleteQuery = _require5.generateDeleteQuery, _require5$symbols = _require5.symbols, recreateIdTrigger = _require5$symbols.recreateIdTrigger, uploadMetadata = _require5$symbols.uploadMetadata; var _require6 = require('./pg.cursor'), PostgresCursor = _require6.PostgresCursor; var _require7 = require('./schema.utils'), isGlobalCategory = _require7.isGlobalCategory, isIgnoredCategory = _require7.isIgnoredCategory, getCategoryRealm = _require7.getCategoryRealm, getCategoryFamily = _require7.getCategoryFamily, constructActions = _require7.constructActions, extractIncludeCategoriesData = _require7.extractIncludeCategoriesData, extractIncludeCategories = _require7.extractIncludeCategories; var _require8 = require('./ddl.utils'), manyToManyTableName = _require8.manyToManyTableName; var _require9 = require('./utils'), runIfFn = _require9.runIfFn, runIf = _require9.runIf; var PostgresProvider = /*#__PURE__*/ function (_StorageProvider) { _inherits(PostgresProvider, _StorageProvider); // Create PostgresProvider function PostgresProvider(options) { var _this; _classCallCheck(this, PostgresProvider); _this = _possibleConstructorReturn(this, _getPrototypeOf(PostgresProvider).call(this, options)); _this.pool = null; _this.cursorFactory = function (provider, category, jsql) { return new PostgresCursor(provider, { category: category, jsql: jsql }); }; return _this; } // Open PostgresProvider // options - <Object>, to be passed to pg // callback - <Function> // err - <Error> | <null> // provider - <this> _createClass(PostgresProvider, [{ key: "open", value: function open(options, callback) { var _this2 = this; _get(_getPrototypeOf(PostgresProvider.prototype), "open", this).call(this, options, function (err) { if (err) { callback(err, _this2); return; } _this2.pool = new pg.Pool(options); _this2.active = true; process.nextTick(callback, null, _this2); }); } // Close PostgresProvider // callback - <Function> // err - <Error> | <null> }, { key: "close", value: function close(callback) { var _this3 = this; if (!this.pool) { callback(); return; } this.pool.end(function () { _this3.pool = null; _this3.active = false; callback(); }); } // Setup StorageProvider // options - <Object> // maxIdCount - <number> // refillPercent - <number> // callback - <Function> // err - <Error> | <null> }, { key: "setup", value: function setup(options, callback) { var _this4 = this; var _ref = options || {}, _ref$maxIdCount = _ref.maxIdCount, maxIdCount = _ref$maxIdCount === void 0 ? 1000 : _ref$maxIdCount, _ref$refillPercent = _ref.refillPercent, refillPercent = _ref$refillPercent === void 0 ? 30 : _ref$refillPercent; metasync.sequential([function (ctx, cb) { fs.readFile(path.join(__dirname, '..', 'sql', 'id.sql'), 'utf8', function (err, initSql) { ctx.initSql = initSql; cb(err); }); }, function (ctx, cb) { _this4.pool.query(ctx.initSql, function (err) { cb(err); }); }, function (cb) { _this4.pool.query(generateDDL(_this4.schema), function (err) { cb(err); }); }, function (cb) { _this4[recreateIdTrigger](maxIdCount, refillPercent, cb); }, function (cb) { _this4[uploadMetadata](cb); }], function (err) { if (err && !(err instanceof GSError)) { _this4.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } else { callback(err); } }); } }, { key: recreateIdTrigger, value: function value(maxIdCount, refillPercent, callback) { var _this5 = this; this.pool.query('DROP TRIGGER IF EXISTS idgen ON "Identifier"', function (err) { if (err) { _this5.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } _this5.pool.query('SELECT trigger_creator($1, $2, $3, $4)', [maxIdCount, refillPercent, _this5.serverSuffix, _this5.serverBitmaskSize], function (err) { if (err) { _this5.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } else { callback(); } }); }); } }, { key: uploadMetadata, value: function value(callback) { var _this6 = this; var categories = common.iter(this.schema.categories.values()).filter(function (_ref2) { var value = _ref2.definition; return !isIgnoredCategory(value); }).map(function (_ref3) { var Name = _ref3.name, value = _ref3.definition; return { Name: Name, Realm: getCategoryRealm(value), Family: getCategoryFamily(value), // TODO: remove Version when metaschema will be able to work with the // default values Version: 0 }; }).toArray(); var applications = common.iter(this.schema.applications.values()).map(function (_ref4) { var Name = _ref4.name, value = _ref4.definition; return { Name: Name, Categories: value.Categories.map(function (name) { return categories.find(function (c) { return c.Name === name; }); }) }; }).toArray(); var _categories$splice = categories.splice(categories.findIndex(function (c) { return c.Name === 'Category'; }), 1), _categories$splice2 = _slicedToArray(_categories$splice, 1), Category = _categories$splice2[0]; metasync.sequential([function (callback) { _this6.create('Category', Category, function (err, id) { Category.Id = id; callback(err); }); }, function (callback) { _this6.update('Identifier', { Id: Category.Id }, { Category: Category.Id }, function (err) { callback(err); }); }, function (callback) { metasync.each(categories, function (value, callback) { _this6.create('Category', value, function (err, id) { value.Id = id; callback(err); }); }, function (err) { callback(err); }); }, function (callback) { metasync.each(common.iter(categories).flatMap(function (c) { return constructActions(_this6.schema.categories.get(c.Name).actions, false, c.Id); }).chain(constructActions(_this6.schema.actions, true)).toArray(), function (value, callback) { _this6.create('Action', value, function (err) { callback(err); }); }, function (err) { callback(err); }); }, function (callback) { metasync.each(applications, function (app, callback) { _this6.create('Application', { Name: app.Name }, function (err, id) { if (err) { callback(err); return; } _this6.linkDetails('Application', 'Categories', id, app.Categories.map(function (c) { return c.Id; }), function (err) { callback(err); }); }); }, function (err) { callback(err); }); }], function (err) { if (err) { _this6.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } else { callback(); } }); } // Generate globally unique id // client - <pg.Pool> | <pg.Client> // callback - <Function> // err - <Error> | <null> // id - <string> }, { key: "takeId", value: function takeId(client, callback) { var takeIdQuery = 'UPDATE "Identifier"' + ' SET "Status" = \'Init\', "Change" = CURRENT_TIMESTAMP' + ' WHERE "Id" = (SELECT "Id"' + ' FROM "Identifier"' + ' WHERE "Status" = \'Prealloc\' AND "StorageKind" = \'Master\'' + ' ORDER BY "Id" LIMIT 1' + ' FOR UPDATE SKIP LOCKED) RETURNING "Id"'; client.query(takeIdQuery, function (err, res) { if (err) { callback(err); return; } if (res.rowCount === 0) { callback(new GSError(errorCodes.NOT_FOUND, 'Cannot get Id to use for object creation')); return; } callback(null, res.rows[0].Id); }); } }, { key: "getCategoryById", value: function getCategoryById(id, callback) { var _this7 = this; var categoryQuery = 'SELECT "Category"."Name"' + ' FROM "Identifier", "Category"' + ' WHERE "Identifier"."Category" = "Category"."Id" AND' + ' "Identifier"."Id" = $1'; this.pool.query(categoryQuery, [id], function (err, res) { if (err) { _this7.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); return; } if (res.rowCount === 0) { callback(new GSError(errorCodes.NOT_FOUND, "No object with Id ".concat(id, " available"))); return; } var Name = res.rows[0].Name; callback(null, Name); }); } // 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> }, { key: "get", value: function get(id, callback, permissionChecker) { var _this8 = this; this.getCategoryById(id, function (err, category) { if (err) { callback(err); return; } _this8.select(category, _defineProperty({}, "".concat(category, ".Id"), id)).fetch(function (err, rows) { if (err) { callback(err); return; } if (rows.length === 0) { callback(new GSError(errorCodes.NOT_FOUND, "No object with Id ".concat(id, " available"))); return; } var result = rows[0]; runIfFn(permissionChecker, category, { record: result }, function (err) { if (err) { callback(err); } else { callback(null, result); } }); }); }); } // 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> }, { key: "getDetails", value: function getDetails(category, id, fieldName, callback, permissionChecker) { var _this9 = this; var categorySchema = this.schema.categories.get(category); if (!categorySchema) { callback(new GSError(errorCodes.NOT_FOUND, "No category ".concat(category, " available"))); return; } var categoryDefinition = categorySchema.definition; var categoryField = categoryDefinition[fieldName]; if (!categoryField || extractDecorator(categoryField) !== 'Many') { callback(new GSError(errorCodes.NOT_FOUND, "No 'Many' field ".concat(fieldName, " in object with Id ").concat(id, " available"))); return; } var rightCategory = categoryField.category; var rightCategorySchema = this.schema.categories.get(rightCategory); var requiresFiltering = false; runIf(permissionChecker, function (callback) { var args = [[category, { id: id }]]; if (!rightCategorySchema.catalog && !rightCategorySchema.subsystem) { args.push([rightCategory, null]); } else { requiresFiltering = true; } metasync.each(args, function (args, callback) { permissionChecker.apply(void 0, _toConsumableArray(args).concat([callback])); }, function (err) { callback(err); }); }, function (err) { if (err) { callback(err); return; } var escapedRightCategory = escapeIdentifier(rightCategory); var escapedManyTableName = escapeIdentifier(manyToManyTableName(category, rightCategory, fieldName)); _this9.pool.query("SELECT ".concat(escapedRightCategory, ".* FROM ").concat(escapedRightCategory, " ") + "INNER JOIN ".concat(escapedManyTableName, " ON ").concat(escapedRightCategory, ".\"Id\" =") + " ".concat(escapedManyTableName, ".").concat(escapeIdentifier(fieldName)) + " WHERE ".concat(escapedManyTableName) + ".".concat(escapeIdentifier(category), " = $1"), [id], function (err, res) { if (err) { _this9.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } else if (requiresFiltering) { metasync.filter(res.rows, function (record, callback) { permissionChecker(rightCategory, { record: record }, function (err) { if (err) { if (err.code === errorCodes.INSUFFICIENT_PERMISSIONS) { callback(null, false); } else { callback(err); } } else { callback(null, true); } }); }, callback); } else { callback(null, res.rows); } }); }); } // 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> }, { key: "set", value: function set(obj, callback, permissionChecker) { var _this10 = this; if (!obj.Id) { throw new TypeError('Id is not provided'); } var updateRecord = function updateRecord(category, obj, client, callback) { var categoryDefinition = _this10.schema.categories.get(category).definition; var fields = Object.keys(obj).filter(function (key) { return key !== 'Id' && extractDecorator(categoryDefinition[key]) !== 'Include'; }); var values = fields.map(function (key) { return obj[key]; }); values.unshift(obj.Id); fields = fields.map(escapeIdentifier); var setQuery = "UPDATE ".concat(escapeIdentifier(category)) + " SET (".concat(fields.join(', '), ") =") + " ROW (".concat(generateQueryParams(fields.length, 2), ")") + ' WHERE "Id" = $1'; client.query(setQuery, values, function (err) { callback(err); }); }; this.getCategoryById(obj.Id, function (err, category) { if (err) { callback(err); return; } var categoryDefinition = _this10.schema.categories.get(category).definition; var error; var _this10$schema$create = _this10.schema.createAndValidate('category', category, obj); var _this10$schema$create2 = _slicedToArray(_this10$schema$create, 2); error = _this10$schema$create2[0]; obj = _this10$schema$create2[1]; if (error) { callback(new GSError(errorCodes.INVALID_SCHEMA, "Invalid schema provided: ".concat(error))); return; } runIf(permissionChecker, function (callback) { metasync.each([{ id: obj.Id }, { record: obj }], function (opt, callback) { permissionChecker(category, opt, callback); }, callback); }, function (err) { if (err) { callback(err); return; } _this10.pool.connect(function (err, client, done) { if (err) { _this10.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); return; } metasync.sequential([function (cb) { client.query('BEGIN', function (err) { cb(err); }); }, function (ctx, cb) { metasync.series(extractIncludeCategoriesData(categoryDefinition, obj), function (data, cb) { updateRecord(data.category, data.value, client, function (err) { cb(err); }); }, function (err) { cb(err); }); }, function (ctx, cb) { updateRecord(category, obj, client, function (err) { cb(err); }); }], function (err, ctx) { if (err) { client.query('ROLLBACK', function (rollbackError) { if (rollbackError) { _this10.systemLogger(rollbackError); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, rollbackError)); } else { _this10.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } done(); }); return; } client.query('COMMIT', function (err) { if (err) { _this10.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } else { callback(null, ctx.id); } done(); }); }); }); }); }); } // 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> }, { key: "create", value: function create(category, obj, callback, permissionChecker) { var _this11 = this; var error; var _this$schema$createAn = this.schema.createAndValidate('category', category, obj); var _this$schema$createAn2 = _slicedToArray(_this$schema$createAn, 2); error = _this$schema$createAn2[0]; obj = _this$schema$createAn2[1]; if (error) { process.nextTick(callback, new GSError(errorCodes.INVALID_SCHEMA, "Invalid schema provided: ".concat(error))); return; } var categorySchema = this.schema.categories.get(category); var categoryDefinition = categorySchema.definition; if (isIgnoredCategory(categoryDefinition)) { process.nextTick(callback, new GSError(errorCodes.INVALID_CATEGORY_TYPE, "Record creation in ignored category: ".concat(category))); return; } if (categorySchema.references.Include.length !== 0) { process.nextTick(callback, new GSError(errorCodes.INVALID_CREATION_OPERATION, "Cannot create instances of category ".concat(category, " individually, it is ") + 'included in categories ' + categorySchema.references.Include.join(', '))); return; } var createRecord = function createRecord(category, obj, client, id, done) { var categoryDefinition = _this11.schema.categories.get(category).definition; var fields = Object.keys(obj).filter(function (key) { if (key === 'Id') return false; var decorator = extractDecorator(categoryDefinition[key]); return decorator !== 'Include' && decorator !== 'Many'; }); var values = fields.map(function (key) { return obj[key]; }); if (id) { fields.push('Id'); values.push(id.toString()); } fields = fields.map(escapeIdentifier); var createQuery = "INSERT INTO ".concat(escapeIdentifier(category), " ") + "(".concat(fields.join(', '), ")") + " VALUES (".concat(generateQueryParams(fields.length), ")") + ' RETURNING "Id"'; client.query(createQuery, values, function (err, res) { if (err) { done(err); return; } done(null, res.rows.length > 0 && res.rows[0].Id); }); }; runIfFn(permissionChecker, category, { record: obj }, function (err) { if (err) { callback(err); return; } if (isGlobalCategory(categoryDefinition)) { _this11.pool.connect(function (err, client, done) { if (err) { _this11.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); return; } metasync.sequential([function (cb) { client.query('BEGIN', function (err) { cb(err); }); }, function (ctx, cb) { _this11.takeId(client, function (err, id) { ctx.id = id; cb(err); }); }, function (ctx, cb) { metasync.series(extractIncludeCategoriesData(categoryDefinition, obj), function (data, cb) { createRecord(data.category, data.value, client, ctx.id, function (err) { cb(err); }); }, function (err) { cb(err); }); }, function (ctx, cb) { createRecord(category, obj, client, ctx.id, function (err) { cb(err); }); }, function (ctx, cb) { client.query('UPDATE "Identifier"' + ' SET "Status" = \'Actual\', "Change" = CURRENT_TIMESTAMP,' + ' "Category" = (SELECT "Id" FROM "Category" WHERE "Name" = $1),' + ' "Checksum" = (SELECT get_checksum($1, $2, \'sha512\'))' + ' WHERE "Id" = $2', [category, ctx.id], function (err) { cb(err); }); }], function (err, ctx) { if (err) { client.query('ROLLBACK', function (rollbackError) { if (rollbackError) { _this11.systemLogger(rollbackError); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, rollbackError)); } else { _this11.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } done(); }); return; } client.query('COMMIT', function (err) { if (err) { _this11.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } else { callback(null, ctx.id); } done(); }); }); }); } else { createRecord(category, obj, _this11.pool, null, function (err, res) { if (err) { _this11.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } else { callback(null, res); } }); } }); } // 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> }, { key: "update", value: function update(category, query, patch, callback, permissionChecker) { var _this12 = this; var error; var _this$schema$createAn3 = this.schema.createAndValidate('category', category, patch, { patch: true }); var _this$schema$createAn4 = _slicedToArray(_this$schema$createAn3, 2); error = _this$schema$createAn4[0]; patch = _this$schema$createAn4[1]; if (error) { process.nextTick(callback, new GSError(errorCodes.INVALID_SCHEMA, "Invalid schema provided: ".concat(error))); return; } runIf(permissionChecker, function (callback) { metasync.each([{ record: query, isQuery: true }, { record: patch, isPatch: true }], function (opts, callback) { permissionChecker(category, opts, callback); }, function (err) { callback(err); }); }, function (err) { if (err) { callback(err); return; } var fields = Object.keys(patch); var values = fields.map(function (key) { return patch[key]; }); fields = fields.map(escapeIdentifier); var _buildWhere = buildWhere(query), _buildWhere2 = _slicedToArray(_buildWhere, 2), where = _buildWhere2[0], whereParams = _buildWhere2[1]; var updateQuery = "UPDATE ".concat(escapeIdentifier(category), " SET ") + "(".concat(fields.join(', '), ") = ") + "ROW (".concat(generateQueryParams(fields.length, whereParams.length + 1), ")") + where; _this12.pool.query(updateQuery, whereParams.concat(values), function (err, res) { if (err) { _this12.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } else { callback(null, res.rowCount); } }); }); } // 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> }, { key: "delete", value: function _delete(category, query, callback, permissionChecker) { var _this13 = this; var categorySchema = this.schema.categories.get(category); var categoryDefinition = categorySchema.definition; if (categorySchema.references.Include.length !== 0) { process.nextTick(callback, new GSError(errorCodes.INVALID_DELETION_OPERATION, "Cannot delete instances of category ".concat(category, ", it is included") + " in categories ".concat(categorySchema.references.Include.join(', ')))); return; } runIfFn(permissionChecker, category, { record: query, isQuery: true }, function (err) { if (err) { callback(err); return; } var includedCategories = extractIncludeCategories(categoryDefinition); var _generateDeleteQuery = generateDeleteQuery(category, includedCategories, query), _generateDeleteQuery2 = _slicedToArray(_generateDeleteQuery, 2), deleteQuery = _generateDeleteQuery2[0], queryParams = _generateDeleteQuery2[1]; _this13.pool.query(deleteQuery, queryParams, function (err, res) { if (err) { _this13.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } else { callback(null, res.rowCount); } }); }); } // 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> }, { key: "linkDetails", value: function linkDetails(category, field, fromId, toIds, callback, permissionChecker) { var _this14 = this; var categorySchema = this.schema.categories.get(category); var categoryDefinition = categorySchema.definition; var toCategory = categorySchema.definition[field].category; var tableName = manyToManyTableName(category, categoryDefinition[field].category, field); if (!Array.isArray(toIds)) { toIds = [toIds]; } if (toIds.length === 0) { process.nextTick(callback); return; } runIf(permissionChecker, function (callback) { metasync.each([[category, { id: fromId }]].concat(_toConsumableArray(toIds.map(function (id) { return [toCategory, { id: id, accessType: 'read' }]; }))), function (args, callback) { permissionChecker.apply(void 0, _toConsumableArray(args).concat([callback])); }, function (err) { callback(err); }); }, function (err) { if (err) { callback(err); return; } // TODO: add support for linking the records placed on different servers var query = "INSERT INTO ".concat(escapeIdentifier(tableName)) + " VALUES ".concat(generateLinkQueryParams(toIds.length)); _this14.pool.query(query, [fromId].concat(_toConsumableArray(toIds)), function (err) { if (err) { _this14.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } else { callback(); } }); }); } // 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> }, { key: "unlinkDetails", value: function unlinkDetails(category, field, fromId, toIds, callback, permissionChecker) { var _this15 = this; var categorySchema = this.schema.categories.get(category); var toCategory = categorySchema.definition[field].category; var tableName = manyToManyTableName(category, toCategory, field); if (!Array.isArray(toIds)) { toIds = [toIds]; } if (toIds.length === 0) { process.nextTick(callback); return; } runIf(permissionChecker, function (callback) { metasync.each([[category, { id: fromId }]].concat(_toConsumableArray(toIds.map(function (id) { return [toCategory, { id: id, accessType: 'read' }]; }))), function (args, callback) { permissionChecker.apply(void 0, _toConsumableArray(args).concat([callback])); }, function (err) { callback(err); }); }, function (err) { if (err) { callback(err); return; } // TODO: add support for unlinking the records placed on different servers var query = "DELETE FROM ".concat(escapeIdentifier(tableName)) + " WHERE ".concat(escapeIdentifier(category), " = $1 AND") + " ".concat(escapeIdentifier(field), " = ANY ($2)"); _this15.pool.query(query, [fromId, toIds], function (err) { if (err) { _this15.systemLogger(err); callback(new GSError(errorCodes.INTERNAL_PROVIDER_ERROR, err)); } else { callback(); } }); }); } // Select objects from GlobalStorage // category - <string>, category to select the records from // query - <Object>, fields conditions // // Returns: <Cursor> }, { key: "select", value: function select(category, query) { return new PostgresCursor(this, { category: category }).select(query); } }]); return PostgresProvider; }(StorageProvider); module.exports = { PostgresProvider: PostgresProvider };