New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

sails-dynamodb

Package Overview
Dependencies
Maintainers
2
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

sails-dynamodb - npm Package Compare versions

Comparing version 0.11.4 to 0.12.0

.package.json

696

index.js

@@ -64,3 +64,4 @@ /**

// (aka model) that gets registered with this adapter.
var _modelReferences = {};
var _collectionReferences = {};
var _vogelsReferences = {};

@@ -97,8 +98,13 @@ var _definedTables = {};

identity: 'sails-dynamodb', keyId: "id", indexPrefix: "-Index"
identity: 'sails-dynamodb',
pkFormat: 'string',
keyId: 'id',
// Set to true if this adapter supports (or requires) things like data types, validations, keys, etc.
// If true, the schema for models using this adapter will be automatically synced when the server starts.
// Not terribly relevant if your data store is not SQL/schemaful.
, syncable: true,
// This doesn't make sense for dynamo, where the schema parts are locked-down during table creation.
syncable: false,

@@ -109,3 +115,5 @@

defaults: {
accessKeyId: null, secretAccessKey: null, region: 'us-west-1'
accessKeyId: null,
secretAccessKey: null,
region: 'us-west-1',
// For example:

@@ -130,103 +138,95 @@ // port: 3306,

// safe => Don't change anything (good for production DBs)
, migrate: 'alter'
// , schema: false
}, _getModel: function (collectionName) {
//Indices currently never change in dynamo
migrate: 'safe',
// schema: false
},
_createModel: function (collectionName) {
var collection = _collectionReferences[collectionName];
var collection = _modelReferences[collectionName];
//console.log("currenct collection.definition", collection.definition);
//console.log(collection);
/*
currenct collection
{
keyId: 'id',
indexPrefix: '-Index',
syncable: true,
defaults:
{ accessKeyId: null,
secretAccessKey: null,
region: 'us-west-1',
migrate: 'alter',
adapter: 'sails-dynamodb' },
_getModel: [Function],
_getPrimaryKeys: [Function],
registerCollection: [Function],
teardown: [Function],
define: [Function],
describe: [Function],
drop: [Function],
find: [Function],
_searchCondition: [Function],
create: [Function],
update: [Function],
destroy: [Function],
_setColumnType: [Function],
_resultFormat: [Function],
config:
{ accessKeyId: null,
secretAccessKey: null,
region: 'us-west-1',
migrate: 'alter',
adapter: 'sails-dynamodb' },
definition:
{ user_id: { primaryKey: true, unique: true },
name: { type: 'string', index: true },
password: { type: 'string', index: true },
email: { type: 'string', index: true },
activated: { type: 'boolean', defaultsTo: false },
activationToken: { type: 'string' },
isSocial: { type: 'boolean' },
socialActivated: { type: 'boolean' },
createdAt: { type: 'datetime', default: 'NOW' },
updatedAt: { type: 'datetime', default: 'NOW' } },
identity: 'user' }
*/
var primaryKeys = _.where(collection.definition, {primaryKey: true});
//console.log("primaryKeys", primaryKeys);
return Vogels.define(collectionName, function (schema) {
//console.log("_getModel", collectionName);
// Attrs with primaryKeys
var primaryKeys = _.pick(collection.definition, function(attr) { return !!attr.primaryKey } );
var primaryKeyNames =_.keys(primaryKeys);
if (primaryKeyNames.length < 1 || primaryKeyNames.length > 2) {
throw new Error('Must have one or two primary key attributes.');
}
// One primary key, then it's a hash
if (primaryKeyNames.length == 1) {
collection.definition[primaryKeyNames[0]].primaryKey = 'hash';
}
// Vogels adds an 's'. So let's remove an 's'.
var vogelsCollectionName = collectionName[collectionName.length-1] === 's' ?
collectionName.slice(0, collectionName.length-1) :
collectionName;
var vogelsModel = Vogels.define(vogelsCollectionName, function (schema) {
var columns = collection.definition;
var primaryKeys = []
var indexes = [];
var indices = {};
// set columns
for (var columnName in columns) {
var attributes = columns[columnName];
// console.log(columnName+":", attributes);
if (typeof attributes !== "function") {
// Add column to Vogel model
adapter._setColumnType(schema, columnName, attributes);
// search primarykey
// if("primaryKey" in attributes)primaryKeys.push( columnName );
// search index
if ("index" in attributes) indexes.push(columnName);
}
}
// set primary key
primaryKeys = adapter._getPrimaryKeys(collectionName);
primaryKeys = _.difference(primaryKeys, ["id"]); // ignore "id"
// console.log("collection.definition", collection.definition);
if (primaryKeys.length < 1)
schema.UUID(adapter.keyId, {hashKey: true});
else {
if (!_.isUndefined(primaryKeys[0])) {
adapter._setColumnType(schema, primaryKeys[0], columns[primaryKeys[0]], {hashKey: true});
if (!_.isUndefined(primaryKeys[1])) {
adapter._setColumnType(schema, primaryKeys[1], columns[primaryKeys[1]], {rangeKey: true});
// Save set indices
var index;
var indexParts;
var indexName;
var indexType;
if ("index" in attributes && attributes.index !== 'secondary') {
index = attributes.index;
indexParts = adapter._parseIndex(index, columnName);
indexName = indexParts[0];
indexType = indexParts[1];
if (typeof indices[indexName] === 'undefined') {
indices[indexName] = {};
}
indices[indexName][indexType] = columnName;
}
}
}
// schema.String( primaryKey, {hashKey: true});
for (var i = 0; i < indexes.length; i++) {
var key = indexes[i];
schema.globalIndex(key + adapter.indexPrefix, {hashKey: key});
// Set global secondary indices
for (indexName in indices) {
schema.globalIndex(indexName, indices[indexName]);
}
schema.Date('createdAt', {default: Date.now});
schema.Date('updatedAt', {default: Date.now});
});
}, _getPrimaryKeys: function (collectionName) {
// Cache Vogels model
_vogelsReferences[collectionName] = vogelsModel;
return vogelsModel;
},
_getModel: function(collectionName) {
return _vogelsReferences[collectionName] || this._createModel(collectionName);
},
_getPrimaryKeys: function (collectionName) {
var lodash = _;
var collection = _modelReferences[collectionName];
var collection = _collectionReferences[collectionName];

@@ -238,7 +238,11 @@ var maps = lodash.mapValues(collection.definition, "primaryKey");

});
var primaryKeys = lodash.keys(list);
return primaryKeys;
}, _keys: function (collectionName) {
},
_keys: function (collectionName) {
var lodash = _;
var collection = _modelReferences[collectionName];
var collection = _collectionReferences[collectionName];

@@ -249,5 +253,7 @@ var list = lodash.pick(collection.definition, function (value, key) {

return lodash.keys(list);
}, _indexes: function (collectionName) {
},
_indexes: function (collectionName) {
var lodash = _;
var collection = _modelReferences[collectionName];
var collection = _collectionReferences[collectionName];

@@ -258,4 +264,61 @@ var list = lodash.pick(collection.definition, function (value, key) {

return lodash.keys(list);
}
},
// index: 'secondary'
_getLocalIndices: function(collectionName) {
},
// index: 'indexName-fieldType' (i.e. 'users-hash' and 'users-range')
_getGlobalIndices: function(collectionName) {
},
_parseIndex: function(index, columnName) {
// Two helpers
var stringEndsWith = function(str, needle) {
if (str.indexOf(needle) !== -1 &&
str.indexOf(needle) === str.length-needle.length) {
return true;
} else {
return false;
}
}
var removeSuffixFromString = function(str, suffix) {
if (stringEndsWith(str, suffix)) {
return str.slice(0, str.length-suffix.length);
} else {
return str;
}
}
var indexName;
var indexType;
if (index === true) {
indexName = columnName;
indexType = 'hashKey';
} else if (stringEndsWith(index, '-hash')) {
indexName = removeSuffixFromString(index, '-hash');
indexType = 'hashKey';
} else if (stringEndsWith(index, '-range')) {
indexName = removeSuffixFromString(index, '-range');
indexType = 'rangeKey';
} else {
throw new Error('Index must be a hash or range.');
}
return [indexName, indexType];
},
/**

@@ -269,12 +332,11 @@ *

* @return {[type]} [description]
*/, registerConnection: function (connection, collections, cb) {
//var sails = require("sails");
//console.log("load registerConnection");
//console.log("::connection",connection);
//console.log("::collections",collections);
*/
registerConnection: function (connection, collections, cb) {
if (!connection.identity) return cb(Errors.IdentityMissing);
if (connections[connection.identity]) return cb(Errors.IdentityDuplicate);
var error = null;
try {
AWS.config.update({

@@ -285,11 +347,18 @@ "accessKeyId": connection.accessKeyId,

});
}
catch (e) {
} catch (e) {
e.message = e.message + ". Please make sure you added the right keys to your adapter config";
error = e;
return cb(e)
}
// Keep a reference to this collection
_modelReferences = collections;
cb(error);
}
// Keep a reference to these collections
_collectionReferences = collections;
// Create Vogels models for the collections
_.forOwn(collections, function(coll, collName) {
adapter._createModel(collName);
});
cb();
},

@@ -303,3 +372,4 @@ /**

* @return {[type]} [description]
*/, teardown: function (connection, cb) {
*/
teardown: function (connection, cb) {
cb();

@@ -326,3 +396,3 @@ },

// If you need to access your private data for this collection:
var collection = _modelReferences[collectionName];
var collection = _collectionReferences[collectionName];

@@ -337,3 +407,3 @@ if (!_definedTables[collectionName]) {

if (err) {
sails.log.error('Error creating tables', err);
//sails.log.error('Error creating tables', err);
cb(err);

@@ -369,3 +439,3 @@ }

// If you need to access your private data for this collection:
var collection = _modelReferences[collectionName];
var collection = _collectionReferences[collectionName];
//console.log("::collection.definition",collection.definition);

@@ -394,3 +464,3 @@

else {
sails.log.error('Error describe tables' + __filename, err);
//sails.log.error('Error describe tables' + __filename, err);
cb(err);

@@ -422,3 +492,3 @@ }

// If you need to access your private data for this collection:
var collection = _modelReferences[collectionName];
var collection = _collectionReferences[collectionName];
//sails.log.error('drop: not supported')

@@ -455,9 +525,10 @@ // Drop a "table" or "collection" schema from the data store

find: function (connection, collectionName, options, cb) {
sails.log.silly("adaptor::find", collectionName);
sails.log.silly("::option", options);
//sails.log.silly("adaptor::find", collectionName);
//sails.log.silly("::option", options);
var collection = _modelReferences[collectionName],
var collection = _collectionReferences[collectionName],
model = adapter._getModel(collectionName),
query = null,
hashKey = null;
error;
// Options object is normalized for you:

@@ -475,6 +546,3 @@ //

if (options && 'where' in options && _.isObject(options.where)) {
var primaryKeys = adapter._getPrimaryKeys(collectionName),
modelIndexes = adapter._indexes(collectionName),
modelKeys = adapter._keys(collectionName);
query = null;

@@ -484,71 +552,61 @@

var wheres = _.keys(options.where);
// compare both of keys
var primaryQuery = _.intersection(primaryKeys, wheres);
var indexQuery = _.intersection(modelIndexes, wheres);
if (primaryQuery.length > 0 && wheres.length < 2) {
hashKey = primaryKeys[0];
if (!_.isArray(options.where[hashKey])) {
query = model.query(options.where[hashKey]);
sails.log.silly('using PK ' + hashKey)
options.where = _.without(options.where, hashKey);
var indexing = adapter._whichIndex(collectionName, wheres);
var hash = indexing.hash;
var range = indexing.range;
var indexName = indexing.index;
var scanning = false;
if (indexing) {
query = model.query(options.where[hash])
delete options.where[hash];
if (indexName && indexName != 'primary') {
query.usingIndex(indexName);
}
}
else if (indexQuery.length > 0 && wheres.length < 2) {
hashKey = indexQuery[0];
query = model.query(options.where[hashKey]).usingIndex(hashKey + adapter.indexPrefix);
sails.log.silly('using index ' + wheres[0] + adapter.indexPrefix);
delete options.where[hashKey];
}
// scan mode
if (!query) {
if (range) {
error = adapter._applyQueryFilter(query, 'where', range, options.where[range]);
if (error) return cb(error);
delete options.where[range];
}
} else {
scanning = true;
query = model.scan();
sails.log.silly('using scan() ');
}
var queryOp = scanning ? 'where' : 'filter';
for (var key in options.where) {
// Using startKey?
if (key == 'startKey') {
try {
query.startKey(JSON.parse(options.where[key]));
}
catch (e) {
} catch (e) {
return cb("Wrong start key format :" + e.message);
}
continue;
} else {
error = adapter._applyQueryFilter(query, queryOp, key, options.where[key]);
if (error) return cb(error);
}
if (modelKeys.indexOf(key) === -1) {
return cb("Wrong attribute given : " + key);
}
var filter = _.keys(options.where[key])[0];
if (filter in filters) {
try {
query.where(key)[filter](filters[filter] ? options.where[key][filter] : null);
}
catch (e) {
return cb(e.message);
}
}
else {
try {
if (_.isString(options.where[key]) || _.isNumber(options.where[key])) {
query.where(key).equals(options.where[key]);
continue;
}
else if (_.isArray(options.where[key])) {
query.where(key).in(options.where[key]);
continue;
}
}
catch (e) {
return cb(e.message);
}
return cb("Wrong filter given :" + filter);
}
}
}
query = adapter._searchCondition(query, options, model);
query.exec(function (err, res) {
if (!err) {
console.log("success", adapter._resultFormat(res));
//console.log("success", adapter._resultFormat(res));
adapter._valueDecode(collection.definition, res.attrs);

@@ -558,3 +616,3 @@ cb(null, adapter._resultFormat(res));

else {
sails.log.error('Error exec query:' + __filename, err);
//sails.log.error('Error exec query:' + __filename, err);
cb(err);

@@ -566,3 +624,180 @@ }

// cb(null, []);
}/**
},
_applyQueryFilter: function(query, op, key, condition) {
try {
if (_.isString(condition) || _.isNumber(condition)) {
query[op](key).equals(condition);
} else if (_.isArray(condition)) {
query[op](key).in(condition);
} else if (_.isObject(condition)) {
var filter = _.keys(condition)[0];
if (filter in filters) {
query[op](key)[filter](filters[filter] ? condition[filter] : null);
} else {
throw new Error("Wrong filter given :" + filter);
}
} else {
throw new Error("Wrong filter given :" + filter);
}
} catch (e) {
return e;
}
},
// Return {index: 'name', hash: 'field1', range:'field2'}
// Primary hash and range > primary hash and secondary range > global secondary hash and range
// > primary hash > global secondary hash > no index/primary
_whichIndex: function(collectionName, fields) {
var columns = _collectionReferences[collectionName].definition;
var primaryHash = false;
var primaryRange = false;
var secondaryRange = false;
var globalHash = false;
var globalRange = false;
var globalIndexName;
// holds all index info from fields
var indices = {};
// temps for loop
var fieldName;
var column;
var indexInfo;
var indexName;
var indexType;
for (var i = 0; i < fields.length; i++) {
fieldName = fields[i];
column = columns[fieldName];
// set primary hash
if (column.primaryKey && column.primaryKey === true || column.primaryKey === 'hash') {
primaryHash = fieldName;
continue;
}
// set primary range
if (column.primaryKey && column.primaryKey === 'range') {
primaryRange = fieldName;
continue;
}
// set secondary range
if (column.index && column.index === 'secondary') {
secondaryRange = fieldName;
continue;
}
// build global secondary hash info
if (column.index && column.index !== 'secondary') {
indexInfo = adapter._parseIndex(column.index, fieldName);
indexName = indexInfo[0];
indexType = indexInfo[1];
if (typeof indices[indexName] === 'undefined') {
indices[indexName] = {};
}
indices[indexName][indexType] = fieldName;
continue;
}
}
// set global secondary hash info
var indicesHashed;
var indicesRanged;
// pick out those with just a hash key
var indicesHashed = _.pick(indices, function(ind) {
return !!ind.hashKey && !ind.rangeKey;
});
// pick out those with a hash and a range key
var indicesRanged = _.pick(indices, function(ind) {
return !!ind.hashKey && !!ind.rangeKey;
});
// found a good ranged global secondary index?
if (!_.isEmpty(indicesRanged)) {
globalIndexName = Object.keys(indicesRanged)[0];
globalHash = indicesRanged[globalIndexName].hashKey;
globalRange = indicesRanged[globalIndexName].rangeKey;
} else if (!_.isEmpty(indicesHashed)) {
globalIndexName = Object.keys(indicesHashed)[0];
globalHash = indicesHashed[globalIndexName].hashKey;
}
if (primaryHash && primaryRange) {
return {
index: 'primary',
hash: primaryHash,
range: primaryRange
}
} else if (primaryHash && secondaryRange) {
return {
index: secondaryRange+'Index', // per Vogels
hash: primaryHash,
range: secondaryRange
}
} else if (globalHash && globalRange) {
return {
index: globalIndexName,
hash: globalHash,
range: globalRange
}
} else if (primaryHash) {
return {
index: 'primary',
hash: primaryHash
}
} else if (globalHash) {
return {
index: globalIndexName,
hash: globalHash
}
} else {
return false;
}
},
/**
* search condition

@@ -573,12 +808,18 @@ * @param query

* @private
*/, _searchCondition: function (query, options, model) {
*/
_searchCondition: function (query, options, model) {
if (!query) {
query = model.scan();
}
if (!options) {
return query;
}
if ('sort' in options) {
//according to http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html#DDB-Query-request-ScanIndexForward
var sort = _.keys(options.sort)[0];
if (sort == 1) {

@@ -591,10 +832,13 @@ query.ascending();

}
if ('limit' in options) {
query.limit(options.limit);
}
else {
} else {
query.loadAll();
}
return query
}
return query;
},

@@ -611,3 +855,3 @@

* @return {[type]} [description]
*/, create: function (connection, collectionName, values, cb) {
*/create: function (connection, collectionName, values, cb) {
//sails.log.silly("adaptor::create", collectionName);

@@ -620,3 +864,3 @@ //sails.log.silly("values", values);

// If you need to access your private data for this collection:
var collection = _modelReferences[collectionName];
var collection = _collectionReferences[collectionName];
adapter._valueEncode(collection.definition, values);

@@ -627,3 +871,3 @@

if (err) {
sails.log.error(__filename + ", create error:", err);
//sails.log.error(__filename + ", create error:", err);
cb(err);

@@ -659,5 +903,6 @@ }

var Model = adapter._getModel(collectionName);
var primaryKeys = adapter._getPrimaryKeys(collectionName);
// If you need to access your private data for this collection:
var collection = _modelReferences[collectionName];
var collection = _collectionReferences[collectionName];
adapter._valueEncode(collection.definition, values);

@@ -679,10 +924,34 @@

// (do both in a single query if you can-- it's faster)
var updateValues = _.assign(options.where, values);
// Move primary keys to values (Vogels-style) so rest of wheres can be used for expected clause.
// Actually, seems like the primary key has to stay in the wheres so as not to create a new item.
var primaryKeyName;
for (var i = 0; i < primaryKeys.length; i++) {
primaryKeyName = primaryKeys[i];
if (options.where[primaryKeyName]) {
values[primaryKeyName] = options.where[primaryKeyName];
}
}
var vogelsOptions = !_.isEmpty(options.where) ? { expected: options.where } : {};
//console.log(updateValues);
var current = Model.update(updateValues, function (err, res) {
Model.update(values, vogelsOptions, function (err, res) {
if (err) {
sails.log.error('Error update data' + __filename, err);
cb(err);
}
else {
//sails.log.error('Error update data' + __filename, err);
// Deal with AWS's funny way of telling us it couldnt update that item
if (err.code == 'ConditionalCheckFailedException') {
cb(null, []);
} else {
cb(err);
}
} else {
// console.log('add model data',res.attrs);

@@ -714,3 +983,3 @@ adapter._valueDecode(collection.definition, res.attrs);

// If you need to access your private data for this collection:
var collection = _modelReferences[collectionName];
var collection = _collectionReferences[collectionName];

@@ -731,3 +1000,3 @@

if (err) {
sails.log.error('Error destory data' + __filename, err);
//sails.log.error('Error destory data' + __filename, err);
cb(err);

@@ -744,3 +1013,3 @@ }

cb();
}
},

@@ -844,5 +1113,19 @@

* @private
*/, _setColumnType: function (schema, name, attr, options) {
*/
_setColumnType: function (schema, name, attr, options) {
options = (typeof options !== 'undefined') ? options : {};
// Set primary key options
if (attr.primaryKey === 'hash') {
_.merge(options, {hashKey: true});
} else if (attr.primaryKey === 'range') {
_.merge(options, {rangeKey: true});
} else if (attr.index === 'secondary') {
_.merge(options, {secondaryIndex: true});
}
// set columns

@@ -879,2 +1162,13 @@ // console.log("name:", name);

// case "binary":
case "string":
if (attr.autoIncrement) {
schema.UUID(name, options);
} else {
schema.String(name, options);
}
break;
default:

@@ -881,0 +1175,0 @@ // console.log("Set String", name);

{
"name": "sails-dynamodb",
"version": "0.11.4",
"version": "0.12.0",
"description": "Amazon DynamoDB adapter for Sails / Waterline",

@@ -26,3 +26,3 @@ "main": "index.js",

"dependencies": {
"vogels": "0.12.0",
"vogels": "~0.12.0",
"lodash": "",

@@ -62,7 +62,46 @@ "async": ""

"maintainers": [
"gadelkareem <gadelkareem@gmail.com>"
"gadelkareem <gadelkareem@gmail.com>",
"devinivy <devin@bigroomstudios.com>"
],
"directories": {
"test": "test"
}
}
},
"contributors": [
{
"name": "Waleed Gadelkareem",
"email": "gadelkareem@gmail.com",
"url": "https://github.com/gadelkareem",
"contributions": 27,
"additions": 1975,
"deletions": 1842,
"hireable": false
},
{
"name": "Mike McNeil",
"email": "customers@balderdash.co",
"url": "https://github.com/mikermcneil",
"contributions": 31,
"additions": 1371,
"deletions": 725,
"hireable": true
},
{
"name": "devin ivy",
"email": "devin@bigroomstudios.com",
"url": "https://github.com/devinivy",
"contributions": 14,
"additions": 586,
"deletions": 245,
"hireable": false
},
{
"name": "dozo",
"email": "",
"url": "https://github.com/dohzoh",
"contributions": 46,
"additions": 943,
"deletions": 517,
"hireable": false
}
]
}

@@ -73,14 +73,61 @@ # sails-dynamodb

### Pagination
Support for Pagination is added as following:
## Pagination
Support for Pagination is added as following:
1. First add a limit to current request
```
/user?limit=2
```
2. Then get the last primaryKey value and send it as startKey in the next request
```
/user?limit=2&startKey={"PrimaryKey": "2"}
```
## Using DynamoDB Indexes
Primary hash/range keys, local secondary indexes, and global secondary indexes are currently supported by this adapter, but their usage is always inferred from query conditions–`Model.find` will attempt to use the most optimal index using the following precedence:
```
/user?limit=2
Primary hash and range > primary hash and secondary range > global secondary hash and range
> primary hash > global secondary hash > no index/primary
```
2. Then get the last primaryKey value and send it as startKey in the next request
If an index is being used and there are additional query conditions, then results are compiled using DynamoDB's result filtering. If no index can be used for a query, then the adapter will perform a scan on the table for results.
### Adding Indexes
#### Primary hash and primary range
```
/user?limit=2&startKey={"PrimaryKey": "2"}
UserId: {
type: 'integer',
primaryKey: 'hash'
},
GameTitle: {
type: 'string',
primaryKey: 'range'
}
```
#### Secondary range (local secondary index)
The index name used for a local secondary index is the name of the field suffixed by "Index". In this case the index name is `TimeIndex`.
```
Time: {
type: 'datetime',
index: 'secondary'
}
```
#### Global secondary index
The index name used for a global secondary index is specified in the `index` property before the type of key (`hash` or `range`). In this case the index name is `GameTitleIndex`.
```
GameTitle: {
type: 'string',
index: 'GameTitleIndex-hash'
},
HighScore: {
type: 'integer',
index: 'GameTitleIndex-range'
}
```
## Update
The `Model.update` method is currently expected to update exactly one item since DynamoDB only offers an [UpdateItem](http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html) endpoint. A complete primary key must be supplied. Any additional "where" conditions passed to `Model.update` are used to build a conditional expression for the update. Despite the fact the DynamoDB updates only one item, `Model.update` will always return an array of the (one or zero) updated items upon success.
## Testing

@@ -87,0 +134,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc