UNPKG

simple-dynamo

Version:

Abstraction of Amazons Dynamo DB Service. Usage of AWS DynamoDB incredible simple.

855 lines (808 loc) 28.2 kB
// Generated by CoffeeScript 1.10.0 (function() { var Attributes, DynamoTable, ERRORMAPPING, EventEmitter, _, attributesHelper, utils, uuid, bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, hasProp = {}.hasOwnProperty, slice = [].slice; uuid = require('node-uuid'); _ = require("underscore"); Attributes = require("./attributes"); attributesHelper = Attributes.helper; utils = require("./utils"); EventEmitter = require("events").EventEmitter; module.exports = DynamoTable = (function(superClass) { extend(DynamoTable, superClass); function DynamoTable(table, options1) { this.options = options1; this.scan = bind(this.scan, this); this._getThroughput = bind(this._getThroughput, this); this._getShema = bind(this._getShema, this); this._generate = bind(this._generate, this); this._checkSetOptions = bind(this._checkSetOptions, this); this._convertValue = bind(this._convertValue, this); this._defaultRangeKey = bind(this._defaultRangeKey, this); this._defaultHashKey = bind(this._defaultHashKey, this); this._createRangeKey = bind(this._createRangeKey, this); this._createHashKey = bind(this._createHashKey, this); this._createId = bind(this._createId, this); this._validateHash = bind(this._validateHash, this); this._deFixHash = bind(this._deFixHash, this); this._fixHash = bind(this._fixHash, this); this._dynamoItem2JSONSingle = bind(this._dynamoItem2JSONSingle, this); this._dynamoItem2JSON = bind(this._dynamoItem2JSON, this); this._del = bind(this._del, this); this._create = bind(this._create, this); this._update = bind(this._update, this); this._mget = bind(this._mget, this); this._get = bind(this._get, this); this._isExistend = bind(this._isExistend, this); this._getOptions = bind(this._getOptions, this); this._fixCombinedHash = bind(this._fixCombinedHash, this); this._error = bind(this._error, this); this.destroy = bind(this.destroy, this); this.find = bind(this.find, this); this.del = bind(this.del, this); this.set = bind(this.set, this); this.mget = bind(this.mget, this); this.get = bind(this.get, this); this.meta = bind(this.meta, this); this.generate = bind(this.generate, this); this.init = bind(this.init, this); this.mng = this.options.manager; this.defaults = this.options.defaults; this.external = this.options.external; this.__defineGetter__("name", (function(_this) { return function() { return _this._model_settings.name; }; })(this)); this.__defineGetter__("tableName", (function(_this) { return function() { return _this._model_settings.combineTableTo || _this._model_settings.name || null; }; })(this)); this.__defineGetter__("isCombinedTable", (function(_this) { return function() { return _this._model_settings.combineTableTo != null; }; })(this)); this.__defineGetter__("combinedHashDelimiter", (function(_this) { return function() { return ""; }; })(this)); this.__defineGetter__("existend", (function(_this) { return function() { return _this.external != null; }; })(this)); this.__defineGetter__("hasRange", (function(_this) { return function() { var ref, ref1; if ((ref = _this._model_settings) != null ? (ref1 = ref.rangeKey) != null ? ref1.length : void 0 : void 0) { return true; } else { return false; } }; })(this)); this.__defineGetter__("hashKey", (function(_this) { return function() { var ref; return ((ref = _this._model_settings) != null ? ref.hashKey : void 0) || null; }; })(this)); this.__defineGetter__("hashKeyType", (function(_this) { return function() { var ref; if (_this.isCombinedTable) { return "S"; } else { return ((ref = _this._model_settings) != null ? ref.hashKeyType : void 0) || "S"; } }; })(this)); this.__defineGetter__("rangeKey", (function(_this) { return function() { var ref; return ((ref = _this._model_settings) != null ? ref.rangeKey : void 0) || null; }; })(this)); this.__defineGetter__("rangeKeyType", (function(_this) { return function() { var ref; if (_this.hasRange) { return ((ref = _this._model_settings) != null ? ref.rangeKeyType : void 0) || "N"; } else { return null; } }; })(this)); this.__defineGetter__("overwriteExistingHash", (function(_this) { return function() { var ref; if (((ref = _this._model_settings) != null ? ref.overwriteExistingHash : void 0) != null) { return _this._model_settings.overwriteExistingHash; } else if (_this.defaults.overwriteExistingHash != null) { return _this.defaults.overwriteExistingHash; } else { return false; } }; })(this)); this.init(table); return; } DynamoTable.prototype.init = function(table) { this._model_settings = table; this._attrs = new Attributes(table.attributes, this); if (this.isCombinedTable) { this._regexRemCT = new RegExp("^" + this.name, "i"); } }; DynamoTable.prototype.generate = function(cb) { var err; err = {}; if (this.external == null) { this._generate(cb); } else { this.emit("create-status", "already-active"); cb(null, false); } }; DynamoTable.prototype.meta = function(cb) { if (this._meta != null) { cb(null, this._meta); } else if (this._isExistend(cb)) { this.external.fetch((function(_this) { return function(err, _meta) { if (err) { _this._error(cb, err); } else { _this._meta = _meta; cb(null, _meta); } }; })(this)); } }; DynamoTable.prototype.get = function() { var _id, args, cb, i, options, query; args = 2 <= arguments.length ? slice.call(arguments, 0, i = arguments.length - 1) : (i = 0, []), cb = arguments[i++]; if (this._isExistend(cb)) { options = null; switch (args.length) { case 1: _id = args[0]; break; case 2: _id = args[0], options = args[1]; } options = this._getOptions(options); query = this._deFixHash(_id, cb); if (query instanceof Error) { this._error(cb, query); return; } this._get(query, options, (function(_this) { return function(err, _item) { var _obj; if (err) { _this._error(cb, err); } else { if (_item) { _obj = _this._dynamoItem2JSON(_item, false); _this.emit("get", _obj); cb(null, _obj); } else { _this.emit("get-empty"); cb(null, null); } } }; })(this)); } }; DynamoTable.prototype.mget = function() { var _id, _ids, args, cb, i, j, len, mQuery, options, query; args = 2 <= arguments.length ? slice.call(arguments, 0, i = arguments.length - 1) : (i = 0, []), cb = arguments[i++]; if (this._isExistend(cb)) { options = null; switch (args.length) { case 1: _ids = args[0]; break; case 2: _ids = args[0], options = args[1]; } options = this._getOptions(options); mQuery = []; for (j = 0, len = _ids.length; j < len; j++) { _id = _ids[j]; query = this._deFixHash(_id, cb); if (query instanceof Error) { this._error(cb, query); return; } else { mQuery.push(query); } } this._mget(mQuery, options, (function(_this) { return function(err, _item) { var _obj; if (err) { _this._error(cb, err); } else { if (_item.length) { _obj = _this._dynamoItem2JSON(_item, false); _this.emit("mget", _obj); cb(null, _obj); } else { _this.emit("mget-empty"); cb(null, []); } } }; })(this)); } }; DynamoTable.prototype.set = function() { var _create, _id, args, attributes, cb, i, options; args = 2 <= arguments.length ? slice.call(arguments, 0, i = arguments.length - 1) : (i = 0, []), cb = arguments[i++]; if (this._isExistend(cb)) { options = null; switch (args.length) { case 1: _create = true; _id = null; attributes = args[0]; break; case 2: if (_.isString(args[0]) || _.isNumber(args[0]) || _.isArray(args[0])) { _create = false; _id = args[0], attributes = args[1]; } else { _create = true; _id = null; attributes = args[0], options = args[1]; } break; case 3: _create = false; _id = args[0], attributes = args[1], options = args[2]; } options = this._getOptions(options); this._attrs.validateAttributes(_create, attributes, (function(_this) { return function(err, attributes) { if (err) { return _this._error(cb, err); } else { if (_create) { return _this._create(attributes, options, function(err, _item) { var _obj, ref; if (err) { _this._error(cb, err); } else { _obj = _this._dynamoItem2JSON(_item, true); _this.emit("create", _obj); if (options != null ? (ref = options.fields) != null ? ref.length : void 0 : void 0) { _obj = utils.reduceObj(_obj, options != null ? options.fields : void 0); } cb(null, _obj); } }); } else { return _this._update(_id, attributes, options, function(err, item) { var _obj, _reducedItem, ref; if (err) { _this._error(cb, err); } else { _obj = _this._dynamoItem2JSON(item, true); _this.emit("update", _obj); if (options != null ? (ref = options.fields) != null ? ref.length : void 0 : void 0) { _reducedItem = utils.reduceObj(_obj, options.fields); } cb(null, _reducedItem || _obj); } }); } } }; })(this)); } }; DynamoTable.prototype.del = function(_id, cb) { var args, i, options, query; args = 2 <= arguments.length ? slice.call(arguments, 0, i = arguments.length - 1) : (i = 0, []), cb = arguments[i++]; _id = args[0], options = args[1]; options || (options = {}); if (this._isExistend(cb)) { query = this._deFixHash(_id); if (query instanceof Error) { this._error(cb, query); } else { this._del(query, options, (function(_this) { return function(err, success) { if (err) { _this._error(cb, err); } else { _this.emit("delete", success); cb(null, success); } }; })(this)); } } }; DynamoTable.prototype.find = function() { var _fetchOpts, _fnHandle, _op, _query, _val, _x, args, cb, i, isScan, options, query, ref, ref1, startAt; args = 2 <= arguments.length ? slice.call(arguments, 0, i = arguments.length - 1) : (i = 0, []), cb = arguments[i++]; if (this._isExistend(cb)) { options = null; startAt = null; query = {}; switch (args.length) { case 1: query = args[0]; break; case 2: query = args[0], _x = args[1]; if (_.isString(_x) || _.isNumber(_x)) { startAt = _x; } else { options = _x; } break; case 3: query = args[0], startAt = args[1], options = args[2]; } options = this._getOptions(options); if (startAt != null) { startAt = this._deFixHash(startAt); if (startAt instanceof Error) { this._error(cb, startAt); return; } } if (this.isCombinedTable) { if (query != null ? query[this.hashKey] : void 0) { _op = _.first(Object.keys(query[this.hashKey])); _val = query[this.hashKey][_op]; _val = ((ref = this._deFixHash(_val)) != null ? ref[this.hashKey] : void 0) || _val; if (_val instanceof Error) { this._error(cb, _val); return; } switch (_op) { case "==": _val = _val; } query[this.hashKey][_op] = _val; } else { query[this.hashKey] = { "startsWith": this.name }; } } if (this._isExistend(cb)) { ref1 = this._attrs.getQuery(this.external, query, startAt, options), _query = ref1[0], isScan = ref1[1]; _fnHandle = (function(_this) { return function(err, _items) { if (err) { _this._error(cb, err); } else { cb(null, _this._dynamoItem2JSON(_items, false)); } }; })(this); if (isScan) { _query.fetch(_fnHandle); } else { _fetchOpts = { consistent: options.consistent }; _query.fetch(_fetchOpts, _fnHandle); } } } }; DynamoTable.prototype.destroy = function(cb) { if (this._isExistend(cb)) { return this.external.destroy(cb); } }; DynamoTable.prototype._error = function(cb, err) { var _err, error; if (ERRORMAPPING[err.name] != null) { _err = ERRORMAPPING[err.name]; error = new Error; error.name = _err.name; error.message = _err.message; cb(error); } else { cb(err); } }; DynamoTable.prototype._fixCombinedHash = function(hash) { var _i; if (this.isCombinedTable) { _i = this.name.length; if (hash.slice(0, +(_i - 1) + 1 || 9e9) === this.name) { return hash.slice(_i); } else { return hash; } } else { return hash; } }; DynamoTable.prototype._getOptions = function(options) { var _defOpt; if (options == null) { options = {}; } _defOpt = { fields: this._model_settings.defaultfields != null ? this._model_settings.defaultfields : void 0, overwriteExistingHash: this.overwriteExistingHash, consistent: this._model_settings.consistent != null ? this._model_settings.consistent : false, forward: this._model_settings.forward != null ? this._model_settings.forward : true, conditionals: null }; return _.extend(_defOpt, options || {}); }; DynamoTable.prototype._isExistend = function(cb) { var error; if (this.existend) { return true; } else { if (_.isFunction(cb)) { error = new Error; error.name = "table-not-created"; error.message = "Table '" + this.tableName + "' not existend at AWS. please run `Table.generate()` or `Manager.generateAll()` first."; this._error(cb, error); } return false; } }; DynamoTable.prototype._get = function(query, options, cb) { var _fetchOpts, _item, ref; _item = this.external.get(query); if (options != null ? (ref = options.fields) != null ? ref.length : void 0 : void 0) { _item.get(options.fields); } _fetchOpts = { consistent: options.consistent }; _item.fetch(_fetchOpts, (function(_this) { return function(err, item) { if (err) { cb(err); } else { cb(null, item); } }; })(this)); }; DynamoTable.prototype._mget = function(mquery, options, cb) { var _batch, _self; _self = this; _batch = this.mng.client.get(function() { var ref; if ((options != null ? (ref = options.fields) != null ? ref.length : void 0 : void 0) && _.isArray(options.fields)) { return this.get(_self.tableName, mquery, options.fields); } else { return this.get(_self.tableName, mquery); } }); _batch.fetch((function(_this) { return function(err, items) { if (err) { cb(err); } else { cb(null, items[_this.tableName] || []); } }; })(this)); }; DynamoTable.prototype._update = function(id, attributes, options, cb) { var _id, _upd, item; if (options == null) { options = {}; } _id = this._deFixHash(id); if (_id instanceof Error) { this._error(cb, _id); return; } item = this.external.get(_id); _upd = item.update(this._attrs.updateAttrsFn(attributes, options)); _upd.returning("ALL_NEW"); _upd = this._checkSetOptions("update", _upd, attributes, options); if (_upd.AttributeUpdates != null) { _upd.save((function(_this) { return function(err, _saved) { if (err) { cb(err); } else { cb(null, _saved.Attributes || {}); } }; })(this)); } else { cb(null, null); } }; DynamoTable.prototype._create = function(attributes, options, cb) { if (attributes == null) { attributes = {}; } this._createId(attributes, (function(_this) { return function(err, attributes) { var _upd; if (err) { cb(err); } else { _upd = _this.external.put(attributes); _upd = _this._checkSetOptions("create", _upd, attributes, options); _upd.save(function(err) { if (err) { cb(err); } else { cb(null, _upd); } }); } }; })(this)); }; DynamoTable.prototype._del = function(query, options, cb) { var _del; _del = this.external.get(query); _del.returning("ALL_OLD"); _del = this._checkSetOptions("update", _del, {}, options); _del.destroy((function(_this) { return function(err, item) { if (err) { cb(err); } else { cb(null, item || null); } }; })(this)); }; DynamoTable.prototype._dynamoItem2JSON = function(items, convertAttrs) { var i, idx, item, len; if (convertAttrs == null) { convertAttrs = false; } if (_.isArray(items)) { for (idx = i = 0, len = items.length; i < len; idx = ++i) { item = items[idx]; items[idx] = this._dynamoItem2JSONSingle(item, convertAttrs); } return items; } else { return this._dynamoItem2JSONSingle(items, convertAttrs); } }; DynamoTable.prototype._dynamoItem2JSONSingle = function(item, convertAttrs) { var _obj; if (convertAttrs == null) { convertAttrs = false; } if (convertAttrs) { _obj = attributesHelper.dyn2obj((item != null ? item.Item : void 0) || item); } else { _obj = item; } return this._fixHash(_obj); }; DynamoTable.prototype._fixHash = function(attrs) { return attrs; }; DynamoTable.prototype._deFixHash = function(attrs) { var _attrs, _h, _hName, _hType, _r, _rName, _rType, error, ref; if (_.isString(attrs) || _.isNumber(attrs) || _.isArray(attrs)) { _hName = this.hashKey; _attrs = {}; _attrs[_hName] = _.clone(attrs); } else { _attrs = _.clone(attrs); } if (this.hasRange) { _hType = this.hashKeyType; _rName = this.rangeKey; _rType = this.rangeKeyType; if (!_.isArray(_attrs[_hName])) { error = new Error; error.name = "invalid-range-call"; error.message = "If you try to access a hash/range item you have to pass a Array of `[hash,range]` as id."; return error; } ref = _attrs[_hName], _h = ref[0], _r = ref[1]; _attrs[_hName] = this._convertValue(_h, _hType); _attrs[_rName] = this._convertValue(_r, _rType); } return _attrs; }; DynamoTable.prototype._validateHash = function(hash) { var _l, _pre; _pre = this.name + this.combinedHashDelimiter; _l = _pre.length; return hash.slice(0, _l) === _pre; }; DynamoTable.prototype._createId = function(attributes, cb) { this._createHashKey(attributes, (function(_this) { return function(attributes) { var error; if (_this.isCombinedTable) { if (!_this._validateHash(attributes[_this.hashKey])) { error = new Error; error.name = "combined-hash-invalid"; error.message = "The hash of a combined-table has to start with the `name` of this table defined in the configuartion. Please try `" + (_this.name + _this.combinedHashDelimiter + attributes[_this.hashKey]) + "`"; _this._error(cb, error); return; } } if (_this.hasRange) { _this._createRangeKey(attributes, function(attributes) { cb(null, attributes); }); } else { cb(null, attributes); } }; })(this)); }; DynamoTable.prototype._createHashKey = function(attributes, cbH) { var _hName, _hType; _hName = this.hashKey; _hType = this.hashKeyType; if (this._model_settings.fnCreateHash && _.isFunction(this._model_settings.fnCreateHash)) { this._model_settings.fnCreateHash(attributes, (function(_this) { return function(_hash) { attributes[_hName] = _this._convertValue(_hash, _hType); cbH(attributes); }; })(this)); } else if (attributes[_hName] != null) { attributes[_hName] = this._convertValue(attributes[_hName], _hType); cbH(attributes); } else { attributes[_hName] = this._convertValue(this._defaultHashKey(), _hType); cbH(attributes); } }; DynamoTable.prototype._createRangeKey = function(attributes, cbR) { var _rName, _rType; _rName = this.rangeKey; _rType = this.rangeKeyType; if (this._model_settings.fnCreateRange && _.isFunction(this._model_settings.fnCreateRange)) { this._model_settings.fnCreateRange(attributes, (function(_this) { return function(__range) { attributes[_rName] = _this._convertValue(__range, _rType); cbR(attributes); }; })(this)); } else if (attributes[_rName] != null) { attributes[_rName] = this._convertValue(attributes[_rName], _rType); cbR(attributes); } else { attributes[_rName] = this._convertValue(this._defaultRangeKey(), _rType); cbR(attributes); } }; DynamoTable.prototype._defaultHashKey = function() { if (this.isCombinedTable) { return this.name + this.combinedHashDelimiter + uuid.v1(); } else { return uuid.v1(); } }; DynamoTable.prototype._defaultRangeKey = function() { return Date.now(); }; DynamoTable.prototype._convertValue = function(val, type) { switch (type.toUpperCase()) { case "N": return parseFloat(val, 10); case "S": if (val) { return val.toString(10); } break; default: return val; } }; DynamoTable.prototype._checkSetOptions = function(type, _upd, attributes, options) { var _pred; if (type === "create" && (!(options != null ? options.overwriteExistingHash : void 0) || !this.overwriteExistingHash)) { _pred = {}; _pred[this.hashKey] = { "==": null }; _upd.when(_pred); } if (type === "update") { if (!_.isEmpty(options.conditionals)) { _upd.when(options.conditionals); } } return _upd; }; DynamoTable.prototype._generate = function(cb) { var _cr; _cr = this.mng.client.add({ name: this.tableName, throughput: this._getThroughput(), schema: this._getShema() }); _cr.save((function(_this) { return function(err, _table) { if (err) { cb(err); } else { _this.emit("create-status", "waiting"); _table.watch(function(err, _table) { if (err) { cb(err); } else { _this.emit("create-status", "active"); _this.external = _table; cb(null, _table); } }); } }; })(this)); }; DynamoTable.prototype._getShema = function() { var _hName, _hType, _rName, _rType, oShema; oShema = {}; _hName = this.hashKey; _hType = this.hashKeyType; oShema[_hName] = _hType === "S" ? String : Number; if (this.hasRange) { _rName = this.rangeKey; _rType = this.rangeKeyType; oShema[_rName] = _rType === "S" ? String : Number; } return oShema; }; DynamoTable.prototype._getThroughput = function() { var oRet, ref, ref1, ref2, ref3; oRet = this.defaults.throughput; if (((ref = this.options) != null ? (ref1 = ref.throughput) != null ? ref1.read : void 0 : void 0) != null) { oRet.read = this.options.throughput.read; } if (((ref2 = this.options) != null ? (ref3 = ref2.throughput) != null ? ref3.write : void 0 : void 0) != null) { oRet.write = this.options.throughput.write; } return oRet; }; DynamoTable.prototype.scan = function(_table, query, cb) { this.fetchTable(_table, (function(_this) { return function(err, table) { var scan; scan = table.scan(query); scan.fetch(function(err, data) { if (err) { cb(err); } else { cb(null, data); } }); }; })(this)); }; return DynamoTable; })(EventEmitter); ERRORMAPPING = { "com.amazonaws.dynamodb.v20120810#ConditionalCheckFailedException": { name: "conditional-check-failed", message: "This is not a valid request. It doesnt match the conditions or you tried to insert a existing hash." } }; }).call(this);