dynamodb-toolbox
Version:
Lightweight and type-safe query builder for DynamoDB and TypeScript.
165 lines (164 loc) • 8.84 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.QueryCommand = exports.IQueryCommand = void 0;
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
const index_js_1 = require("../../../entity/actions/format/index.js");
const index_js_2 = require("../../../entity/utils/index.js");
const index_js_3 = require("../../../errors/index.js");
const constants_js_1 = require("../../../table/constants.js");
const decorator_js_1 = require("../../../table/decorator.js");
const index_js_4 = require("../../../table/index.js");
const isString_js_1 = require("../../../utils/validation/isString.js");
const constants_js_2 = require("./constants.js");
const index_js_5 = require("./queryParams/index.js");
class IQueryCommand extends index_js_4.TableAction {
constructor(table, entities = [], query, options = {}) {
super(table, entities);
this.params = () => {
return (0, index_js_5.queryParams)(this.table, ...this[constants_js_1.$sentArgs]());
};
this[constants_js_2.$query] = query;
this[constants_js_2.$options] = options;
}
[constants_js_1.$sentArgs]() {
if (!this[constants_js_2.$query]) {
throw new index_js_3.DynamoDBToolboxError('actions.incompleteAction', {
message: 'QueryCommand incomplete: Missing "query" property'
});
}
return [
this[index_js_4.$entities],
this[constants_js_2.$query],
/**
* @debt type "Make any QueryOptions<...> instance extend base QueryOptions"
*/
this[constants_js_2.$options]
];
}
async send(documentClientOptions) {
const entityAttrSavedAs = this.table.entityAttributeSavedAs;
const queryParams = this.params();
const formattersByName = {};
this[index_js_4.$entities].forEach(entity => {
formattersByName[entity.entityName] = entity.build(index_js_1.EntityFormatter);
});
const formattedItems = [];
let lastEvaluatedKey = undefined;
let count = 0;
let scannedCount = 0;
let consumedCapacity = undefined;
let responseMetadata = undefined;
const { attributes, maxPages = 1, showEntityAttr = false, tagEntities = false, noEntityMatchBehavior = 'THROW' } = this[constants_js_2.$options];
let pageIndex = 0;
do {
pageIndex += 1;
const pageQueryParams = {
...queryParams,
// NOTE: Important NOT to override initial exclusiveStartKey on first page
...(lastEvaluatedKey !== undefined ? { ExclusiveStartKey: lastEvaluatedKey } : {})
};
const { Items: items = [], LastEvaluatedKey: pageLastEvaluatedKey, Count: pageCount, ScannedCount: pageScannedCount, ConsumedCapacity: pageConsumedCapacity, $metadata: pageMetadata } = await this.table
.getDocumentClient()
.send(new lib_dynamodb_1.QueryCommand(pageQueryParams), documentClientOptions);
for (const item of items) {
if (this[index_js_4.$entities].length === 0) {
formattedItems.push(item);
continue;
}
const itemEntityName = item[entityAttrSavedAs];
const itemEntityFormatter = (0, isString_js_1.isString)(itemEntityName)
? formattersByName[itemEntityName]
: undefined;
if (itemEntityFormatter === undefined) {
let hasEntityMatch = false;
// If data doesn't contain entity name (e.g. migrating to DynamoDB-Toolbox), we try all formatters
// (NOTE: Can only happen if `entityAttrFilter` is false)
for (const [entityName, formatter] of Object.entries(formattersByName)) {
try {
const formattedItem = formatter.format(item, { attributes });
const { entityAttribute } = formatter.entity;
const entityAttrName = (0, index_js_2.getEntityAttrOptionValue)(entityAttribute, 'name');
const addEntityAttr = showEntityAttr && (0, index_js_2.isEntityAttrEnabled)(entityAttribute);
formattedItems.push({
...formattedItem,
...(addEntityAttr ? { [entityAttrName]: entityName } : {}),
// $entity symbol is useful for the DeletePartition command
...(tagEntities ? { [constants_js_2.$entity]: entityName } : {})
});
hasEntityMatch = true;
break;
}
catch {
continue;
}
}
if (!hasEntityMatch && noEntityMatchBehavior === 'THROW') {
throw new index_js_3.DynamoDBToolboxError('queryCommand.noEntityMatched', {
message: 'Unable to match item of unidentified entity to the QueryCommand entities',
payload: { item }
});
}
continue;
}
const formattedItem = itemEntityFormatter.format(item, { attributes });
const { entityAttribute, entityName } = itemEntityFormatter.entity;
const entityAttrName = (0, index_js_2.getEntityAttrOptionValue)(entityAttribute, 'name');
const addEntityAttr = showEntityAttr && (0, index_js_2.isEntityAttrEnabled)(entityAttribute);
formattedItems.push({
...formattedItem,
...(addEntityAttr ? { [entityAttrName]: entityName } : {}),
// $entity symbol is useful for the DeletePartition command
...(tagEntities ? { [constants_js_2.$entity]: entityName } : {})
});
}
lastEvaluatedKey = pageLastEvaluatedKey;
if (count !== undefined) {
count = pageCount !== undefined ? count + pageCount : undefined;
}
if (scannedCount !== undefined) {
scannedCount = pageScannedCount !== undefined ? scannedCount + pageScannedCount : undefined;
}
consumedCapacity = pageConsumedCapacity;
responseMetadata = pageMetadata;
} while (pageIndex < maxPages && lastEvaluatedKey !== undefined);
return {
Items: formattedItems,
...(lastEvaluatedKey !== undefined ? { LastEvaluatedKey: lastEvaluatedKey } : {}),
...(count !== undefined ? { Count: count } : {}),
...(scannedCount !== undefined ? { ScannedCount: scannedCount } : {}),
// return ConsumedCapacity & $metadata only if one page has been fetched
...(pageIndex === 1
? {
...(consumedCapacity !== undefined ? { ConsumedCapacity: consumedCapacity } : {}),
...(responseMetadata !== undefined ? { $metadata: responseMetadata } : {})
}
: {})
};
}
}
exports.IQueryCommand = IQueryCommand;
IQueryCommand.actionName = 'query';
__decorate([
(0, decorator_js_1.interceptable)()
], IQueryCommand.prototype, "send", null);
class QueryCommand extends IQueryCommand {
constructor(table, entities = [], query, options = {}) {
super(table, entities, query, options);
}
entities(...nextEntities) {
return new QueryCommand(this.table, nextEntities, this[constants_js_2.$query], this[constants_js_2.$options]);
}
query(nextQuery) {
return new QueryCommand(this.table, this[index_js_4.$entities], nextQuery, this[constants_js_2.$options]);
}
options(nextOptions) {
return new QueryCommand(this.table, this[index_js_4.$entities], this[constants_js_2.$query], typeof nextOptions === 'function' ? nextOptions(this[constants_js_2.$options]) : nextOptions);
}
}
exports.QueryCommand = QueryCommand;