module import { Metadata } from '../../decorator/metadata/metadata' ; import { alterCollectionPropertyMetadataForSingleItem , PropertyMetadata , } from '../../decorator/metadata/property-metadata.model' ; import { toDbOne } from '../../mapper/mapper' ; import { AttributeType } from '../../mapper/type/attribute-type.type' ; import { Attribute , Attributes } from '../../mapper/type/attribute.type' ; 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' ; import { UpdateActionDef } from './type/update-action-def' ; import { UpdateAction } from './type/update-action.type' ; import { UpdateExpression } from './type/update-expression.type' ; param { string } attributePath param { ConditionOperator } operation param { any [ ] } values param { string [ ] } existingValueNames param { Metadata < any > } metadata returns { Expression } hidden export function buildUpdateExpression ( attributePath , operation , values , existingValueNames , metadata ) { values = deepFilter ( values , ( value ) => value !== undefined ) || [ ] ; let propertyMetadata ; if ( metadata ) { propertyMetadata = metadata . forProperty ( attributePath ) ; } const resolvedAttributeNames = resolveAttributeNames ( attributePath , metadata ) ; const valuePlaceholder = uniqueAttributeValueName ( attributePath , existingValueNames ) ; return buildDefaultExpression ( attributePath , resolvedAttributeNames . placeholder , valuePlaceholder , resolvedAttributeNames . attributeNames , values , existingValueNames , propertyMetadata , operation ) ; } hidden function buildDefaultExpression ( attributePath , namePlaceholder , valuePlaceholder , attributeNames , values , existingValueNames , propertyMetadata , operator ) { const attributeValues = { } ; let attribute = null ; if ( ! isNoAttributeValueAction ( operator . action ) ) { 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 ] = [ ... values [ 0 ] ] ; } if ( /\[\d+\]$/ . test ( attributePath ) ) { attribute = toDbOne ( values [ 0 ] , getPropertyPath ( propertyMetadata , attributePath ) , alterCollectionPropertyMetadataForSingleItem ( propertyMetadata ) ) ; } else { attribute = toDbOne ( values [ 0 ] , propertyMetadata ) ; } if ( attribute ) { attributeValues [ valuePlaceholder ] = attribute ; } } let 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 ( ( pos ) => `${ 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 , attributeNames , attributeValues , } ; } hidden function isNoAttributeValueAction ( action ) { return ( action === 'remove' || action === 'removeFromListAt' ) ; } hidden export function validateAttributeType ( name , attribute , ... allowedTypes ) { if ( attribute === null || attribute === undefined ) { throw new Error ( `${ name } requires an attributeValue of ${ allowedTypes . join ( ', ' ) } but non was given` ) ; } const key = < AttributeType > 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 ( '. ' ) }` ) ; } }