Skip to content

Commit

Permalink
feat(Slack Node): Update to use the new API method for file uploads (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Joffcom committed May 8, 2024
1 parent 435272b commit 695e762
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 24 deletions.
3 changes: 2 additions & 1 deletion packages/nodes-base/nodes/Slack/Slack.node.ts
Expand Up @@ -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);
Expand Down
40 changes: 40 additions & 0 deletions packages/nodes-base/nodes/Slack/V2/FileDescription.ts
Expand Up @@ -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',
Expand All @@ -61,6 +62,7 @@ export const fileFields: INodeProperties[] = [
operation: ['upload'],
resource: ['file'],
binaryData: [false],
'@version': [2, 2.1],
},
},
placeholder: '',
Expand All @@ -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: '',
Expand All @@ -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 <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
},
{
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 <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
},
{
displayName: 'File Name',
name: 'fileName',
Expand Down
3 changes: 2 additions & 1 deletion packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts
Expand Up @@ -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);
Expand Down Expand Up @@ -78,6 +78,7 @@ export async function slackApiRequest(
},
);
}

throw new NodeOperationError(
this.getNode(),
'Slack error response: ' + JSON.stringify(response.error),
Expand Down
98 changes: 76 additions & 22 deletions packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts
Expand Up @@ -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',
},
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down

0 comments on commit 695e762

Please sign in to comment.