From 411f93ad7fd3cb1af866efa1882463cdb6109c9b Mon Sep 17 00:00:00 2001 From: Jonathan Bennetts Date: Fri, 15 Nov 2024 09:11:00 +0000 Subject: [PATCH 1/2] feat(Slack Node): Update wait for approval to use markdown --- packages/nodes-base/nodes/Slack/Slack.node.ts | 3 +- .../nodes/Slack/V2/GenericFunctions.ts | 12 +- .../nodes/Slack/V2/MessageInterface.ts | 33 +++++ .../nodes/Slack/test/v2/utils.test.ts | 125 +++++++++++++++++- 4 files changed, 165 insertions(+), 8 deletions(-) diff --git a/packages/nodes-base/nodes/Slack/Slack.node.ts b/packages/nodes-base/nodes/Slack/Slack.node.ts index 0eac92e5366ee..6906b263958d6 100644 --- a/packages/nodes-base/nodes/Slack/Slack.node.ts +++ b/packages/nodes-base/nodes/Slack/Slack.node.ts @@ -14,7 +14,7 @@ export class Slack extends VersionedNodeType { group: ['output'], subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Consume Slack API', - defaultVersion: 2.2, + defaultVersion: 2.3, }; const nodeVersions: IVersionedNodeType['nodeVersions'] = { @@ -22,6 +22,7 @@ export class Slack extends VersionedNodeType { 2: new SlackV2(baseDescription), 2.1: new SlackV2(baseDescription), 2.2: new SlackV2(baseDescription), + 2.3: new SlackV2(baseDescription), }; super(nodeVersions, baseDescription); diff --git a/packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts b/packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts index 590453308cd75..54cf540fd0864 100644 --- a/packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts @@ -1,3 +1,4 @@ +import get from 'lodash/get'; import type { IDataObject, IExecuteFunctions, @@ -7,10 +8,9 @@ import type { IRequestOptions, IWebhookFunctions, } from 'n8n-workflow'; - import { NodeOperationError } from 'n8n-workflow'; -import get from 'lodash/get'; +import type { SendAndWaitMessageBody } from './MessageInterface'; import { getSendAndWaitConfig } from '../../../utils/sendAndWait/utils'; export async function slackApiRequest( @@ -265,7 +265,7 @@ export function createSendAndWaitMessageBody(context: IExecuteFunctions) { const config = getSendAndWaitConfig(context); - const body: IDataObject = { + const body: SendAndWaitMessageBody = { channel: target, blocks: [ { @@ -274,7 +274,7 @@ export function createSendAndWaitMessageBody(context: IExecuteFunctions) { { type: 'section', text: { - type: 'plain_text', + type: context.getNode().typeVersion > 2.2 ? 'mrkdwn' : 'plain_text', text: config.message, emoji: true, }, @@ -307,5 +307,9 @@ export function createSendAndWaitMessageBody(context: IExecuteFunctions) { ], }; + if (context.getNode().typeVersion > 2.2 && body.blocks?.[1]?.type === 'section') { + delete body.blocks[1].text.emoji; + } + return body; } diff --git a/packages/nodes-base/nodes/Slack/V2/MessageInterface.ts b/packages/nodes-base/nodes/Slack/V2/MessageInterface.ts index 69c7b9cb4ba3e..371b03951f904 100644 --- a/packages/nodes-base/nodes/Slack/V2/MessageInterface.ts +++ b/packages/nodes-base/nodes/Slack/V2/MessageInterface.ts @@ -3,3 +3,36 @@ export interface IAttachment { item?: object[]; }; } + +// Used for SendAndWaitMessage +export interface TextBlock { + type: string; + text: string; + emoji?: boolean; +} + +export interface SectionBlock { + type: 'section'; + text: TextBlock; +} + +export interface DividerBlock { + type: 'divider'; +} + +export interface ButtonElement { + type: 'button'; + style?: 'primary'; + text: TextBlock; + url: string; +} + +export interface ActionsBlock { + type: 'actions'; + elements: ButtonElement[]; +} + +export interface SendAndWaitMessageBody { + channel: string; + blocks: Array; +} diff --git a/packages/nodes-base/nodes/Slack/test/v2/utils.test.ts b/packages/nodes-base/nodes/Slack/test/v2/utils.test.ts index 634ee25900351..e56decf79f9b7 100644 --- a/packages/nodes-base/nodes/Slack/test/v2/utils.test.ts +++ b/packages/nodes-base/nodes/Slack/test/v2/utils.test.ts @@ -1,5 +1,6 @@ import { type MockProxy, mock } from 'jest-mock-extended'; import type { IExecuteFunctions } from 'n8n-workflow'; + import { getTarget, createSendAndWaitMessageBody } from '../../V2/GenericFunctions'; describe('Slack Utility Functions', () => { @@ -12,7 +13,7 @@ describe('Slack Utility Functions', () => { }); describe('getTarget', () => { - it('should return corect target id', () => { + it('should return correct target id', () => { mockExecuteFunctions.getNodeParameter.mockImplementation((parameterName: string) => { if (parameterName === 'user') { return 'testUser'; @@ -26,7 +27,7 @@ describe('Slack Utility Functions', () => { }); describe('createSendAndWaitMessageBody', () => { - it('should create message with single button', () => { + it('should create message with single button - pre 2.3 plain_text', () => { mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channel'); mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channelID'); @@ -79,7 +80,7 @@ describe('Slack Utility Functions', () => { }); }); - it('should create message with double buttona', () => { + it('should create message with double buttons - pre 2.3 plain_text', () => { mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channel'); mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channelID'); @@ -142,5 +143,123 @@ describe('Slack Utility Functions', () => { channel: 'channelID', }); }); + + it('should create message with single button - 2.3+ mrkdwn', () => { + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channel'); + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channelID'); + + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('message'); + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('subject'); + mockExecuteFunctions.evaluateExpression.mockReturnValueOnce('localhost'); + mockExecuteFunctions.evaluateExpression.mockReturnValueOnce('node123'); + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({}); + mockExecuteFunctions.getNode.mockReturnValue({ name: 'Slack', typeVersion: 2.3 } as any); + + expect(createSendAndWaitMessageBody(mockExecuteFunctions)).toEqual({ + blocks: [ + { + type: 'divider', + }, + { + text: { + text: 'message', + type: 'mrkdwn', + }, + type: 'section', + }, + { + text: { + text: ' ', + type: 'plain_text', + }, + type: 'section', + }, + { + type: 'divider', + }, + { + elements: [ + { + style: 'primary', + text: { + emoji: true, + text: 'Approve', + type: 'plain_text', + }, + type: 'button', + url: 'localhost/node123?approved=true', + }, + ], + type: 'actions', + }, + ], + channel: 'channelID', + }); + }); + + it('should create message with double buttons - 2.3+ mrkdwn', () => { + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channel'); + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channelID'); + + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('message'); + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('subject'); + mockExecuteFunctions.evaluateExpression.mockReturnValueOnce('localhost'); + mockExecuteFunctions.evaluateExpression.mockReturnValueOnce('node123'); + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({ approvalType: 'double' }); + + mockExecuteFunctions.getNode.mockReturnValue({ name: 'Slack', typeVersion: 2.3 } as any); + + expect(createSendAndWaitMessageBody(mockExecuteFunctions)).toEqual({ + blocks: [ + { + type: 'divider', + }, + { + text: { + text: 'message', + type: 'mrkdwn', + }, + type: 'section', + }, + { + text: { + text: ' ', + type: 'plain_text', + }, + type: 'section', + }, + { + type: 'divider', + }, + { + elements: [ + { + style: undefined, + text: { + emoji: true, + text: 'Disapprove', + type: 'plain_text', + }, + type: 'button', + url: 'localhost/node123?approved=false', + }, + + { + style: 'primary', + text: { + emoji: true, + text: 'Approve', + type: 'plain_text', + }, + type: 'button', + url: 'localhost/node123?approved=true', + }, + ], + type: 'actions', + }, + ], + channel: 'channelID', + }); + }); }); }); From cead427a5c570c6aef7f85a806b78c93437b4403 Mon Sep 17 00:00:00 2001 From: Jonathan Bennetts Date: Fri, 15 Nov 2024 09:16:01 +0000 Subject: [PATCH 2/2] Added missing version --- packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts b/packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts index 3ba8cca7b802e..5277750491b83 100644 --- a/packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts +++ b/packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts @@ -52,7 +52,7 @@ export class SlackV2 implements INodeType { constructor(baseDescription: INodeTypeBaseDescription) { this.description = { ...baseDescription, - version: [2, 2.1, 2.2], + version: [2, 2.1, 2.2, 2.3], defaults: { name: 'Slack', },