Skip to content

Commit 0e369c0

Browse files
feat(web-api): include a blocks argument for file uploads (#2261)
Co-authored-by: William Bergamin <[email protected]>
1 parent ce6c9f6 commit 0e369c0

4 files changed

Lines changed: 86 additions & 9 deletions

File tree

docs/content/packages/web-api.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,9 @@ of binary data follow soon!
501501
The `channel_id` and `initial_comment` aren't required, but the file either won't be shared to a channel or it won't be
502502
posted with a message if these aren't included.
503503

504+
A posted message can be formatted with [Block Kit](https://docs.slack.dev/block-kit) using the `blocks` argument instead
505+
of the `initial_comment` text.
506+
504507
In a successful response, the `result.files` contains an array of [shared files](https://api.slack.com/types/file).
505508
These files are "private" and available to just the `token` holder if no `channel_id` is included in the request, and
506509
are marked "public" when shared to a provided `channel_id`.

packages/web-api/src/WebClient.spec.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,58 @@ describe('WebClient', () => {
12281228
uploader.done();
12291229
});
12301230

1231+
it('uploads content', async () => {
1232+
const scope = nock('https://slack.com')
1233+
.post('/api/files.getUploadURLExternal', { filename: 'content.txt', length: 42 })
1234+
.reply(200, {
1235+
ok: true,
1236+
file_id: 'F0101010101',
1237+
upload_url: 'https://files.slack.com/upload/v1/abcdefghijklmnopqrstuvwxyz',
1238+
})
1239+
.post('/api/files.completeUploadExternal', {
1240+
blocks: '[{"type":"section","text":{"type":"plain_text","text":"Hello"}}]',
1241+
channel_id: 'C010101010',
1242+
files: '[{"id":"F0101010101","title":"content.txt"}]',
1243+
})
1244+
.reply(200, {
1245+
ok: true,
1246+
files: [
1247+
{
1248+
id: 'F0101010101',
1249+
name: 'content.txt',
1250+
permalink: 'https://my-workspace.slack.com/files/U0123456789/F0101010101/content.txt',
1251+
},
1252+
],
1253+
});
1254+
const uploader = nock('https://files.slack.com').post('/upload/v1/abcdefghijklmnopqrstuvwxyz').reply(200);
1255+
const client = new WebClient(token);
1256+
const response = await client.filesUploadV2({
1257+
blocks: [{ type: 'section', text: { type: 'plain_text', text: 'Hello' } }],
1258+
channel_id: 'C010101010',
1259+
content: 'Words from another variable might go here!',
1260+
filename: 'content.txt',
1261+
});
1262+
const expected = {
1263+
ok: true,
1264+
files: [
1265+
{
1266+
ok: true,
1267+
files: [
1268+
{
1269+
id: 'F0101010101',
1270+
name: 'content.txt',
1271+
permalink: 'https://my-workspace.slack.com/files/U0123456789/F0101010101/content.txt',
1272+
},
1273+
],
1274+
response_metadata: {},
1275+
},
1276+
],
1277+
};
1278+
assert.deepEqual(response, expected);
1279+
scope.done();
1280+
uploader.done();
1281+
});
1282+
12311283
it('uploads multiple files', async () => {
12321284
const scope = nock('https://slack.com')
12331285
.post('/api/files.getUploadURLExternal', { filename: 'test-png.png', length: 55292 })

packages/web-api/src/file-upload.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export async function getFileUploadJob(
2727
const fileUploadJob: Record<string, unknown> = {
2828
// supplied by user
2929
alt_text: options.alt_text,
30+
blocks: options.blocks,
3031
channel_id: options.channels ?? options.channel_id,
3132
filename: options.filename ?? fileName,
3233
initial_comment: options.initial_comment,
@@ -96,8 +97,8 @@ export async function getMultipleFileUploadJobs(
9697
// ensure no omitted properties included in files_upload entry
9798
// these properties are valid only at the top-level, not
9899
// inside file_uploads.
99-
const { channel_id, channels, initial_comment, thread_ts } = upload as FileUploadV2;
100-
if (channel_id || channels || initial_comment || thread_ts) {
100+
const { blocks, channel_id, channels, initial_comment, thread_ts } = upload as FileUploadV2;
101+
if (blocks || channel_id || channels || initial_comment || thread_ts) {
101102
throw errorWithCode(
102103
new Error(buildInvalidFilesUploadParamError()),
103104
ErrorCode.FileUploadInvalidArgumentsError,
@@ -107,6 +108,7 @@ export async function getMultipleFileUploadJobs(
107108
// supplied at the top level.
108109
const uploadJobArgs: Record<string, unknown> = {
109110
...upload,
111+
blocks: options.blocks,
110112
channels: options.channels,
111113
channel_id: options.channel_id,
112114
initial_comment: options.initial_comment,
@@ -220,7 +222,7 @@ export async function getFileDataAsStream(readable: Readable): Promise<Buffer> {
220222

221223
/**
222224
* Filters through all fileUploads and groups them into jobs for completion
223-
* based on combination of channel_id, thread_ts, initial_comment.
225+
* based on combination of channel_id, thread_ts, initial_comment, blocks.
224226
* {@link https://api.slack.com/methods/files.completeUploadExternal files.completeUploadExternal} allows for multiple
225227
* files to be uploaded with a message (`initial_comment`), and as a threaded message (`thread_ts`)
226228
* In order to be grouped together, file uploads must have like properties.
@@ -232,13 +234,14 @@ export function getAllFileUploadsToComplete(
232234
): Record<string, FilesCompleteUploadExternalArguments> {
233235
const toComplete: Record<string, FilesCompleteUploadExternalArguments> = {};
234236
for (const upload of fileUploads) {
235-
const { channel_id, thread_ts, initial_comment, file_id, title } = upload;
237+
const { blocks, channel_id, thread_ts, initial_comment, file_id, title } = upload;
236238
if (file_id) {
237-
const compareString = `:::${channel_id}:::${thread_ts}:::${initial_comment}`;
239+
const compareString = `:::${channel_id}:::${thread_ts}:::${initial_comment}:::${JSON.stringify(blocks)}`;
238240
if (!Object.prototype.hasOwnProperty.call(toComplete, compareString)) {
239241
toComplete[compareString] = {
240242
files: [{ id: file_id, title }],
241243
channel_id,
244+
blocks,
242245
initial_comment,
243246
};
244247
if (thread_ts && channel_id) {
@@ -425,7 +428,7 @@ export function buildMultipleChannelsErrorMsg(): string {
425428

426429
export function buildInvalidFilesUploadParamError(): string {
427430
return (
428-
'You may supply file_uploads only for a single channel, comment, thread respectively. ' +
429-
'Therefore, please supply any channel_id, initial_comment, thread_ts in the top-layer.'
431+
'You may supply file_uploads only for a single channel, message, or thread respectively. ' +
432+
'Therefore, please supply any channel_id, initial_comment or blocks, or thread_ts in the top-layer.'
430433
);
431434
}

packages/web-api/src/types/request/files.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Stream } from 'node:stream';
2+
import type { Block, KnownBlock } from '@slack/types';
23
import type { ExcludeFromUnion } from '../helpers';
34
import type { FilesGetUploadURLExternalResponse } from '../response/index';
45
import type {
@@ -64,10 +65,19 @@ type FileDestinationArgumentChannels = FileChannelDestinationArgumentChannels |
6465
// https://api.slack.com/methods/files.completeUploadExternal
6566
export type FilesCompleteUploadExternalArguments = FileDestinationArgument &
6667
TokenOverridable & {
67-
/** @description Array of file IDs and their corresponding (optional) titles. */
68+
/**
69+
* @description Array of file IDs and their corresponding (optional) titles.
70+
* @example [{"id":"F044GKUHN9Z", "title":"slack-test"}]
71+
**/
6872
files: [FileUploadComplete, ...FileUploadComplete[]];
6973
/** @description The message text introducing the file in the specified channel. */
7074
initial_comment?: string;
75+
/**
76+
* @description An array of structured rich text blocks. If the `initial_comment` field is provided, the `blocks` field is ignored.
77+
* @example [{"type": "section", "text": {"type": "plain_text", "text": "Hello world"}}]
78+
* @see {@link https://api.slack.com/reference/block-kit/blocks}
79+
*/
80+
blocks?: (KnownBlock | Block)[];
7181
};
7282

7383
// https://api.slack.com/methods/files.delete
@@ -148,6 +158,12 @@ export type FilesUploadArguments = FileUpload & TokenOverridable;
148158
export type FileUploadV2 = FileUpload & {
149159
/** @description Description of image for screen-reader. */
150160
alt_text?: string;
161+
/**
162+
* @description An array of structured rich text blocks. If the `initial_comment` field is provided, the `blocks` field is ignored.
163+
* @example [{"type": "section", "text": {"type": "plain_text", "text": "Hello world"}}]
164+
* @see {@link https://api.slack.com/reference/block-kit/blocks}
165+
*/
166+
blocks?: (KnownBlock | Block)[];
151167
/** @description Channel ID where the file will be shared. If not specified the file will be private. */
152168
channel_id?: string;
153169
/** @deprecated use channel_id instead */
@@ -157,7 +173,10 @@ export type FileUploadV2 = FileUpload & {
157173
};
158174

159175
export interface FilesUploadV2ArgumentsMultipleFiles {
160-
file_uploads: ExcludeFromUnion<FileUploadV2, 'channel_id' | 'channels' | 'initial_comment' | 'thread_ts'>[];
176+
file_uploads: ExcludeFromUnion<
177+
FileUploadV2,
178+
'blocks' | 'channel_id' | 'channels' | 'initial_comment' | 'thread_ts'
179+
>[];
161180
}
162181

163182
// https://tools.slack.dev/node-slack-sdk/web-api#upload-a-file

0 commit comments

Comments
 (0)