From 4b06720c8b7dd8b8d435690f7b594340a59f9f8a Mon Sep 17 00:00:00 2001 From: Bernhard Wittmann Date: Fri, 10 Apr 2026 08:29:36 +0200 Subject: [PATCH] fix(AWS DynamoDB Node): Add option to disable auto-parsing of numeric strings (#28093) Co-authored-by: umut-polat <52835619+umut-polat@users.noreply.github.com> Co-authored-by: RomanDavydchuk --- .../nodes/Aws/DynamoDB/AwsDynamoDB.node.ts | 5 ++- .../nodes/Aws/DynamoDB/ItemDescription.ts | 13 ++++++ .../nodes/Aws/DynamoDB/__test__/utils.test.ts | 40 +++++++++++++++++++ .../nodes-base/nodes/Aws/DynamoDB/utils.ts | 8 ++-- 4 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 packages/nodes-base/nodes/Aws/DynamoDB/__test__/utils.test.ts diff --git a/packages/nodes-base/nodes/Aws/DynamoDB/AwsDynamoDB.node.ts b/packages/nodes-base/nodes/Aws/DynamoDB/AwsDynamoDB.node.ts index cd9ed0f4622..13cdb0cbe2e 100644 --- a/packages/nodes-base/nodes/Aws/DynamoDB/AwsDynamoDB.node.ts +++ b/packages/nodes-base/nodes/Aws/DynamoDB/AwsDynamoDB.node.ts @@ -138,6 +138,7 @@ export class AwsDynamoDB implements INodeType { const dataToSend = this.getNodeParameter('dataToSend', 0) as | 'defineBelow' | 'autoMapInputData'; + const autoParseNumbers = this.getNodeParameter('autoParseNumbers', i, true) as boolean; const item: { [key: string]: string } = {}; if (dataToSend === 'autoMapInputData') { @@ -150,11 +151,11 @@ export class AwsDynamoDB implements INodeType { item[key] = items[i].json[key] as string; } - body.Item = adjustPutItem(item as PutItemUi); + body.Item = adjustPutItem(item as PutItemUi, autoParseNumbers); } else { const fields = this.getNodeParameter('fieldsUi.fieldValues', i, []) as FieldsUiValues; fields.forEach(({ fieldId, fieldValue }) => (item[fieldId] = fieldValue)); - body.Item = adjustPutItem(item as PutItemUi); + body.Item = adjustPutItem(item as PutItemUi, autoParseNumbers); } const headers = { diff --git a/packages/nodes-base/nodes/Aws/DynamoDB/ItemDescription.ts b/packages/nodes-base/nodes/Aws/DynamoDB/ItemDescription.ts index 4e2a9729bed..d2bd7dbf1f0 100644 --- a/packages/nodes-base/nodes/Aws/DynamoDB/ItemDescription.ts +++ b/packages/nodes-base/nodes/Aws/DynamoDB/ItemDescription.ts @@ -90,6 +90,19 @@ export const itemFields: INodeProperties[] = [ default: 'defineBelow', description: 'Whether to insert the input data this node receives in the new row', }, + { + displayName: 'Automatically Parse Numbers', + name: 'autoParseNumbers', + type: 'boolean', + displayOptions: { + show: { + operation: ['upsert'], + }, + }, + default: true, + description: + 'Whether to convert number-looking string values to DynamoDB Number (N). Disable to keep strings as String (S).', + }, { displayName: 'Inputs to Ignore', name: 'inputsToIgnore', diff --git a/packages/nodes-base/nodes/Aws/DynamoDB/__test__/utils.test.ts b/packages/nodes-base/nodes/Aws/DynamoDB/__test__/utils.test.ts new file mode 100644 index 00000000000..5072d079f3c --- /dev/null +++ b/packages/nodes-base/nodes/Aws/DynamoDB/__test__/utils.test.ts @@ -0,0 +1,40 @@ +import type { PutItemUi } from '../types'; +import { adjustPutItem } from '../utils'; + +describe('adjustPutItem', () => { + it('maps actual numbers to N', () => { + expect(adjustPutItem({ count: 42 } as unknown as PutItemUi)).toEqual({ + count: { N: '42' }, + }); + }); + + it('parses numeric strings as N when autoParseNumbers is enabled', () => { + expect( + adjustPutItem({ id: '34', executionId: '1234567890' } as unknown as PutItemUi, true), + ).toEqual({ + id: { N: '34' }, + executionId: { N: '1234567890' }, + }); + }); + + it('keeps numeric strings as S when autoParseNumbers is disabled', () => { + expect( + adjustPutItem({ id: '34', executionId: '1234567890' } as unknown as PutItemUi, false), + ).toEqual({ + id: { S: '34' }, + executionId: { S: '1234567890' }, + }); + }); + + it('maps booleans to BOOL', () => { + expect(adjustPutItem({ active: true } as unknown as PutItemUi)).toEqual({ + active: { BOOL: 'true' }, + }); + }); + + it('maps non-numeric strings to S', () => { + expect(adjustPutItem({ name: 'hello' } as unknown as PutItemUi)).toEqual({ + name: { S: 'hello' }, + }); + }); +}); diff --git a/packages/nodes-base/nodes/Aws/DynamoDB/utils.ts b/packages/nodes-base/nodes/Aws/DynamoDB/utils.ts index ee5605c83e3..d7b37ece5a1 100644 --- a/packages/nodes-base/nodes/Aws/DynamoDB/utils.ts +++ b/packages/nodes-base/nodes/Aws/DynamoDB/utils.ts @@ -37,7 +37,7 @@ export function adjustExpressionAttributeName(eanUi: IAttributeNameUi[]) { return ean; } -export function adjustPutItem(putItemUi: PutItemUi) { +export function adjustPutItem(putItemUi: PutItemUi, autoParseNumbers = true) { const adjustedPutItem: AdjustedPutItem = {}; Object.entries(putItemUi).forEach(([attribute, value]) => { @@ -47,10 +47,10 @@ export function adjustPutItem(putItemUi: PutItemUi) { type = 'BOOL'; } else if (typeof value === 'object' && !Array.isArray(value) && value !== null) { type = 'M'; - } else if (isNaN(Number(value))) { - type = 'S'; - } else { + } else if (autoParseNumbers ? !isNaN(Number(value)) : typeof value === 'number') { type = 'N'; + } else { + type = 'S'; } adjustedPutItem[attribute] = { [type]: value.toString() };