dynamoose
Advanced tools
Comparing version 0.6.0 to 0.7.0
@@ -1,3 +0,1 @@ | ||
exports = module.exports = process.env.TEST_COV ? | ||
require('./lib-cov') : | ||
require('./lib'); | ||
exports = module.exports = require('./lib'); |
@@ -180,2 +180,3 @@ 'use strict'; | ||
debug('put', this); | ||
var deferred = Q.defer(); | ||
options = options || {}; | ||
@@ -189,7 +190,14 @@ if(typeof options === 'function') { | ||
} | ||
var schema = this.$__.schema; | ||
var item = { | ||
TableName: this.$__.name, | ||
Item: schema.toDynamo(this) | ||
TableName: this.$__.name | ||
}; | ||
try { | ||
item.Item = schema.toDynamo(this); | ||
} catch(err) { | ||
deferred.reject(err); | ||
return deferred.promise.nodeify(next); | ||
} | ||
if(!options.overwrite) { | ||
@@ -206,3 +214,2 @@ item.ConditionExpression = 'attribute_not_exists(' + schema.hashKey.name + ')'; | ||
function put() { | ||
var deferred = Q.defer(); | ||
model$.base.ddb().putItem(item, function(err) { | ||
@@ -281,7 +288,12 @@ if(err) { | ||
getReq.Key[hashKeyName] = schema.hashKey.toDynamo(key[hashKeyName]); | ||
try { | ||
getReq.Key[hashKeyName] = schema.hashKey.toDynamo(key[hashKeyName]); | ||
if(schema.rangeKey) { | ||
var rangeKeyName = schema.rangeKey.name; | ||
getReq.Key[rangeKeyName] = schema.rangeKey.toDynamo(key[rangeKeyName]); | ||
if(schema.rangeKey) { | ||
var rangeKeyName = schema.rangeKey.name; | ||
getReq.Key[rangeKeyName] = schema.rangeKey.toDynamo(key[rangeKeyName]); | ||
} | ||
} catch (err) { | ||
deferred.reject(err); | ||
return deferred.promise.nodeify(next); | ||
} | ||
@@ -313,10 +325,14 @@ | ||
var model = new NewModel(); | ||
try { | ||
var model = new NewModel(); | ||
model.$__.isNew = false; | ||
schema.parseDynamo(model, data.Item); | ||
model.$__.isNew = false; | ||
schema.parseDynamo(model, data.Item); | ||
debug('getItem parsed model', model); | ||
debug('getItem parsed model', model); | ||
deferred.resolve(model); | ||
deferred.resolve(model); | ||
} catch (err) { | ||
deferred.reject(err); | ||
} | ||
}); | ||
@@ -379,7 +395,12 @@ } | ||
updateReq.Key[hashKeyName] = schema.hashKey.toDynamo(key[hashKeyName]); | ||
try { | ||
updateReq.Key[hashKeyName] = schema.hashKey.toDynamo(key[hashKeyName]); | ||
if(schema.rangeKey) { | ||
var rangeKeyName = schema.rangeKey.name; | ||
updateReq.Key[rangeKeyName] = schema.rangeKey.toDynamo(key[rangeKeyName]); | ||
if(schema.rangeKey) { | ||
var rangeKeyName = schema.rangeKey.name; | ||
updateReq.Key[rangeKeyName] = schema.rangeKey.toDynamo(key[rangeKeyName]); | ||
} | ||
} catch(err) { | ||
deferred.reject(err); | ||
return deferred.promise.nodeify(next); | ||
} | ||
@@ -402,6 +423,18 @@ | ||
var val = updatePUT[putItem]; | ||
if(val === null || val === undefined || val === '' || (Array.isArray(val) && val.length === 0)) { | ||
var removeParams = val === null || val === undefined || val === ''; | ||
if (!options.allowEmptyArray) { | ||
removeParams = removeParams || (Array.isArray(val) && val.length === 0); | ||
} | ||
if(removeParams) { | ||
operations.REMOVE[putItem] = null; | ||
} else { | ||
operations.SET[putItem] = putAttr.toDynamo(val); | ||
try { | ||
operations.SET[putItem] = putAttr.toDynamo(val); | ||
} catch (err) { | ||
deferred.reject(err); | ||
return deferred.promise.nodeify(next); | ||
} | ||
} | ||
@@ -418,3 +451,8 @@ } | ||
if(delVal !== null && delVal !== undefined) { | ||
operations.REMOVE[deleteItem] = deleteAttr.toDynamo(delVal); | ||
try { | ||
operations.REMOVE[deleteItem] = deleteAttr.toDynamo(delVal); | ||
} catch (err) { | ||
deferred.reject(err); | ||
return deferred.promise.nodeify(next); | ||
} | ||
} else { | ||
@@ -431,3 +469,8 @@ operations.REMOVE[deleteItem] = null; | ||
if(addAttr) { | ||
operations.ADD[addItem] = addAttr.toDynamo(update.$ADD[addItem]); | ||
try { | ||
operations.ADD[addItem] = addAttr.toDynamo(update.$ADD[addItem]); | ||
} catch (err) { | ||
deferred.reject(err); | ||
return deferred.promise.nodeify(next); | ||
} | ||
} | ||
@@ -567,7 +610,12 @@ } | ||
getDelete.Key[hashKeyName] = schema.hashKey.toDynamo(this[hashKeyName]); | ||
try { | ||
getDelete.Key[hashKeyName] = schema.hashKey.toDynamo(this[hashKeyName]); | ||
if(schema.rangeKey) { | ||
var rangeKeyName = schema.rangeKey.name; | ||
getDelete.Key[rangeKeyName] = schema.rangeKey.toDynamo(this[rangeKeyName]); | ||
if(schema.rangeKey) { | ||
var rangeKeyName = schema.rangeKey.name; | ||
getDelete.Key[rangeKeyName] = schema.rangeKey.toDynamo(this[rangeKeyName]); | ||
} | ||
} catch (err) { | ||
deferred.reject(err); | ||
return deferred.promise.nodeify(next); | ||
} | ||
@@ -574,0 +622,0 @@ |
@@ -212,14 +212,19 @@ 'use strict'; | ||
try { | ||
var models = data.Items.map(toModel); | ||
var models = data.Items.map(toModel); | ||
if(options.one) { | ||
if (!models || models.length === 0) { | ||
return deferred.resolve(); | ||
if(options.one) { | ||
if (!models || models.length === 0) { | ||
return deferred.resolve(); | ||
} | ||
return deferred.resolve(models[0]); | ||
} | ||
return deferred.resolve(models[0]); | ||
models.lastKey = data.LastEvaluatedKey; | ||
deferred.resolve(models); | ||
} catch (err) { | ||
deferred.reject(err); | ||
} | ||
models.lastKey = data.LastEvaluatedKey; | ||
deferred.resolve(models); | ||
}); | ||
@@ -226,0 +231,0 @@ |
@@ -105,7 +105,10 @@ 'use strict'; | ||
try { | ||
var models = data.Items.map(toModel); | ||
var models = data.Items.map(toModel); | ||
models.lastKey = data.LastEvaluatedKey; | ||
deferred.resolve(models); | ||
models.lastKey = data.LastEvaluatedKey; | ||
deferred.resolve(models); | ||
} catch (err) { | ||
deferred.reject(err); | ||
} | ||
}); | ||
@@ -112,0 +115,0 @@ |
@@ -38,3 +38,3 @@ 'use strict'; | ||
/* | ||
* Added support for timestamps attribute | ||
* Added support for timestamps attribute | ||
*/ | ||
@@ -44,3 +44,3 @@ if (this.options.timestamps) { | ||
var updatedAt = null; | ||
if (this.options.timestamps === true) { | ||
@@ -59,6 +59,6 @@ createdAt = 'createdAt'; | ||
} | ||
obj[createdAt] = { type: Date, default: Date.now }; | ||
obj[updatedAt] = { type: Date, default: Date.now, set: function() { return Date.now(); } }; | ||
this.timestamps = { createdAt: createdAt, updatedAt: updatedAt }; | ||
this.timestamps = { createdAt: createdAt, updatedAt: updatedAt }; | ||
} | ||
@@ -70,11 +70,11 @@ | ||
this.indexes = {local: {}, global: {}}; | ||
for(var name in obj) { | ||
if(this.attributes[name]) { | ||
throw new errors.SchemaError('Duplicate attribute: ' + name); | ||
for(var n in obj) { | ||
if(this.attributes[n]) { | ||
throw new errors.SchemaError('Duplicate attribute: ' + n); | ||
} | ||
debug('Adding Attribute to Schema (%s)', name, obj); | ||
this.attributes[name] = Attribute.create(this, name, obj[name]); | ||
debug('Adding Attribute to Schema (%s)', n, obj); | ||
this.attributes[n] = Attribute.create(this, n, obj[n]); | ||
} | ||
@@ -81,0 +81,0 @@ } |
329
lib/Table.js
@@ -5,4 +5,4 @@ 'use strict'; | ||
var util = require('util'); | ||
var _ = require('underscore'); | ||
function Table(name, schema, options, base) { | ||
@@ -15,3 +15,3 @@ debug('new Table (%s)', name, schema); | ||
if(this.options.create === undefined || this.options.create === null) { | ||
if (this.options.create === undefined || this.options.create === null) { | ||
this.options.create = true; | ||
@@ -21,4 +21,119 @@ } | ||
var compareIndexes = function compareIndexes(local, remote) { | ||
var indexes = { | ||
delete: [], | ||
create: [], | ||
both: [] | ||
}; | ||
var localTableReq = local; | ||
var remoteTableReq = remote; | ||
var i; | ||
var j; | ||
Table.prototype.init = function(next) { | ||
var localIndexes = localTableReq.GlobalSecondaryIndexes || []; | ||
var remoteIndexes = remoteTableReq.GlobalSecondaryIndexes || []; | ||
debug('compareIndexes'); | ||
// let's see what remote indexes we need to sync or create | ||
for (i = 0; i < localIndexes.length; i++) { | ||
var remoteIndexFound = false; | ||
for (j = 0; j < remoteIndexes.length; j++) { | ||
if (remoteIndexes[j].IndexName === localIndexes[i].IndexName) { | ||
// let's see if the core data matches. if it doesn't, | ||
// we may need to delete the remote GSI and rebuild. | ||
var localIndex = _.pick(localIndexes[i], 'IndexName', 'KeySchema', 'Projection', 'ProvisionedThroughput'); | ||
var remoteIndex = _.pick(remoteIndexes[j], 'IndexName', 'KeySchema', 'Projection', 'ProvisionedThroughput'); | ||
if (remoteIndex.hasOwnProperty('ProvisionedThroughput')) { | ||
delete remoteIndex.ProvisionedThroughput.NumberOfDecreasesToday; | ||
} | ||
if (!_.isEqual(remoteIndex, localIndex)) { | ||
indexes.both.push(localIndex); | ||
remoteIndexFound = true; | ||
} else { | ||
remoteIndexFound = true; | ||
} | ||
} | ||
} | ||
if (!remoteIndexFound) { | ||
indexes.create.push(localIndexes[i]); | ||
} | ||
} | ||
for (j = 0; j < remoteIndexes.length; j++) { | ||
var localExists = false; | ||
for (i = 0; i < localIndexes.length; i++) { | ||
if (remoteIndexes[j].IndexName === localIndexes[i].IndexName) { | ||
localExists = true; | ||
} | ||
} | ||
if (!localExists) { | ||
indexes.delete.push(remoteIndexes[j]); | ||
} | ||
} | ||
// now let's see what remote indexes exist that shouldn't exist | ||
return indexes; | ||
}; | ||
Table.prototype.deleteIndex = function deleteIndex(indexName) { | ||
var deferred = Q.defer(); | ||
var table = this; | ||
table.active = false; | ||
var params = { | ||
TableName: table.name, | ||
GlobalSecondaryIndexUpdates: [ | ||
{ | ||
Delete: { | ||
IndexName: indexName | ||
} | ||
} | ||
] | ||
}; | ||
table.base.ddb().updateTable(params, function (err, data) { | ||
debug('deleteIndex handler running'); | ||
if (err) { | ||
deferred.reject(err); | ||
} | ||
else { | ||
setTimeout(function () { | ||
table.waitForActive() | ||
.then(function () { | ||
deferred.resolve(data); | ||
}); | ||
}, 300); | ||
} | ||
}); | ||
return deferred.promise; | ||
}; | ||
Table.prototype.createIndex = function createIndex(attributes, indexSpec) { | ||
var deferred = Q.defer(); | ||
var table = this; | ||
table.active = false; | ||
var params = { | ||
TableName: this.name, | ||
AttributeDefinitions: attributes, | ||
GlobalSecondaryIndexUpdates: [ | ||
{ | ||
Create: indexSpec | ||
} | ||
] | ||
}; | ||
this.base.ddb().updateTable(params, function (err, data) { | ||
if (err) { | ||
deferred.reject(err); | ||
} | ||
else { | ||
setTimeout(function () { | ||
table.waitForActive() | ||
.then(function () { | ||
deferred.resolve(data); | ||
}); | ||
}, 300); | ||
} | ||
}); | ||
return deferred.promise; | ||
}; | ||
Table.prototype.init = function (next) { | ||
debug('initializing table, %s, %j', this.name, this.options); | ||
@@ -28,32 +143,61 @@ var deferred = Q.defer(); | ||
var table = this; | ||
var localTableReq; | ||
if (this.options.create) { | ||
this.describe() | ||
.then(function (data) { | ||
debug('table exist -- initialization done'); | ||
// TODO verify table keys and index's match | ||
table.active = data.Table.TableStatus === 'ACTIVE'; | ||
table.initialized = true; | ||
return deferred.resolve(); | ||
}, | ||
function (err) { | ||
if(err && err.code === 'ResourceNotFoundException') { | ||
debug('table does not exist -- creating'); | ||
return deferred.resolve( | ||
table.create() | ||
.then(function (data) { | ||
debug('table exist -- initialization done'); | ||
localTableReq = buildTableReq(table.name, table.schema); | ||
var indexes = compareIndexes(localTableReq, data.Table); | ||
debug('%s', JSON.stringify(indexes, null, 2)); | ||
if (table.options.update) { | ||
debug('checking indexes'); | ||
for (var deleteIdx in indexes.delete) { | ||
table.deleteIndex(indexes.delete[deleteIdx].IndexName); | ||
} | ||
for (var bothIdx in indexes.both) { | ||
/*jshint loopfunc: true */ | ||
table.deleteIndex(indexes.both[bothIdx].IndexName) | ||
.then(function () { | ||
table.createIndex(localTableReq.AttributeDefinitions, indexes.both[bothIdx]); | ||
}); | ||
} | ||
for (var createIdx in indexes.create) { | ||
table.createIndex(localTableReq.AttributeDefinitions, indexes.create[createIdx]); | ||
} | ||
} else { | ||
if (indexes.delete.length > 0 || indexes.create.length > 0) { | ||
debug('indexes are not synchronized and update flag is set to false'); | ||
deferred.reject('indexes are not synchronized and update flag is set to false'); | ||
} | ||
} | ||
table.initialized = true; | ||
return table.waitForActive() | ||
.then(function () { | ||
table.initialized = true; | ||
}) | ||
// .then(function() { | ||
// if(table.options.waitForActive) { | ||
// return table.waitForActive(); | ||
// } | ||
// }) | ||
); | ||
} | ||
if(err) { | ||
debug('error initializing', err); | ||
return deferred.reject(err); | ||
} | ||
}); | ||
//table.active = data.Table.TableStatus === 'ACTIVE'; | ||
return deferred.resolve(); | ||
}); | ||
}) | ||
.catch(function (err) { | ||
if (err && err.code === 'ResourceNotFoundException') { | ||
debug('table does not exist -- creating'); | ||
return deferred.resolve( | ||
table.create() | ||
.then(function () { | ||
table.initialized = true; | ||
}) | ||
.then(function() { | ||
if(table.options.waitForActive) { | ||
return table.waitForActive(); | ||
} | ||
}) | ||
); | ||
} | ||
if (err) { | ||
debug('error initializing', err.stack); | ||
return deferred.reject(err); | ||
} | ||
}); | ||
} else { | ||
@@ -66,7 +210,7 @@ table.initialized = true; | ||
Table.prototype.waitForActive = function(timeout, next) { | ||
Table.prototype.waitForActive = function (timeout, next) { | ||
debug('Waiting for Active table, %s, %j', this.name, this.options); | ||
var deferred = Q.defer(); | ||
if(typeof timeout === 'function') { | ||
if (typeof timeout === 'function') { | ||
next = timeout; | ||
@@ -76,3 +220,3 @@ timeout = null; | ||
if(!timeout) { | ||
if (!timeout) { | ||
timeout = this.options.waitForActiveTimeout; | ||
@@ -84,8 +228,11 @@ } | ||
var timeoutAt = Date.now() + timeout; | ||
function waitForActive() { | ||
if(table.active) { | ||
debug('Table is Active - %s', table.name); | ||
return deferred.resolve(); | ||
} | ||
if(Date.now() > timeoutAt) { | ||
debug('Waiting...'); | ||
/* | ||
if (table.active) { | ||
debug('Table flag is set to Active - %s', table.name); | ||
return deferred.resolve(); | ||
}*/ | ||
if (Date.now() > timeoutAt) { | ||
return deferred.reject( | ||
@@ -95,18 +242,31 @@ new Error('Wait for Active timed out after ' + timeout + ' ms.') | ||
} | ||
if(!table.initialized) { | ||
if (!table.initialized) { | ||
return setTimeout(waitForActive, 10); | ||
} | ||
table.describe() | ||
.then(function (data) { | ||
if(data.Table.TableStatus !== 'ACTIVE'){ | ||
debug('Waiting for Active - %s', table.name); | ||
setTimeout(waitForActive, 1000); | ||
} else { | ||
// TODO verify table keys and index's match | ||
table.active = true; | ||
deferred.resolve(); | ||
} | ||
}, function (err) { | ||
return deferred.reject(err); | ||
}); | ||
.then(function (data) { | ||
var active = (data.Table.TableStatus === 'ACTIVE'); | ||
var indexes = data.Table.GlobalSecondaryIndexes || []; | ||
indexes.forEach(function (gsi) { | ||
//debug('waitForActive Index Check: %s', JSON.stringify(gsi, null, 2)); | ||
debug('index %s.IndexStatus is %s', gsi.IndexName, gsi.IndexStatus); | ||
if (gsi.IndexStatus !== 'ACTIVE') { | ||
active = false; | ||
} | ||
}); | ||
if (!active) { | ||
debug('Waiting for Active again - %s', table.name); | ||
setTimeout(waitForActive, 500); | ||
} else { | ||
table.active = true; | ||
deferred.resolve(); | ||
} | ||
}) | ||
.catch(function (err) { | ||
if (err && err.code === 'ResourceNotFoundException') { | ||
return setTimeout(waitForActive, 10); | ||
} | ||
debug('Error waiting for active', err.stack); | ||
return deferred.reject(err); | ||
}); | ||
} | ||
@@ -119,3 +279,3 @@ | ||
Table.prototype.describe = function(next) { | ||
Table.prototype.describe = function (next) { | ||
var describeTableReq = { | ||
@@ -125,13 +285,10 @@ TableName: this.name | ||
debug('ddb.describeTable request: %j', describeTableReq); | ||
var deferred = Q.defer(); | ||
var ddb = this.base.ddb(); | ||
ddb.describeTable(describeTableReq, function(err, data) { | ||
if(err) { | ||
ddb.describeTable(describeTableReq, function (err, data) { | ||
if (err) { | ||
debug('error describing table', err); | ||
return deferred.reject(err); | ||
} | ||
debug('got table description: %j', data); | ||
deferred.resolve(data); | ||
@@ -144,12 +301,9 @@ }); | ||
Table.prototype.create = function(next) { | ||
var ddb = this.base.ddb(); | ||
var schema = this.schema; | ||
var buildTableReq = function buildTableReq(name, schema) { | ||
var attrDefs = []; | ||
var keyAttr = {}; | ||
function addKeyAttr (attr) { | ||
if(attr) { | ||
function addKeyAttr(attr) { | ||
if (attr) { | ||
keyAttr[attr.name] = attr; | ||
@@ -161,3 +315,3 @@ } | ||
addKeyAttr(schema.rangeKey); | ||
for(var globalIndexName in schema.indexes.global) { | ||
for (var globalIndexName in schema.indexes.global) { | ||
addKeyAttr(schema.indexes.global[globalIndexName]); | ||
@@ -169,7 +323,7 @@ | ||
} | ||
for(var indexName in schema.indexes.local) { | ||
for (var indexName in schema.indexes.local) { | ||
addKeyAttr(schema.indexes.local[indexName]); | ||
} | ||
for(var keyAttrName in keyAttr) { | ||
for (var keyAttrName in keyAttr) { | ||
attrDefs.push({ | ||
@@ -186,3 +340,3 @@ AttributeName: keyAttrName, | ||
}]; | ||
if(schema.rangeKey) { | ||
if (schema.rangeKey) { | ||
keySchema.push({ | ||
@@ -201,3 +355,3 @@ AttributeName: schema.rangeKey.name, | ||
AttributeDefinitions: attrDefs, | ||
TableName: this.name, | ||
TableName: name, | ||
KeySchema: keySchema, | ||
@@ -209,3 +363,3 @@ ProvisionedThroughput: provThroughput | ||
var localSecIndexes, index; | ||
for(var localSecIndexName in schema.indexes.local) { | ||
for (var localSecIndexName in schema.indexes.local) { | ||
localSecIndexes = localSecIndexes || []; | ||
@@ -226,4 +380,4 @@ | ||
if(index.project) { | ||
if(util.isArray(index.project)){ | ||
if (index.project) { | ||
if (util.isArray(index.project)) { | ||
localSecIndex.Projection = { | ||
@@ -249,3 +403,3 @@ ProjectionType: 'INCLUDE', | ||
var globalSecIndexes; | ||
for(var globalSecIndexName in schema.indexes.global) { | ||
for (var globalSecIndexName in schema.indexes.global) { | ||
globalSecIndexes = globalSecIndexes || []; | ||
@@ -269,3 +423,3 @@ | ||
if(index.rangeKey) { | ||
if (index.rangeKey) { | ||
globalSecIndex.KeySchema.push({ | ||
@@ -277,4 +431,4 @@ AttributeName: index.rangeKey, | ||
if(index.project) { | ||
if(util.isArray(index.project)){ | ||
if (index.project) { | ||
if (util.isArray(index.project)) { | ||
globalSecIndex.Projection = { | ||
@@ -298,17 +452,24 @@ ProjectionType: 'INCLUDE', | ||
if(localSecIndexes) { | ||
if (localSecIndexes) { | ||
createTableReq.LocalSecondaryIndexes = localSecIndexes; | ||
} | ||
if(globalSecIndexes) { | ||
if (globalSecIndexes) { | ||
createTableReq.GlobalSecondaryIndexes = globalSecIndexes; | ||
} | ||
return createTableReq; | ||
}; | ||
Table.prototype.create = function (next) { | ||
var ddb = this.base.ddb(); | ||
var schema = this.schema; | ||
var createTableReq = buildTableReq(this.name, schema); | ||
debug('ddb.createTable request:', createTableReq); | ||
var deferred = Q.defer(); | ||
ddb.createTable(createTableReq, function(err, data) { | ||
if(err) { | ||
ddb.createTable(createTableReq, function (err, data) { | ||
if (err) { | ||
debug('error creating table', err); | ||
@@ -324,3 +485,3 @@ return deferred.reject(err); | ||
Table.prototype.delete = function(next) { | ||
Table.prototype.delete = function (next) { | ||
var deleteTableReq = { | ||
@@ -336,4 +497,4 @@ TableName: this.name | ||
ddb.deleteTable(deleteTableReq, function(err, data) { | ||
if(err) { | ||
ddb.deleteTable(deleteTableReq, function (err, data) { | ||
if (err) { | ||
debug('error deleting table', err); | ||
@@ -350,3 +511,3 @@ return deferred.reject(err); | ||
Table.prototype.update = function(next) { | ||
Table.prototype.update = function (next) { | ||
// var ddb = this.base.ddb(); | ||
@@ -353,0 +514,0 @@ // ddb.updateTable(); |
{ | ||
"name": "dynamoose", | ||
"version": "0.6.0", | ||
"version": "0.7.0", | ||
"description": "Dynamoose is a modeling tool for Amazon's DynamoDB (inspired by Mongoose)", | ||
@@ -50,4 +50,5 @@ "homepage": "https://github.com/automategreen/dynamoose", | ||
"q": "~1.0.1", | ||
"hooks": "0.3.2" | ||
"hooks": "0.3.2", | ||
"underscore": "^1.8.3" | ||
} | ||
} |
@@ -63,3 +63,4 @@ # Dynamoose [![Build Status](https://travis-ci.org/automategreen/dynamoose.png)](https://travis-ci.org/automategreen/dynamoose) | ||
{ | ||
create: true, // Create table in DB, if it does not exist | ||
create: true, // Create table in DB, if it does not exist, | ||
update: false, // Update remote indexes if they do not match local index structure | ||
waitForActive: true, // Wait for table to be created before trying to us it | ||
@@ -483,6 +484,13 @@ waitForActiveTimeout: 180000 // wait 3 minutes for table to activate | ||
#### Model.update(key, update, callback) | ||
#### Model.update(key, update, options, callback) | ||
Updates and existing item in the table. Three types of updates: $PUT, $ADD, and $DELETE. Refer to DynamoDB's updateItem documentation for details on how PUT, ADD, and DELETE work. | ||
##### Options | ||
**allowEmptyArray**: boolean | ||
If true, the attribute can be updated to an empty array. If falsey, empty arrays will remove the attribute. Defaults to false. | ||
**$PUT** | ||
@@ -489,0 +497,0 @@ |
@@ -29,3 +29,6 @@ 'use strict'; | ||
{ | ||
id: Number, | ||
id: { | ||
type: Number, | ||
validate: function (v) { return v > 0; } | ||
}, | ||
name: String, | ||
@@ -43,3 +46,7 @@ owner: String, | ||
more: Object, | ||
array: Array | ||
array: Array, | ||
validated: { | ||
type: String, | ||
validate: function (v) { return v === 'valid'; } | ||
} | ||
}, | ||
@@ -86,3 +93,3 @@ {useDocumentTypes: true}); | ||
should.not.exist(schema.attributes.id.default); | ||
should.not.exist(schema.attributes.id.validator); | ||
should.exist(schema.attributes.id.validator); | ||
should(schema.attributes.id.required).not.be.ok; | ||
@@ -107,3 +114,4 @@ | ||
more: {fovorites: {food: 'fish'}}, | ||
array: [{one: '1'}] | ||
array: [{one: '1'}], | ||
validated: 'valid' | ||
} | ||
@@ -130,3 +138,4 @@ ); | ||
more: { S: '{"fovorites":{"food":"fish"}}' }, | ||
array: { S: '[{"one":"1"}]' } | ||
array: { S: '[{"one":"1"}]' }, | ||
validated: { S: 'valid' } | ||
}); | ||
@@ -182,2 +191,12 @@ | ||
it('Get item with invalid key', function (done) { | ||
Cat.get(0, function(err, model) { | ||
should.exist(err); | ||
err.name.should.equal('ValidationError'); | ||
should.not.exist(model); | ||
done(); | ||
}); | ||
}); | ||
it('Save existing item', function (done) { | ||
@@ -290,2 +309,23 @@ | ||
it('Save existing item with an invalid attribute', function (done) { | ||
Cat.get(1, function(err, model) { | ||
should.not.exist(err); | ||
should.exist(model); | ||
model.validated = 'bad'; | ||
model.save().catch(function(err) { | ||
should.exist(err); | ||
err.name.should.equal('ValidationError'); | ||
Cat.get({id: 1}, {consistent: true}, function(err, badCat) { | ||
should.not.exist(err); | ||
badCat.name.should.eql('Fluffy'); | ||
badCat.vet.name.should.eql('Nice Guy'); | ||
badCat.ears[0].name.should.eql('right'); | ||
badCat.ears[1].name.should.eql('right'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
it('Deletes item', function (done) { | ||
@@ -298,2 +338,13 @@ | ||
it('Deletes item with invalid key', function (done) { | ||
var cat = new Cat({id: 0}); | ||
cat.delete(function(err) { | ||
should.exist(err); | ||
err.name.should.equal('ValidationError'); | ||
done(); | ||
}); | ||
}); | ||
it('Get missing item', function (done) { | ||
@@ -549,2 +600,17 @@ | ||
}); | ||
it('With invalid attribute', function (done) { | ||
Cat.update({id: 999}, {name: 'Oliver', validated: 'bad'}, function (err, data) { | ||
should.exist(err); | ||
should.not.exist(data); | ||
err.name.should.equal('ValidationError'); | ||
Cat.get(999, function (err, tomcat) { | ||
should.not.exist(err); | ||
should.exist(tomcat); | ||
tomcat.id.should.eql(999); | ||
tomcat.name.should.eql('Tom'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
@@ -551,0 +617,0 @@ |
@@ -266,5 +266,6 @@ 'use strict'; | ||
Dog.query('breed').eq('unknown') | ||
.where('ownerId').not().lt(10) | ||
.and() | ||
.filter('color').not().eq('Brown') | ||
.and() | ||
.filter('ownerId').not().lt(10).exec() | ||
.exec() | ||
.then(function (dogs) { | ||
@@ -271,0 +272,0 @@ dogs.length.should.eql(1); |
@@ -19,10 +19,12 @@ 'use strict'; | ||
describe('Table tests', function (){ | ||
describe('Table tests', function () { | ||
this.timeout(5000); | ||
var schema = new Schema({ id: Number, name: String, childern: [Number], address: { street:String,city:String} }); | ||
var schema = new Schema({id: Number, name: String, childern: [Number], address: {street: String, city: String}}); | ||
var globalIndexSchema = new Schema({ | ||
ownerId: { | ||
type: Number, | ||
validate: function(v) { return v > 0; }, | ||
validate: function (v) { | ||
return v > 0; | ||
}, | ||
hashKey: true | ||
@@ -62,3 +64,3 @@ }, | ||
table.create(function(err) { | ||
table.create(function (err) { | ||
should.not.exist(err); | ||
@@ -71,3 +73,3 @@ done(); | ||
table.describe(function(err, data) { | ||
table.describe(function (err, data) { | ||
should.not.exist(err); | ||
@@ -81,3 +83,3 @@ should.exist(data); | ||
table.delete(function(err, data) { | ||
table.delete(function (err, data) { | ||
should.not.exist(err); | ||
@@ -93,3 +95,3 @@ should.exist(data); | ||
missing.describe(function(err, data) { | ||
missing.describe(function (err, data) { | ||
should.exist(err); | ||
@@ -104,3 +106,3 @@ should.not.exist(data); | ||
globalIndexTable.create(function(err) { | ||
globalIndexTable.create(function (err) { | ||
should.not.exist(err); | ||
@@ -113,3 +115,3 @@ done(); | ||
globalIndexTable.delete(function(err, data) { | ||
globalIndexTable.delete(function (err, data) { | ||
should.not.exist(err); | ||
@@ -120,2 +122,144 @@ should.exist(data); | ||
}); | ||
it('create DMSong with limited projection', function (done) { | ||
var Song = dynamoose.model('DMSong', { | ||
id: { | ||
type: Number, | ||
required: true, | ||
hashKey: true, | ||
}, | ||
band: { | ||
type: String, | ||
required: true, | ||
trim: true | ||
}, | ||
album: { | ||
type: String, | ||
required: true, | ||
trim: true, | ||
index: { | ||
global: true, | ||
rangeKey: 'id', | ||
name: 'albumIndex', | ||
project: ['band', 'album'], | ||
throughput: 5 // read and write are both 5 | ||
} | ||
}, | ||
song: { | ||
type: String, | ||
required: true, | ||
trim: true, | ||
index: { | ||
global: true, | ||
rangeKey: 'id', | ||
name: 'songIndex', | ||
project: true, // ProjectionType: ALL | ||
throughput: 5 // read and write are both 5 | ||
} | ||
}, | ||
track: { | ||
type: Number, | ||
required: false, | ||
} | ||
}, | ||
{ | ||
create: true, update: true | ||
}); | ||
var tom_sawyer = new Song({id: 1, band: 'Rush', album: 'Moving Pictures', song: 'Tom Sawyer', track: 1}); | ||
tom_sawyer.save(); | ||
var params = {TableName: 'DMSong'}; | ||
setTimeout(function() { | ||
dynamoose.ddb().describeTable(params, function (err, data) { | ||
if (err) { | ||
done(err); | ||
} | ||
else { | ||
var found = false; | ||
for (var i in data.Table.GlobalSecondaryIndexes) { | ||
var gsi = data.Table.GlobalSecondaryIndexes[i]; | ||
if (gsi.IndexName === 'albumIndex') { | ||
should.equal(gsi.Projection.ProjectionType, 'INCLUDE'); | ||
found = true; | ||
} | ||
} | ||
should.equal(found, true); | ||
delete dynamoose.models.DMSong; | ||
done(); | ||
} | ||
}); | ||
}, 2000); | ||
}); | ||
it('update DMSong with broader projection', function (done) { | ||
var Song = dynamoose.model('DMSong', { | ||
id: { | ||
type: Number, | ||
required: true, | ||
hashKey: true, | ||
}, | ||
band: { | ||
type: String, | ||
required: true, | ||
trim: true | ||
}, | ||
album: { | ||
type: String, | ||
required: true, | ||
trim: true, | ||
index: { | ||
global: true, | ||
rangeKey: 'id', | ||
name: 'albumIndex', | ||
project: true, // ProjectionType: ALL | ||
throughput: 5 // read and write are both 5 | ||
} | ||
}, | ||
song: { | ||
type: String, | ||
required: true, | ||
trim: true, | ||
index: { | ||
global: true, | ||
rangeKey: 'id', | ||
name: 'songIndex', | ||
project: true, // ProjectionType: ALL | ||
throughput: 5 // read and write are both 5 | ||
} | ||
}, | ||
track: { | ||
type: Number, | ||
required: false, | ||
} | ||
}, | ||
{ | ||
create: true, | ||
update: true, | ||
waitForActive: true | ||
}); | ||
var red_barchetta = new Song({id: 2, band: 'Rush', album: 'Moving Pictures', song: 'Red Barchetta', track: 2}); | ||
red_barchetta.save(); | ||
var params = {TableName: 'DMSong'}; | ||
setTimeout(function() { | ||
dynamoose.ddb().describeTable(params, function (err, data) { | ||
if (err) { | ||
done(err); | ||
} | ||
else { | ||
console.log("---------------------REVISED TABLE"); | ||
console.log(JSON.stringify(data, null, 2)); | ||
var found = false; | ||
for (var i in data.Table.GlobalSecondaryIndexes) { | ||
var gsi = data.Table.GlobalSecondaryIndexes[i]; | ||
if (gsi.IndexName === 'albumIndex') { | ||
should.equal(gsi.Projection.ProjectionType, 'ALL'); | ||
found = true; | ||
} | ||
} | ||
should.equal(found, true); | ||
done(); | ||
} | ||
}); | ||
}, 2000); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
176830
4967
741
0
5
+ Addedunderscore@^1.8.3
+ Addedunderscore@1.13.7(transitive)