From dd2ed90b954b00897c7883da9b4dea1d0aa6043a Mon Sep 17 00:00:00 2001 From: RomanDavydchuk Date: Tue, 17 Jun 2025 12:49:10 +0300 Subject: [PATCH] fix(AWS DynamoDB Node): Incorrect parameter names (#16408) --- .../nodes/Aws/DynamoDB/AwsDynamoDB.node.ts | 10 +- .../nodes/Aws/DynamoDB/ItemDescription.ts | 2 +- .../__test__/node/AwsDynamoDB.node.test.ts | 82 ++++++ .../Aws/DynamoDB/__test__/node/workflow.json | 261 ++++++++++++++++++ 4 files changed, 349 insertions(+), 6 deletions(-) create mode 100644 packages/nodes-base/nodes/Aws/DynamoDB/__test__/node/AwsDynamoDB.node.test.ts create mode 100644 packages/nodes-base/nodes/Aws/DynamoDB/__test__/node/workflow.json diff --git a/packages/nodes-base/nodes/Aws/DynamoDB/AwsDynamoDB.node.ts b/packages/nodes-base/nodes/Aws/DynamoDB/AwsDynamoDB.node.ts index c3ff132061..d797c73088 100644 --- a/packages/nodes-base/nodes/Aws/DynamoDB/AwsDynamoDB.node.ts +++ b/packages/nodes-base/nodes/Aws/DynamoDB/AwsDynamoDB.node.ts @@ -107,7 +107,7 @@ export class AwsDynamoDB implements INodeType { [], ) as IAttributeValueUi[]; const conditionExpession = this.getNodeParameter( - 'conditionExpression', + 'additionalFields.conditionExpression', i, '', ) as string; @@ -130,7 +130,7 @@ export class AwsDynamoDB implements INodeType { const expressionAttributeName = adjustExpressionAttributeName(eanUi); if (Object.keys(expressionAttributeName).length) { - body.expressionAttributeNames = expressionAttributeName; + body.ExpressionAttributeNames = expressionAttributeName; } if (conditionExpession) { @@ -180,7 +180,7 @@ export class AwsDynamoDB implements INodeType { }; const eavUi = this.getNodeParameter( - 'additionalFields.eavUi.eavValues', + 'additionalFields.expressionAttributeUi.expressionAttributeValues', i, [], ) as IAttributeValueUi[]; @@ -213,7 +213,7 @@ export class AwsDynamoDB implements INodeType { const expressionAttributeName = adjustExpressionAttributeName(eanUi); if (Object.keys(expressionAttributeName).length) { - body.expressionAttributeNames = expressionAttributeName; + body.ExpressionAttributeNames = expressionAttributeName; } const headers = { @@ -260,7 +260,7 @@ export class AwsDynamoDB implements INodeType { const expressionAttributeName = adjustExpressionAttributeName(eanUi); if (Object.keys(expressionAttributeName).length) { - body.expressionAttributeNames = expressionAttributeName; + body.ExpressionAttributeNames = expressionAttributeName; } if (additionalFields.readType) { diff --git a/packages/nodes-base/nodes/Aws/DynamoDB/ItemDescription.ts b/packages/nodes-base/nodes/Aws/DynamoDB/ItemDescription.ts index 8a36e8c11a..4e2a9729be 100644 --- a/packages/nodes-base/nodes/Aws/DynamoDB/ItemDescription.ts +++ b/packages/nodes-base/nodes/Aws/DynamoDB/ItemDescription.ts @@ -418,7 +418,7 @@ export const itemFields: INodeProperties[] = [ options: [ { name: 'expressionAttributeValues', - displayName: 'Expression Attribute Vaue', + displayName: 'Expression Attribute Value', values: [ { displayName: 'Attribute', diff --git a/packages/nodes-base/nodes/Aws/DynamoDB/__test__/node/AwsDynamoDB.node.test.ts b/packages/nodes-base/nodes/Aws/DynamoDB/__test__/node/AwsDynamoDB.node.test.ts new file mode 100644 index 0000000000..e9540bbeb8 --- /dev/null +++ b/packages/nodes-base/nodes/Aws/DynamoDB/__test__/node/AwsDynamoDB.node.test.ts @@ -0,0 +1,82 @@ +import { NodeTestHarness } from '@nodes-testing/node-test-harness'; +import nock from 'nock'; + +import { credentials } from '../../../__tests__/credentials'; + +describe('AWS DynamoDB Node', () => { + const dynamoDbNock = nock('https://dynamodb.eu-central-1.amazonaws.com'); + + beforeAll(() => { + dynamoDbNock + .post('/', { + TableName: 'n8n-testing', + KeyConditionExpression: 'id = :idVal', + ExpressionAttributeValues: { + ':idVal': { S: 'foo' }, + }, + ProjectionExpression: '#time', + ExpressionAttributeNames: { + '#time': 'timestamp', + }, + Limit: 50, + Select: 'SPECIFIC_ATTRIBUTES', + }) + .reply(200, { + Items: [ + { + timestamp: { S: '2025-01-01' }, + }, + ], + }); + + dynamoDbNock + .post( + '/', + (body) => + body?.TableName === 'n8n-testing' && + body?.Key?.id?.S === 'foo' && + body?.Key?.timestamp?.S === '2025-01-01' && + body?.ExpressionAttributeNames?.['#time'] === 'timestamp' && + body?.ProjectionExpression === '#time', + ) + .reply(200, { + Item: { + timestamp: { S: '2025-01-01' }, + }, + }); + + dynamoDbNock + .post( + '/', + (body) => + body?.TableName === 'n8n-testing' && + body?.Item?.id?.S === 'foo' && + body?.Item?.timestamp?.S === '2025-01-01' && + body?.Item?.data?.S === 'payload' && + body?.ConditionExpression === '#d = :v' && + body?.ExpressionAttributeNames?.['#d'] === 'data' && + body?.ExpressionAttributeValues?.[':v']?.S === 'lorem ipsum', + ) + .reply(200, {}); + + dynamoDbNock + .post( + '/', + (body) => + body?.TableName === 'n8n-testing' && + body?.Key?.id?.S === 'foo' && + body?.Key?.timestamp?.S === '2025-01-01' && + body?.ConditionExpression === '#d = :v' && + body?.ExpressionAttributeNames?.['#d'] === 'data' && + body?.ExpressionAttributeValues?.[':v']?.S === 'payload', + ) + .reply(200, {}); + }); + + afterAll(() => dynamoDbNock.done()); + + new NodeTestHarness().setupTests({ + credentials, + workflowFiles: ['workflow.json'], + }); +}); diff --git a/packages/nodes-base/nodes/Aws/DynamoDB/__test__/node/workflow.json b/packages/nodes-base/nodes/Aws/DynamoDB/__test__/node/workflow.json new file mode 100644 index 0000000000..afab5f9ec7 --- /dev/null +++ b/packages/nodes-base/nodes/Aws/DynamoDB/__test__/node/workflow.json @@ -0,0 +1,261 @@ +{ + "name": "AWS DynamoDB", + "nodes": [ + { + "parameters": {}, + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [0, -40], + "id": "efd3f04e-8695-4f2c-8f8d-0b0166390d3f", + "name": "When clicking ‘Execute workflow’" + }, + { + "parameters": { + "operation": "getAll", + "tableName": "n8n-testing", + "keyConditionExpression": "id = :idVal", + "eavUi": { + "eavValues": [ + { + "attribute": ":idVal", + "value": "foo" + } + ] + }, + "select": "SPECIFIC_ATTRIBUTES", + "options": { + "projectionExpression": "#time", + "eanUi": { + "eanValues": [ + { + "key": "#time", + "value": "timestamp" + } + ] + } + } + }, + "type": "n8n-nodes-base.awsDynamoDb", + "typeVersion": 1, + "position": [220, -340], + "id": "fe8a7fbf-83af-45bb-ae58-77f143ce2f30", + "name": "Get many items", + "credentials": { + "aws": { + "id": "UamouRLgIHQY0AXg", + "name": "AWS account" + } + } + }, + { + "parameters": { + "operation": "get", + "tableName": "n8n-testing", + "keysUi": { + "keyValues": [ + { + "key": "id", + "value": "foo" + }, + { + "key": "timestamp", + "value": "2025-01-01" + } + ] + }, + "additionalFields": { + "projectionExpression": "#time", + "eanUi": { + "eanValues": [ + { + "key": "#time", + "value": "timestamp" + } + ] + } + } + }, + "type": "n8n-nodes-base.awsDynamoDb", + "typeVersion": 1, + "position": [220, -140], + "id": "4cbddd91-c851-4ef6-b76a-d84d84f1d598", + "name": "Get an item", + "credentials": { + "aws": { + "id": "UamouRLgIHQY0AXg", + "name": "AWS account" + } + } + }, + { + "parameters": { + "tableName": "n8n-testing", + "fieldsUi": { + "fieldValues": [ + { + "fieldId": "id", + "fieldValue": "foo" + }, + { + "fieldId": "timestamp", + "fieldValue": "2025-01-01" + }, + { + "fieldId": "data", + "fieldValue": "payload" + } + ] + }, + "additionalFields": { + "eavUi": { + "eavValues": [ + { + "attribute": ":v", + "value": "lorem ipsum" + } + ] + }, + "conditionExpression": "#d = :v", + "eanUi": { + "eanValues": [ + { + "key": "#d", + "value": "data" + } + ] + } + } + }, + "type": "n8n-nodes-base.awsDynamoDb", + "typeVersion": 1, + "position": [220, 60], + "id": "17eee2d1-6547-4d99-b168-74d407cc252c", + "name": "Create or update an item", + "credentials": { + "aws": { + "id": "UamouRLgIHQY0AXg", + "name": "AWS account" + } + } + }, + { + "parameters": { + "operation": "delete", + "tableName": "n8n-testing", + "keysUi": { + "keyValues": [ + { + "key": "id", + "value": "foo" + }, + { + "key": "timestamp", + "value": "2025-01-01" + } + ] + }, + "additionalFields": { + "conditionExpression": "#d = :v", + "eanUi": { + "eanValues": [ + { + "key": "#d", + "value": "data" + } + ] + }, + "expressionAttributeUi": { + "expressionAttributeValues": [ + { + "attribute": ":v", + "value": "payload" + } + ] + } + } + }, + "type": "n8n-nodes-base.awsDynamoDb", + "typeVersion": 1, + "position": [220, 260], + "id": "6d266500-16b0-4ee9-a3b2-a4300bc23b1f", + "name": "Delete an item", + "credentials": { + "aws": { + "id": "UamouRLgIHQY0AXg", + "name": "AWS account" + } + } + } + ], + "pinData": { + "Get many items": [ + { + "json": { + "timestamp": "2025-01-01" + } + } + ], + "Get an item": [ + { + "json": { + "timestamp": "2025-01-01" + } + } + ], + "Create or update an item": [ + { + "json": { + "id": "foo", + "timestamp": "2025-01-01", + "data": "payload" + } + } + ], + "Delete an item": [ + { + "json": { + "success": true + } + } + ] + }, + "connections": { + "When clicking ‘Execute workflow’": { + "main": [ + [ + { + "node": "Get many items", + "type": "main", + "index": 0 + }, + { + "node": "Get an item", + "type": "main", + "index": 0 + }, + { + "node": "Create or update an item", + "type": "main", + "index": 0 + }, + { + "node": "Delete an item", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "ada45c19-f09e-43e5-8e5c-d66f54f78d4f", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "e115be144a6a5547dbfca93e774dfffa178aa94a181854c13e2ce5e14d195b2e" + }, + "id": "Ffm7CA1AIhKGiOlK", + "tags": [] +}