@baselinejs/dynamodb
Version:
DynamoDB library for simple and optimized way to use AWS DynamoDB
553 lines (550 loc) • 19 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/index.ts
import {
DynamoDB
} from "@aws-sdk/client-dynamodb";
import {
DynamoDBDocument
} from "@aws-sdk/lib-dynamodb";
import {
marshall,
unmarshall
} from "@aws-sdk/util-dynamodb";
// src/utils/utils.ts
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
var decorrelatedJitterBackoff = (previousDelay) => {
const maxDelayMiliseconds = 4e3;
const baseMiliseconds = 50;
const nextDelay = Math.min(
maxDelayMiliseconds,
Math.random() * previousDelay * 3 + baseMiliseconds
);
return nextDelay;
};
var buildCondition = (args) => {
const { operator, value, betweenSecondValue, field } = args;
let conditionExpression = "";
switch (operator) {
case "BeginsWith":
conditionExpression = `begins_with(${field}, ${value})`;
break;
case "Equal":
conditionExpression = `${field} = ${value}`;
break;
case "NotEqual":
conditionExpression = `${field} <> ${value}`;
break;
case "GreaterThan":
conditionExpression = `${field} > ${value}`;
break;
case "GreaterThanEqual":
conditionExpression = `${field} >= ${value}`;
break;
case "LessThan":
conditionExpression = `${field} < ${value}`;
break;
case "LessThanEqual":
conditionExpression = `${field} <= ${value}`;
break;
case "Between":
conditionExpression = `${field} BETWEEN ${value} AND ${betweenSecondValue}`;
break;
case "AttributeExists":
conditionExpression = `attribute_exists(${field})`;
break;
case "AttributeNotExists":
conditionExpression = `attribute_not_exists(${field})`;
break;
default:
throw new Error("Unknown Query Condition type");
}
return conditionExpression;
};
var buildConditionExpression = (conditions) => {
if (!(conditions == null ? void 0 : conditions.length)) {
return null;
}
let count = 0;
const attributeNames = {};
const attributeValues = {};
let conditionExpression = "";
conditions.forEach((values) => {
if (conditionExpression == null ? void 0 : conditionExpression.length) {
conditionExpression += " AND ";
}
conditionExpression += buildCondition({
field: `#field${count}`,
value: `:val${count}`,
operator: values.operator,
betweenSecondValue: `:val${count + 1}`
});
attributeNames[`#field${count}`] = values.field;
if (values.value !== void 0) {
attributeValues[`:val${count}`] = values.value;
}
if (values.betweenSecondValue !== void 0) {
attributeValues[`:val${count + 1}`] = values.betweenSecondValue;
}
count += 2;
});
if (!(conditionExpression == null ? void 0 : conditionExpression.length)) {
return null;
}
return {
conditionExpression,
attributeNames: Object.keys(attributeNames).length ? attributeNames : null,
attributeValues: Object.keys(attributeValues).length ? attributeValues : null
};
};
// src/index.ts
var IS_OFFLINE = process.env.IS_OFFLINE;
var FORCE_ONLINE = process.env.FORCE_ONLINE;
function getDynamodbConnection(config) {
let newConnection;
if (IS_OFFLINE === "true" && FORCE_ONLINE !== "true") {
newConnection = DynamoDBDocument.from(
new DynamoDB({
region: "localhost",
endpoint: "http://localhost:8000"
})
);
} else {
newConnection = DynamoDBDocument.from(new DynamoDB(config));
}
return newConnection;
}
var putItem = (params) => __async(void 0, null, function* () {
const dynamoDb = params.dynamoDb;
const putItemInput = {
TableName: params.table,
Item: params.item,
ReturnValuesOnConditionCheckFailure: "ALL_OLD"
};
const conditionalData = buildConditionExpression(params.conditions);
if (conditionalData == null ? void 0 : conditionalData.attributeNames) {
putItemInput.ExpressionAttributeNames = conditionalData.attributeNames;
}
if (conditionalData == null ? void 0 : conditionalData.attributeValues) {
putItemInput.ExpressionAttributeValues = conditionalData.attributeValues;
}
if (conditionalData == null ? void 0 : conditionalData.conditionExpression) {
putItemInput.ConditionExpression = conditionalData.conditionExpression;
}
yield dynamoDb.put(putItemInput);
return params.item;
});
var updateItem = (params) => __async(void 0, null, function* () {
const updateItems = [];
let count = 0;
const keyNames = Object.keys(params.key);
for (const element in params.fields) {
const attributeValue = params.fields[element];
if (attributeValue !== void 0 && !keyNames.includes(element)) {
updateItems.push({
name: element,
attributeName: `#attr${count}`,
attributeValue,
ref: `:attr${count}`
});
count++;
}
}
const removeAttributeItems = [];
for (const field of params.removeFields || []) {
if (field !== void 0 && !keyNames.includes(field)) {
removeAttributeItems.push({
name: field,
attributeName: `#attr${count}`
});
count++;
}
}
if (!updateItems.length && !removeAttributeItems.length) {
throw new Error("No fields or removeFields provided to updateItem");
}
let updateExpression = "";
if (updateItems.length) {
updateExpression = "SET " + updateItems.map((i) => `${i.attributeName}=${i.ref}`).join(", ");
}
if (removeAttributeItems.length) {
updateExpression += (updateExpression.length > 1 ? " REMOVE " : "REMOVE") + removeAttributeItems.map((i) => i.attributeName).join(", ");
}
const expressionAttributeValues = updateItems.reduce(
(p, c) => {
p[`${c.ref}`] = c.attributeValue;
return p;
},
{}
);
const expressionAttributeNames = [
...updateItems,
...removeAttributeItems
].reduce(
(p, c) => {
p[`${c.attributeName}`] = c.name;
return p;
},
{}
);
const updateItemInput = {
TableName: params.table || "",
Key: params.key,
UpdateExpression: updateExpression,
ReturnValues: "ALL_NEW",
ExpressionAttributeNames: expressionAttributeNames,
ExpressionAttributeValues: expressionAttributeValues,
ReturnValuesOnConditionCheckFailure: "ALL_OLD"
};
const conditionalData = buildConditionExpression(params.conditions);
if (conditionalData == null ? void 0 : conditionalData.attributeNames) {
updateItemInput.ExpressionAttributeNames = __spreadValues(__spreadValues({}, updateItemInput.ExpressionAttributeNames), conditionalData.attributeNames);
}
if (conditionalData == null ? void 0 : conditionalData.attributeValues) {
updateItemInput.ExpressionAttributeValues = __spreadValues(__spreadValues({}, updateItemInput.ExpressionAttributeValues), conditionalData.attributeValues);
}
if (conditionalData == null ? void 0 : conditionalData.conditionExpression) {
updateItemInput.ConditionExpression = conditionalData.conditionExpression;
}
const result = yield params.dynamoDb.update(updateItemInput);
return result.Attributes;
});
var deleteItem = (params) => __async(void 0, null, function* () {
const deleteInput = {
TableName: `${params.table}`,
Key: params.key,
ReturnValuesOnConditionCheckFailure: "ALL_OLD"
};
const conditionalData = buildConditionExpression(params.conditions);
if (conditionalData == null ? void 0 : conditionalData.attributeNames) {
deleteInput.ExpressionAttributeNames = conditionalData.attributeNames;
}
if (conditionalData == null ? void 0 : conditionalData.attributeValues) {
deleteInput.ExpressionAttributeValues = conditionalData.attributeValues;
}
if (conditionalData == null ? void 0 : conditionalData.conditionExpression) {
deleteInput.ConditionExpression = conditionalData.conditionExpression;
}
yield params.dynamoDb.delete(deleteInput);
return true;
});
function getItem(params) {
return __async(this, null, function* () {
const getItemInput = {
TableName: params.table || "",
Key: params.key
};
if (params.consistentRead) {
getItemInput.ConsistentRead = params.consistentRead;
}
const result = yield params.dynamoDb.get(getItemInput);
return result.Item;
});
}
function getAllItems(params) {
return __async(this, null, function* () {
const scanInput = {
TableName: params.table || ""
};
if (params.consistentRead) {
scanInput.ConsistentRead = params.consistentRead;
}
if (params.limit) {
scanInput.Limit = params.limit;
}
if (params.projectionExpression) {
scanInput.ProjectionExpression = params.projectionExpression.join(",");
}
if (params.exclusiveStartKey) {
scanInput.ExclusiveStartKey = params.exclusiveStartKey;
}
const conditionalData = buildConditionExpression(params.filterConditions);
if (conditionalData == null ? void 0 : conditionalData.attributeNames) {
scanInput.ExpressionAttributeNames = conditionalData.attributeNames;
}
if (conditionalData == null ? void 0 : conditionalData.attributeValues) {
scanInput.ExpressionAttributeValues = conditionalData.attributeValues;
}
if (conditionalData == null ? void 0 : conditionalData.conditionExpression) {
scanInput.FilterExpression = conditionalData.conditionExpression;
}
const allRecords = [];
let lastKey = void 0;
do {
const result = yield params.dynamoDb.scan(scanInput);
const resultRecords = result.Items;
allRecords.push(...resultRecords);
lastKey = result.LastEvaluatedKey;
scanInput.ExclusiveStartKey = lastKey;
} while (lastKey && (params.limit ? allRecords.length < params.limit : true));
return allRecords;
});
}
var batchGetItems = (params) => __async(void 0, null, function* () {
var _a, _b, _c, _d, _e, _f;
if (!params.keys.length) {
return [];
}
const unique = /* @__PURE__ */ new Map();
for (const item of params.keys) {
const key = JSON.stringify(item);
if (!unique.has(key)) {
unique.set(key, item);
}
}
const uniqueIds = Array.from(unique.values());
const totalBatches = Math.ceil(uniqueIds.length / 100);
const keyBatches = [];
for (let index = 0; index < totalBatches; index++) {
const start = index * 100;
const end = start + 100 > uniqueIds.length ? uniqueIds.length : start + 100;
const batch = uniqueIds.slice(start, end);
keyBatches.push(batch);
}
const initialPromises = keyBatches.map((keyBatch) => {
const batchGetInput = {
RequestItems: {
[params.table]: {
Keys: keyBatch
}
}
};
return params.dynamoDb.batchGet(batchGetInput);
});
const initialResults = yield Promise.all(initialPromises);
let allRecords = [];
for (const result of initialResults) {
const records = (_a = result.Responses) == null ? void 0 : _a[params.table];
allRecords = allRecords.concat(records);
let unprocessedKeys = (_c = (_b = result.UnprocessedKeys) == null ? void 0 : _b[params.table]) == null ? void 0 : _c.Keys;
while (unprocessedKeys && unprocessedKeys.length) {
const batchGetInput = {
RequestItems: {
[params.table]: {
Keys: unprocessedKeys
}
}
};
const retryResult = yield params.dynamoDb.batchGet(batchGetInput);
const retryRecords = (_d = retryResult.Responses) == null ? void 0 : _d[params.table];
allRecords = allRecords.concat(retryRecords);
unprocessedKeys = (_f = (_e = retryResult.UnprocessedKeys) == null ? void 0 : _e[params.table]) == null ? void 0 : _f.Keys;
}
}
return allRecords;
});
function queryItems(params) {
return __async(this, null, function* () {
const queryInput = {
TableName: params.table,
KeyConditionExpression: `#a = :b`,
ExpressionAttributeNames: {
"#a": params.keyName
},
ExpressionAttributeValues: {
":b": params.keyValue
}
};
if (params == null ? void 0 : params.indexName) {
queryInput.IndexName = params == null ? void 0 : params.indexName;
}
if (params == null ? void 0 : params.exclusiveStartKey) {
queryInput.ExclusiveStartKey = params == null ? void 0 : params.exclusiveStartKey;
}
if (params.consistentRead) {
queryInput.ConsistentRead = params.consistentRead;
}
if ((params == null ? void 0 : params.scanIndexForward) === false) {
queryInput.ScanIndexForward = false;
}
if (params == null ? void 0 : params.limit) {
queryInput.Limit = params.limit;
}
if (params.projectionExpression) {
queryInput.ProjectionExpression = params.projectionExpression.join(",");
}
const rangeData = buildConditionExpression(
params.rangeCondition ? [params.rangeCondition] : void 0
);
if (rangeData == null ? void 0 : rangeData.attributeNames) {
queryInput.ExpressionAttributeNames = __spreadValues(__spreadValues({}, queryInput.ExpressionAttributeNames), rangeData.attributeNames);
}
if (rangeData == null ? void 0 : rangeData.attributeValues) {
queryInput.ExpressionAttributeValues = __spreadValues(__spreadValues({}, queryInput.ExpressionAttributeValues), rangeData.attributeValues);
}
if (rangeData == null ? void 0 : rangeData.conditionExpression) {
queryInput.KeyConditionExpression = `${queryInput.KeyConditionExpression} AND ${rangeData == null ? void 0 : rangeData.conditionExpression}`;
}
const allRecords = [];
let lastKey = void 0;
do {
const result = yield params.dynamoDb.query(queryInput);
const resultRecords = result.Items;
allRecords.push(...resultRecords);
lastKey = result.LastEvaluatedKey;
queryInput.ExclusiveStartKey = lastKey;
} while (lastKey && (params.limit ? allRecords.length < params.limit : true));
return allRecords;
});
}
function queryItemsRange(params) {
return __async(this, null, function* () {
const result = yield queryItems(__spreadProps(__spreadValues({}, params), {
rangeCondition: {
operator: params.fuzzy ? "BeginsWith" : "Equal",
field: params.rangeKeyName,
value: params.rangeKeyValue
}
}));
return result;
});
}
function queryItemsRangeBetween(params) {
return __async(this, null, function* () {
const result = yield queryItems(__spreadProps(__spreadValues({}, params), {
rangeCondition: {
operator: "Between",
field: params.rangeKeyName,
value: params.rangeKeyValueMin,
betweenSecondValue: params.rangeKeyValueMax
}
}));
return result;
});
}
var batchPutItems = (params) => __async(void 0, null, function* () {
const totalBatches = Math.ceil(params.items.length / 25);
const itemBatches = [];
for (let index = 0; index < totalBatches; index++) {
const start = index * 25;
const end = start + 25 > params.items.length ? params.items.length : start + 25;
const batch = params.items.slice(start, end);
itemBatches.push(batch);
}
const initialPromises = itemBatches.map((itemsBatch) => {
const batchWriteInput = {
RequestItems: {
[params.table]: itemsBatch.map((item) => ({
PutRequest: {
Item: item
}
}))
}
};
return params.dynamoDb.batchWrite(batchWriteInput);
});
const initialResults = yield Promise.all(initialPromises);
let previousDelay = 0;
for (const result of initialResults) {
let unprocessedItems = result.UnprocessedItems;
while (Object.keys(unprocessedItems || {}).length > 0) {
const batchWriteInput = {
RequestItems: unprocessedItems
};
const delay = decorrelatedJitterBackoff(previousDelay);
yield sleep(delay);
const retryResult = yield params.dynamoDb.batchWrite(batchWriteInput);
unprocessedItems = retryResult.UnprocessedItems;
}
}
return params.items;
});
var batchDeleteItems = (params) => __async(void 0, null, function* () {
const totalBatches = Math.ceil(params.keys.length / 25);
const keyBatches = [];
for (let index = 0; index < totalBatches; index++) {
const start = index * 25;
const end = start + 25 > params.keys.length ? params.keys.length : start + 25;
const batch = params.keys.slice(start, end);
keyBatches.push(batch);
}
const initialPromises = keyBatches.map((keysBatch) => {
const batchWriteInput = {
RequestItems: {
[params.table]: keysBatch.map((item) => ({
DeleteRequest: {
Key: item
}
}))
}
};
return params.dynamoDb.batchWrite(batchWriteInput);
});
const initialResults = yield Promise.all(initialPromises);
let previousDelay = 0;
for (const result of initialResults) {
let unprocessedItems = result.UnprocessedItems;
while (Object.keys(unprocessedItems || {}).length > 0) {
const batchWriteInput = {
RequestItems: unprocessedItems
};
const delay = decorrelatedJitterBackoff(previousDelay);
yield sleep(delay);
const retryResult = yield params.dynamoDb.batchWrite(batchWriteInput);
unprocessedItems = retryResult.UnprocessedItems;
}
}
return true;
});
var unmarshallItem = (item, options) => {
const unmarshallItem2 = unmarshall(item, options);
return unmarshallItem2;
};
var marshallItem = (item, options) => {
const unmarshallItem2 = marshall(item, options);
return unmarshallItem2;
};
export {
batchDeleteItems,
batchGetItems,
batchPutItems,
deleteItem,
getAllItems,
getDynamodbConnection,
getItem,
marshallItem,
putItem,
queryItems,
queryItemsRange,
queryItemsRangeBetween,
unmarshallItem,
updateItem
};
//# sourceMappingURL=index.mjs.map