Skip to content

Commit 0903f5f

Browse files
authored
fix(web-api): complete file upload v2 calls if absolute urls are not allowed (#2196)
1 parent d3fe084 commit 0903f5f

File tree

2 files changed

+139
-7
lines changed

2 files changed

+139
-7
lines changed

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

Lines changed: 136 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ describe('WebClient', () => {
919919
await client.apiCall('method', { foo: 'bar' });
920920
assert.fail('expected error to be thrown');
921921
} catch (_err) {
922-
assert(spy.calledOnceWith(0, sinon.match({ url: 'method', body: { foo: 'bar' } })));
922+
assert(spy.calledOnceWith(0, sinon.match({ url: 'https://slack.com/api/method', body: { foo: 'bar' } })));
923923
scope.done();
924924
}
925925
});
@@ -982,7 +982,7 @@ describe('WebClient', () => {
982982
await client.apiCall('method', { foo: 'bar' });
983983
assert.fail('expected error to be thrown');
984984
} catch (_err) {
985-
assert(spy.calledOnceWith(0, sinon.match({ url: 'method', body: { foo: 'bar' } })));
985+
assert(spy.calledOnceWith(0, sinon.match({ url: 'https://slack.com/api/method', body: { foo: 'bar' } })));
986986
scope.done();
987987
}
988988
});
@@ -1156,6 +1156,140 @@ describe('WebClient', () => {
11561156
});
11571157
});
11581158

