dynamodb-ts-model
Version:
A DynamoDB model/client with full TypeScript typings
182 lines • 9.34 kB
JavaScript
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
var __asyncDelegator = (this && this.__asyncDelegator) || function (o) {
var i, p;
return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DynamoBatchWriteStatement = exports.DynamoBatchGetStatement = exports.DynamoBatchStatementProxy = void 0;
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
const DynamoWrapper_1 = require("./DynamoWrapper");
const requests_1 = require("./requests");
const utils_1 = require("./utils");
function batchResultIterator(execute) {
return __asyncGenerator(this, arguments, function* batchResultIterator_1() {
let delay = 100;
while (true) {
const { items, done } = yield __await(execute());
for (const item of items) {
yield yield __await(item);
}
if (done) {
return yield __await(void 0);
}
yield __await(new Promise(resolve => setTimeout(resolve, Math.random() * delay)));
delay *= 2;
}
});
}
class DynamoBatchStatementProxy extends DynamoWrapper_1.DynamoWrapper {
get(model, ...paramsList) {
return new DynamoBatchGetStatement(this.client, this.name).get(model, ...paramsList);
}
put(model, ...paramsList) {
return new DynamoBatchWriteStatement(this.client, this.name).put(model, ...paramsList);
}
delete(model, ...paramsList) {
return new DynamoBatchWriteStatement(this.client, this.name).delete(model, ...paramsList);
}
}
exports.DynamoBatchStatementProxy = DynamoBatchStatementProxy;
class DynamoBatchGetStatement extends DynamoWrapper_1.DynamoWrapper {
constructor() {
super(...arguments);
this.requestMap = {};
this.modelMap = new Map();
}
get(model, ...paramsList) {
var _a;
for (const params of paramsList) {
let keys = (_a = this.requestMap[model.tableName]) === null || _a === void 0 ? void 0 : _a.Keys;
if (!keys) {
this.requestMap[model.tableName] = { Keys: keys = [] };
}
keys.push(params.key);
this.modelMap.set(model.tableName, model);
}
return this;
}
/**
* Execute the statements in this batch and return the retrieved items.
* Note that while done is false, some statements were not executed and this method should then be called again,
* preferably after utilizing exponential backoff.
*/
async execute() {
const { Responses: itemMap = {}, UnprocessedKeys: nextRequestMap } = await this.command(new lib_dynamodb_1.BatchGetCommand({
RequestItems: this.requestMap,
ReturnConsumedCapacity: (0, requests_1.getReturnedConsumedCapacity)(this)
}));
const items = [];
for (const [tableName, tableItems] of Object.entries(itemMap)) {
const model = this.modelMap.get(tableName);
for (const item of tableItems) {
items.push({ model, item });
}
}
this.requestMap = nextRequestMap !== null && nextRequestMap !== void 0 ? nextRequestMap : {};
// Keys that don't exist end up as unprocessed items, so clients should only loop if !done && items.length > 0,
// otherwise there will be an infinite loop trying to fetch non-existent items.
return {
items,
done: Object.keys(this.requestMap).length === 0
};
}
/**
* Returns an iterator which executes the statements in this batch and returns the retrieved items until done,
* adding a random delay between batches.
*/
executeIterator() {
return __asyncGenerator(this, arguments, function* executeIterator_1() {
yield __await(yield* __asyncDelegator(__asyncValues(batchResultIterator(() => this.execute()))));
});
}
}
exports.DynamoBatchGetStatement = DynamoBatchGetStatement;
class DynamoBatchWriteStatement extends DynamoWrapper_1.DynamoWrapper {
constructor() {
super(...arguments);
this.requestMap = {};
this.modelMap = new Map();
}
put(model, ...paramsList) {
for (const params of paramsList) {
let requestItems = this.requestMap[model.tableName];
if (!requestItems) {
this.requestMap[model.tableName] = requestItems = [];
}
requestItems.push({ PutRequest: (0, requests_1.createPutRequest)(model, params) });
this.modelMap.set(model.tableName, model);
}
return this;
}
delete(model, ...paramsList) {
for (const params of paramsList) {
let requestItems = this.requestMap[model.tableName];
if (!requestItems) {
this.requestMap[model.tableName] = requestItems = [];
}
requestItems.push({ DeleteRequest: (0, requests_1.createDeleteRequest)(model, params) });
this.modelMap.set(model.tableName, model);
}
return this;
}
/**
* Execute the statements in this batch. Note that while done is false, some statements were not executed and this
* method should then be called again, preferably after utilizing exponential backoff.
*/
async execute() {
var _a;
const { UnprocessedItems: nextRequestMap } = await this.command(new lib_dynamodb_1.BatchWriteCommand({
RequestItems: this.requestMap,
ReturnConsumedCapacity: (0, requests_1.getReturnedConsumedCapacity)(this)
}));
const items = [];
for (const [tableName, requests] of Object.entries(this.requestMap)) {
const model = this.modelMap.get(tableName);
const nextKeyValues = (_a = nextRequestMap === null || nextRequestMap === void 0 ? void 0 : nextRequestMap[tableName]) === null || _a === void 0 ? void 0 : _a.map(request => JSON.stringify((0, utils_1.getKeyValues)((0, utils_1.parseRequest)(request).key, model.params.keyAttributes)));
for (const request of requests) {
const { key, command } = (0, utils_1.parseRequest)(request);
const keyValue = JSON.stringify((0, utils_1.getKeyValues)(key, model.params.keyAttributes));
if (!(nextKeyValues === null || nextKeyValues === void 0 ? void 0 : nextKeyValues.some(v => v === keyValue))) {
// This request was processed
model.params.triggers.forEach(trigger => trigger(key, command, model));
items.push({ model, command, key });
}
}
}
this.requestMap = nextRequestMap !== null && nextRequestMap !== void 0 ? nextRequestMap : {};
return {
items,
done: Object.keys(this.requestMap).length === 0
};
}
/**
* Returns an iterator which executes the statements in this batch until done, adding a random delay between batches.
*/
executeIterator() {
return __asyncGenerator(this, arguments, function* executeIterator_2() {
yield __await(yield* __asyncDelegator(__asyncValues(batchResultIterator(() => this.execute()))));
});
}
}
exports.DynamoBatchWriteStatement = DynamoBatchWriteStatement;
//# sourceMappingURL=DynamoBatch.js.map
;