globalstorage
Version:
Global Storage is a Global Distributed Data Warehouse
1,067 lines (897 loc) • 38.5 kB
JavaScript
;
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
};