diff --git a/packages/nodes-base/nodes/Slack/Slack.node.ts b/packages/nodes-base/nodes/Slack/Slack.node.ts index 16ef542f3b..0eac92e536 100644 --- a/packages/nodes-base/nodes/Slack/Slack.node.ts +++ b/packages/nodes-base/nodes/Slack/Slack.node.ts @@ -14,13 +14,14 @@ export class Slack extends VersionedNodeType { group: ['output'], subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Consume Slack API', - defaultVersion: 2.1, + defaultVersion: 2.2, }; const nodeVersions: IVersionedNodeType['nodeVersions'] = { 1: new SlackV1(baseDescription), 2: new SlackV2(baseDescription), 2.1: new SlackV2(baseDescription), + 2.2: new SlackV2(baseDescription), }; super(nodeVersions, baseDescription); diff --git a/packages/nodes-base/nodes/Slack/V2/FileDescription.ts b/packages/nodes-base/nodes/Slack/V2/FileDescription.ts index aecf8e2bfb..07d514ff49 100644 --- a/packages/nodes-base/nodes/Slack/V2/FileDescription.ts +++ b/packages/nodes-base/nodes/Slack/V2/FileDescription.ts @@ -47,6 +47,7 @@ export const fileFields: INodeProperties[] = [ show: { operation: ['upload'], resource: ['file'], + '@version': [2, 2.1], }, }, description: 'Whether the data to upload should be taken from binary field', @@ -61,6 +62,7 @@ export const fileFields: INodeProperties[] = [ operation: ['upload'], resource: ['file'], binaryData: [false], + '@version': [2, 2.1], }, }, placeholder: '', @@ -76,6 +78,23 @@ export const fileFields: INodeProperties[] = [ operation: ['upload'], resource: ['file'], binaryData: [true], + '@version': [2, 2.1], + }, + }, + placeholder: '', + description: 'Name of the binary property which contains the data for the file to be uploaded', + }, + { + displayName: 'File Property', + name: 'binaryPropertyName', + type: 'string', + default: 'data', + required: true, + displayOptions: { + show: { + operation: ['upload'], + resource: ['file'], + '@version': [2.2], }, }, placeholder: '', @@ -102,10 +121,31 @@ export const fileFields: INodeProperties[] = [ typeOptions: { loadOptionsMethod: 'getChannels', }, + displayOptions: { + show: { + '@version': [2, 2.1], + }, + }, default: [], description: 'The channels to send the file to. Choose from the list, or specify IDs using an expression.', }, + { + displayName: 'Channel Name or ID', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + displayOptions: { + show: { + '@version': [2.2], + }, + }, + default: [], + description: + 'The channel to send the file to. Choose from the list, or specify an ID using an expression.', + }, { displayName: 'File Name', name: 'fileName', diff --git a/packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts b/packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts index 7df2ff5d11..2554565239 100644 --- a/packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts @@ -29,7 +29,7 @@ export async function slackApiRequest( }, body, qs: query, - uri: `https://slack.com/api${resource}`, + uri: resource.startsWith('https') ? resource : `https://slack.com/api${resource}`, json: true, }; options = Object.assign({}, options, option); @@ -78,6 +78,7 @@ export async function slackApiRequest( }, ); } + throw new NodeOperationError( this.getNode(), 'Slack error response: ' + JSON.stringify(response.error), diff --git a/packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts b/packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts index 3e9d4133bc..df0ac7b999 100644 --- a/packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts +++ b/packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts @@ -38,7 +38,7 @@ export class SlackV2 implements INodeType { constructor(baseDescription: INodeTypeBaseDescription) { this.description = { ...baseDescription, - version: [2, 2.1], + version: [2, 2.1, 2.2], defaults: { name: 'Slack', }, @@ -1040,11 +1040,13 @@ export class SlackV2 implements INodeType { if (operation === 'upload') { const options = this.getNodeParameter('options', i); const body: IDataObject = {}; + const fileBody: IDataObject = {}; + if (options.channelIds) { body.channels = (options.channelIds as string[]).join(','); } - if (options.fileName) { - body.filename = options.fileName as string; + if (options.channelId) { + body.channel_id = options.channelId as string; } if (options.initialComment) { body.initial_comment = options.initialComment as string; @@ -1053,35 +1055,87 @@ export class SlackV2 implements INodeType { body.thread_ts = options.threadTs as string; } if (options.title) { - body.title = options.title as string; + if (nodeVersion <= 2.1) { + body.title = options.title as string; + } } - if (this.getNodeParameter('binaryData', i)) { + + if (this.getNodeParameter('binaryData', i, false) || nodeVersion > 2.1) { const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i); const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName); + let fileSize: number; let uploadData: Buffer | Readable; if (binaryData.id) { uploadData = await this.helpers.getBinaryStream(binaryData.id); + const metadata = await this.helpers.getBinaryMetadata(binaryData.id); + fileSize = metadata.fileSize; } else { uploadData = Buffer.from(binaryData.data, BINARY_ENCODING); + fileSize = uploadData.length; + } + + if (nodeVersion <= 2.1) { + body.file = { + value: uploadData, + options: { + filename: binaryData.fileName, + contentType: binaryData.mimeType, + }, + }; + + responseData = await slackApiRequest.call( + this, + 'POST', + '/files.upload', + {}, + qs, + { 'Content-Type': 'multipart/form-data' }, + { formData: body }, + ); + responseData = responseData.file; + } else { + fileBody.file = { + value: uploadData, + options: { + filename: binaryData.fileName, + contentType: binaryData.mimeType, + }, + }; + + const uploadUrl = await slackApiRequest.call( + this, + 'GET', + '/files.getUploadURLExternal', + {}, + { + filename: options.fileName ? options.fileName : binaryData.fileName, + length: fileSize, + }, + ); + await slackApiRequest.call( + this, + 'POST', + uploadUrl.upload_url, + {}, + qs, + { 'Content-Type': 'multipart/form-data' }, + { formData: fileBody }, + ); + body.files = [ + { + id: uploadUrl.file_id, + title: options.title ? options.title : binaryData.fileName, + }, + ]; + responseData = await slackApiRequest.call( + this, + 'POST', + '/files.completeUploadExternal', + body, + ); + responseData = responseData.files; } - body.file = { - value: uploadData, - options: { - filename: binaryData.fileName, - contentType: binaryData.mimeType, - }, - }; - responseData = await slackApiRequest.call( - this, - 'POST', - '/files.upload', - {}, - qs, - { 'Content-Type': 'multipart/form-data' }, - { formData: body }, - ); - responseData = responseData.file; } else { const fileContent = this.getNodeParameter('fileContent', i) as string; body.content = fileContent;