UNPKG

@env0/dynamo-easy

Version:

DynamoDB client for NodeJS and browser with a fluent api to build requests. We take care of the type mapping between JS and DynamoDB, customizable trough typescript decorators.

149 lines 6.96 kB
import * as tslib_1 from "tslib"; import { alterCollectionPropertyMetadataForSingleItem, } from '../../decorator/metadata/property-metadata.model'; import { toDbOne } from '../../mapper/mapper'; import { getPropertyPath, isSet } from '../../mapper/util'; import { deepFilter } from './condition-expression-builder'; import { resolveAttributeNames } from './functions/attribute-names.function'; import { uniqueAttributeValueName } from './functions/unique-attribute-value-name.function'; /** * Will create a condition which can be added to a request using the param object. * It will create the expression statement and the attribute names and values. * * @param {string} attributePath * @param {ConditionOperator} operation * @param {any[]} values Depending on the operation the amount of values differs * @param {string[]} existingValueNames If provided the existing names are used to make sure we have a unique name for the current attributePath * @param {Metadata<any>} metadata If provided we use the metadata to define the attribute name and use it to map the given value(s) to attributeValue(s) * @returns {Expression} * @hidden */ export function buildUpdateExpression(attributePath, operation, values, existingValueNames, metadata) { // metadata get rid of undefined values values = deepFilter(values, function (value) { return value !== undefined; }) || []; // load property metadata if model metadata was provided var propertyMetadata; if (metadata) { propertyMetadata = metadata.forProperty(attributePath); } /* * resolve placeholder and valuePlaceholder names (same as attributePath if it not already exists) * myProp -> #myProp for name placeholder and :myProp for value placeholder * * person[0] -> #person: person * person.list[0].age -> #person: person, #attr: attr, #age: age * person.age */ var resolvedAttributeNames = resolveAttributeNames(attributePath, metadata); var valuePlaceholder = uniqueAttributeValueName(attributePath, existingValueNames); /* * build the statement */ return buildDefaultExpression(attributePath, resolvedAttributeNames.placeholder, valuePlaceholder, resolvedAttributeNames.attributeNames, values, existingValueNames, propertyMetadata, operation); } /** * @hidden */ function buildDefaultExpression(attributePath, namePlaceholder, valuePlaceholder, attributeNames, values, existingValueNames, propertyMetadata, operator) { var attributeValues = {}; var attribute = null; if (!isNoAttributeValueAction(operator.action)) { // special cases: appendToList, add, removeFromSet // we allow to provide arrays or sets for those methods. // so it's necessary to make sure appendToList receives Arrays, add & removeFromSet receive Sets if (['removeFromSet', 'add'].includes(operator.action) && Array.isArray(values[0])) { values[0] = new Set(values[0]); } else if (['appendToList'].includes(operator.action) && isSet(values[0])) { values[0] = tslib_1.__spread(values[0]); } // special case: [same as in buildDefaultConditionExpression] // we have the metadata for an Array/Set of an Object, // but only get a single item when using list indexes in attributePath // e.g. `attribute('myCollectionProp[0]').set(...)` // (not exclusive to `.set(...)` but all updateActions) if (/\[\d+\]$/.test(attributePath)) { attribute = toDbOne(values[0], getPropertyPath(propertyMetadata, attributePath), alterCollectionPropertyMetadataForSingleItem(propertyMetadata)); } else { attribute = toDbOne(values[0], propertyMetadata); } if (attribute) { attributeValues[valuePlaceholder] = attribute; } } // see update-expression-definition-chain.ts for action definitions var statement; switch (operator.action) { case 'incrementBy': validateAttributeType(operator.action, attribute, 'N'); statement = namePlaceholder + " = " + namePlaceholder + " + " + valuePlaceholder; break; case 'decrementBy': validateAttributeType(operator.action, attribute, 'N'); statement = namePlaceholder + " = " + namePlaceholder + " - " + valuePlaceholder; break; case 'set': if (values.length > 1 && !!values[values.length - 1] === true) { statement = namePlaceholder + " = if_not_exists(" + namePlaceholder + ", " + valuePlaceholder + ")"; } else { statement = namePlaceholder + " = " + valuePlaceholder; } break; case 'appendToList': if (values.length > 1 && values[values.length - 1] === 'START') { statement = namePlaceholder + " = list_append(" + valuePlaceholder + ", " + namePlaceholder + ")"; } else { statement = namePlaceholder + " = list_append(" + namePlaceholder + ", " + valuePlaceholder + ")"; } break; case 'remove': statement = "" + namePlaceholder; break; case 'removeFromListAt': statement = values.map(function (pos) { return namePlaceholder + "[" + pos + "]"; }).join(', '); break; case 'add': validateAttributeType(operator.action, attribute, 'N', 'SS', 'NS', 'BS'); statement = namePlaceholder + " " + valuePlaceholder; break; case 'removeFromSet': validateAttributeType(operator.action, attribute, 'SS', 'NS', 'BS'); statement = namePlaceholder + " " + valuePlaceholder; break; default: throw new Error("no implementation for action " + operator.action); } return { type: operator.actionKeyword, statement: statement, attributeNames: attributeNames, attributeValues: attributeValues, }; } /** * @hidden */ function isNoAttributeValueAction(action) { return (action === 'remove' || // special cases: values are used in statement instead of expressionValues action === 'removeFromListAt'); } /** * @hidden */ export function validateAttributeType(name, attribute) { var allowedTypes = []; for (var _i = 2; _i < arguments.length; _i++) { allowedTypes[_i - 2] = arguments[_i]; } if (attribute === null || attribute === undefined) { throw new Error(name + " requires an attributeValue of " + allowedTypes.join(', ') + " but non was given"); } var key = Object.keys(attribute)[0]; if (!allowedTypes.includes(key)) { throw new Error("Type " + key + " of " + JSON.stringify(attribute) + " is not allowed for " + name + ". Valid types are: " + allowedTypes.join('. ')); } } //# sourceMappingURL=update-expression-builder.js.map