UNPKG

dynamodb-toolbox

Version:

Lightweight and type-safe query builder for DynamoDB and TypeScript.

165 lines (164 loc) 8.84 kB
"use strict"; 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;