1159+
describe('filesUploadV2', () => {
1160+
it('uploads a single file', async () => {
1161+
const scope = nock('https://slack.com')
1162+
.post('/api/files.getUploadURLExternal', { filename: 'test-txt.txt', length: 18 })
1163+
.reply(200, {
1164+
ok: true,
1165+
file_id: 'F0123456789',
1166+
upload_url: 'https://files.slack.com/upload/v1/abcdefghijklmnopqrstuvwxyz',
1167+
})
1168+
.post('/api/files.completeUploadExternal', { files: '[{"id":"F0123456789","title":"test-txt.txt"}]' })
1169+
.reply(200, {
1170+
ok: true,
1171+
files: [
1172+
{
1173+
id: 'F0123456789',
1174+
name: 'test-txt.txt',
1175+
permalink: 'https://my-workspace.slack.com/files/U0123456789/F0123456789/test-txt.txt',
1176+
},
1177+
],
1178+
});
1179+
const uploader = nock('https://files.slack.com').post('/upload/v1/abcdefghijklmnopqrstuvwxyz').reply(200);
1180+
const client = new WebClient(token);
1181+
const response = await client.filesUploadV2({
1182+
file: fs.createReadStream('./test/fixtures/test-txt.txt'),
1183+
filename: 'test-txt.txt',
1184+
});
1185+
const expected = {
1186+
ok: true,
1187+
files: [
1188+
{
1189+
ok: true,
1190+
files: [
1191+
{
1192+
id: 'F0123456789',
1193+
name: 'test-txt.txt',
1194+
permalink: 'https://my-workspace.slack.com/files/U0123456789/F0123456789/test-txt.txt',
1195+
},
1196+
],
1197+
response_metadata: {},
1198+
},
1199+
],
1200+
};
1201+
assert.deepEqual(response, expected);
1202+
scope.done();
1203+
uploader.done();
1204+
});
1205+
1206+
it('uploads multiple files', async () => {
1207+
const scope = nock('https://slack.com')
1208+
.post('/api/files.getUploadURLExternal', { filename: 'test-png.png', length: 55292 })
1209+
.reply(200, {
1210+
ok: true,
1211+
file_id: 'F0000000001',
1212+
upload_url: 'https://files.slack.com/upload/v1/zyxwvutsrqponmlkjihgfedcba',
1213+
})
1214+
.post('/api/files.getUploadURLExternal', { filename: 'test-txt.txt', length: 18 })
1215+
.reply(200, {
1216+
ok: true,
1217+
file_id: 'F0123456789',
1218+
upload_url: 'https://files.slack.com/upload/v1/abcdefghijklmnopqrstuvwxyz',
1219+
})
1220+
.post('/api/files.completeUploadExternal', (args) => {
1221+
const { channel_id, thread_ts, initial_comment, files } = args;
1222+
return (
1223+
channel_id === 'C0123456789' &&
1224+
thread_ts === '1223313423434.131321' &&
1225+
initial_comment === 'success!' &&
1226+
(files === '[{"id":"F0123456789","title":"test-txt.txt"},{"id":"F0000000001","title":"test-png.png"}]' ||
1227+
files === '[{"id":"F0000000001","title":"test-png.png"},{"id":"F0123456789","title":"test-txt.txt"}]')
1228+
);
1229+
})
1230+
.reply(200, {
1231+
ok: true,
1232+
files: [
1233+
{
1234+
id: 'F0123456789',
1235+
name: 'test-txt.txt',
1236+
permalink: 'https://my-workspace.slack.com/files/U0123456789/F0123456789/test-txt.txt',
1237+
},
1238+
{
1239+
id: 'F0000000001',
1240+
name: 'test-png.png',
1241+
permalink: 'https://my-workspace.slack.com/files/U0123456789/F0000000001/test-png.png',
1242+
},
1243+
],
1244+
});
1245+
const uploader = nock('https://files.slack.com')
1246+
.post('/upload/v1/abcdefghijklmnopqrstuvwxyz')
1247+
.reply(200)
1248+
.post('/upload/v1/zyxwvutsrqponmlkjihgfedcba')
1249+
.reply(200);
1250+
const client = new WebClient(token, { allowAbsoluteUrls: false });
1251+
const response = await client.filesUploadV2({
1252+
channel_id: 'C0123456789',
1253+
thread_ts: '1223313423434.131321',
1254+
initial_comment: 'success!',
1255+
file_uploads: [
1256+
{
1257+
file: fs.createReadStream('./test/fixtures/test-txt.txt'),
1258+
filename: 'test-txt.txt',
1259+
},
1260+
{
1261+
file: fs.createReadStream('./test/fixtures/test-png.png'),
1262+
filename: 'test-png.png',
1263+
},
1264+
],
1265+
});
1266+
const expected = {
1267+
ok: true,
1268+
files: [
1269+
{
1270+
ok: true,
1271+
files: [
1272+
{
1273+
id: 'F0123456789',
1274+
name: 'test-txt.txt',
1275+
permalink: 'https://my-workspace.slack.com/files/U0123456789/F0123456789/test-txt.txt',
1276+
},
1277+
{
1278+
id: 'F0000000001',
1279+
name: 'test-png.png',
1280+
permalink: 'https://my-workspace.slack.com/files/U0123456789/F0000000001/test-png.png',
1281+
},
1282+
],
1283+
response_metadata: {},
1284+
},
1285+
],
1286+
};
1287+
assert.deepEqual(response, expected);
1288+
scope.done();
1289+
uploader.done();
1290+
});
1291+
});
1292+
11591293
describe('getAllFileUploads', () => {
11601294
const client = new WebClient(token);
11611295
it('adds a single file data to uploads with content supplied', async () => {

packages/web-api/src/WebClient.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,6 @@ export class WebClient extends Methods {
312312
adapter: adapter ? (config: InternalAxiosRequestConfig) => adapter({ ...config, adapter: undefined }) : undefined,
313313
timeout,
314314
baseURL: slackApiUrl,
315-
allowAbsoluteUrls,
316315
headers: isElectron() ? headers : { 'User-Agent': getUserAgent(), ...headers },
317316
httpAgent: agent,
318317
httpsAgent: agent,
@@ -360,8 +359,9 @@ export class WebClient extends Methods {
360359
const headers: Record<string, string> = {};
361360
if (options.token) headers.Authorization = `Bearer ${options.token}`;
362361

362+
const url = this.deriveRequestUrl(method);
363363
const response = await this.makeRequest(
364-
method,
364+
url,
365365
{
366366
team_id: this.teamId,
367367
...options,
@@ -646,8 +646,6 @@ export class WebClient extends Methods {
646646
// TODO: better input types - remove any
647647
const task = () =>
648648
this.requestQueue.add(async () => {
649-
const requestURL = this.deriveRequestUrl(url);
650-
651649
try {
652650
// biome-ignore lint/suspicious/noExplicitAny: TODO: type this
653651
const config: any = {
@@ -664,7 +662,7 @@ export class WebClient extends Methods {
664662
if (url.endsWith('apps.event.authorizations.list')) {
665663
body.token = undefined;
666664
}
667-
this.logger.debug(`http request url: ${requestURL}`);
665+
this.logger.debug(`http request url: ${url}`);
668666
this.logger.debug(`http request body: ${JSON.stringify(redact(body))}`);
669667
// compile all headers - some set by default under the hood by axios - that will be sent along
670668
let allHeaders: Record<string, AxiosHeaderValue | undefined> = Object.keys(

0 commit comments

Comments
 (0)