From 52b93ed5b237e031c4e079ad3e620c0943fb8cda Mon Sep 17 00:00:00 2001 From: yehorkardash Date: Wed, 26 Nov 2025 18:35:14 +0100 Subject: [PATCH] fix(Slack Node): Sort messages manually (#21822) --- packages/nodes-base/nodes/Slack/Slack.node.ts | 3 +- .../nodes-base/nodes/Slack/V2/SlackV2.node.ts | 8 +++- .../nodes/Slack/test/v2/Slack.node.test.ts | 37 +++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/packages/nodes-base/nodes/Slack/Slack.node.ts b/packages/nodes-base/nodes/Slack/Slack.node.ts index fd33d2a9cc0..1c17ce6e3a0 100644 --- a/packages/nodes-base/nodes/Slack/Slack.node.ts +++ b/packages/nodes-base/nodes/Slack/Slack.node.ts @@ -13,7 +13,7 @@ export class Slack extends VersionedNodeType { group: ['output'], subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Consume Slack API', - defaultVersion: 2.3, + defaultVersion: 2.4, }; const nodeVersions: IVersionedNodeType['nodeVersions'] = { @@ -22,6 +22,7 @@ export class Slack extends VersionedNodeType { 2.1: new SlackV2(baseDescription), 2.2: new SlackV2(baseDescription), 2.3: new SlackV2(baseDescription), + 2.4: new SlackV2(baseDescription), }; super(nodeVersions, baseDescription); diff --git a/packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts b/packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts index c73dabde9a4..0a2b4855008 100644 --- a/packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts +++ b/packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts @@ -56,7 +56,7 @@ export class SlackV2 implements INodeType { constructor(baseDescription: INodeTypeBaseDescription) { this.description = { ...baseDescription, - version: [2, 2.1, 2.2, 2.3], + version: [2, 2.1, 2.2, 2.3, 2.4], defaults: { name: 'Slack', }, @@ -574,6 +574,12 @@ export class SlackV2 implements INodeType { ); responseData = responseData.messages; } + + // Slack API "feature" - messages sorting breaks in-between pages when oldest is provided + // Always sort manually in descending order to ensure consistent sorting + if (nodeVersion >= 2.4) { + responseData.sort((a: IDataObject, b: IDataObject) => +(b.ts ?? 0) - +(a.ts ?? 0)); + } } //https://api.slack.com/methods/conversations.invite if (operation === 'invite') { diff --git a/packages/nodes-base/nodes/Slack/test/v2/Slack.node.test.ts b/packages/nodes-base/nodes/Slack/test/v2/Slack.node.test.ts index d8d7f05386e..bf5e8219e7b 100644 --- a/packages/nodes-base/nodes/Slack/test/v2/Slack.node.test.ts +++ b/packages/nodes-base/nodes/Slack/test/v2/Slack.node.test.ts @@ -194,6 +194,43 @@ describe('SlackV2', () => { ]); }); + it('should return channel history sorted by timestamp descending for node version >= 2.4', async () => { + mockExecuteFunctions.getNode.mockReturnValue({ + ...mockNode, + typeVersion: 2.4, + }); + + mockExecuteFunctions.getNodeParameter.mockImplementation((paramName: string) => { + const params: Record = { + resource: 'channel', + operation: 'history', + channelId: 'C123456789', + returnAll: true, + filters: {}, + }; + return params[paramName]; + }); + + // Mock unsorted messages + const mockResponse = [ + { type: 'message', text: 'Message 2', ts: '1234567892.123456' }, + { type: 'message', text: 'Message 4', ts: '1234567894.123456' }, + { type: 'message', text: 'Message 1', ts: '1234567891.123456' }, + { type: 'message', text: 'Message 3', ts: '1234567893.123456' }, + ]; + slackApiRequestAllItemsSpy.mockResolvedValue(mockResponse); + + const result = await node.execute.call(mockExecuteFunctions); + + // Verify messages are sorted by timestamp descending (newest first) + expect(result[0][0].json).toEqual([ + { type: 'message', text: 'Message 4', ts: '1234567894.123456' }, + { type: 'message', text: 'Message 3', ts: '1234567893.123456' }, + { type: 'message', text: 'Message 2', ts: '1234567892.123456' }, + { type: 'message', text: 'Message 1', ts: '1234567891.123456' }, + ]); + }); + it('should get all channel history', async () => { mockExecuteFunctions.getNodeParameter.mockImplementation((paramName: string) => { const params: Record = {