UNPKG

dynamodb-toolbox

Version:

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

150 lines (149 loc) 6.27 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseQuery = void 0; const index_js_1 = require("../../../../errors/index.js"); const index_js_2 = require("../../../../schema/actions/parseCondition/index.js"); const schema_js_1 = require("../../../../schema/binary/schema.js"); const schema_js_2 = require("../../../../schema/item/schema.js"); const schema_js_3 = require("../../../../schema/number/schema.js"); const schema_js_4 = require("../../../../schema/string/schema.js"); const index_js_3 = require("../../../../table/index.js"); const pick_js_1 = require("../../../../utils/pick.js"); const isArray_js_1 = require("../../../../utils/validation/isArray.js"); const isObject_js_1 = require("../../../../utils/validation/isObject.js"); const queryOperatorSet = new Set([ 'eq', 'gt', 'gte', 'lt', 'lte', 'between', 'beginsWith' ]); const getIndexKeySchema = (key) => { switch (key.type) { case 'number': return new schema_js_3.NumberSchema({ big: true }); case 'string': return new schema_js_4.StringSchema({}); case 'binary': return new schema_js_1.BinarySchema({}); } }; const parseQuery = (table, query) => { const queryIndex = getQueryIndex(table, query); const { partitionKeys, sortKeys } = flattenQuerySchema(table, queryIndex); const { partition, range } = query; const isPartitionArray = (0, isArray_js_1.isArray)(partition); const hasRange = range !== undefined; const isRangeArray = hasRange && (0, isArray_js_1.isArray)(range); if (!(queryIndex instanceof index_js_3.Table) && queryIndex.type === 'global') { if ('partitionKeys' in queryIndex && !isPartitionArray) { throw new index_js_1.DynamoDBToolboxError('queryCommand.invalidPartition', { message: 'Invalid query partition. Expected: Array.', payload: { partition } }); } if (hasRange && 'sortKeys' in queryIndex && !isRangeArray) { throw new index_js_1.DynamoDBToolboxError('queryCommand.invalidRange', { message: 'Invalid query range. Expected: Array.', payload: { range } }); } } const partitions = isPartitionArray ? partition : [partition]; if (partitions.length !== partitionKeys.length) { throw new index_js_1.DynamoDBToolboxError('queryCommand.invalidPartition', { message: `Invalid number of query partitions. Expected: ${partitionKeys.length}.`, payload: { partition: partitions } }); } const ranges = hasRange ? (isRangeArray ? range : [range]) : []; if (ranges.length > sortKeys.length) { throw new index_js_1.DynamoDBToolboxError('queryCommand.invalidRange', { message: `Invalid number of query ranges. Expected: Less than or equal to ${sortKeys.length}.`, payload: { range: ranges } }); } const keyConditions = []; let partitionIndex = 0; for (const partition of partitions) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion keyConditions.push({ attr: partitionKeys[partitionIndex].name, eq: partition }); partitionIndex++; } if (hasRange) { let hasRangeObject = false; let rangeIndex = 0; for (const range of ranges) { if (hasRangeObject) { throw new index_js_1.DynamoDBToolboxError('queryCommand.invalidRange', { message: 'Invalid query range: Range object must be provided last.', payload: { range } }); } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const sortKey = sortKeys[rangeIndex]; if ((0, isObject_js_1.isObject)(range)) { keyConditions.push({ attr: sortKey.name, ...(0, pick_js_1.pick)(range, ...queryOperatorSet) }); hasRangeObject = true; } else { keyConditions.push({ attr: sortKey.name, eq: range }); } rangeIndex++; } } const indexSchema = new schema_js_2.ItemSchema(Object.fromEntries([...partitionKeys, ...sortKeys].map(key => [key.name, getIndexKeySchema(key)]))); const conditionParser = new index_js_2.ConditionParser(indexSchema); const { ConditionExpression, ExpressionAttributeNames, ExpressionAttributeValues } = conditionParser.parse({ and: keyConditions }, { expressionId: '0' }); return { KeyConditionExpression: ConditionExpression, ExpressionAttributeNames, ExpressionAttributeValues }; }; exports.parseQuery = parseQuery; const getQueryIndex = (table, { index }) => { if (index === undefined) { return table; } const indexSchema = table.indexes[index]; if (indexSchema === undefined) { const indexes = Object.keys(table.indexes); const hasIndex = indexes.length > 0; throw new index_js_1.DynamoDBToolboxError('queryCommand.invalidIndex', { message: `Unknown index: ${index}. ${hasIndex ? ` Expected one of: ${indexes.join(', ')}.` : ''}`, payload: { received: index, ...(hasIndex ? { expected: indexes } : {}) } }); } return indexSchema; }; const flattenQuerySchema = (table, queryIndex) => { var _a; if (queryIndex instanceof index_js_3.Table) { return { partitionKeys: [queryIndex.partitionKey], sortKeys: queryIndex.sortKey ? [queryIndex.sortKey] : [] }; } switch (queryIndex.type) { case 'global': return { partitionKeys: (_a = queryIndex.partitionKeys) !== null && _a !== void 0 ? _a : [queryIndex.partitionKey], sortKeys: queryIndex.sortKeys ? queryIndex.sortKeys : queryIndex.sortKey ? [queryIndex.sortKey] : [] }; case 'local': return { partitionKeys: [table.partitionKey], sortKeys: [queryIndex.sortKey] }; } };