From 7b92d9d2cff76d752bf85a00f65ccfe3779265b6 Mon Sep 17 00:00:00 2001 From: Merlijn Vos Date: Thu, 18 Jul 2024 16:22:29 +0200 Subject: [PATCH] @uppy/xhr-upload: bring back getResponseData (#5354) --- docs/uploader/xhr.mdx | 10 +++++++++ packages/@uppy/xhr-upload/src/index.test.ts | 23 ++++++++++++++------- packages/@uppy/xhr-upload/src/index.ts | 12 ++++++++++- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/docs/uploader/xhr.mdx b/docs/uploader/xhr.mdx index 8eaf5f28e..d65c7e3e4 100644 --- a/docs/uploader/xhr.mdx +++ b/docs/uploader/xhr.mdx @@ -245,6 +245,16 @@ export default { }; ``` +#### `getResponseData` + +An optional function to turn your non-JSON response into JSON with a `url` +property pointing to the uploaded file +(`(xhr: XMLHttpRequest) => { url: string }`). + +You can also return properties other than `url` and they will end up in +`file.response.body`. If you do, make sure to set the `Body` generic on `Uppy` +to make it typesafe when using TS. + ## Frequently Asked Questions ### How can I refresh auth tokens after they expire? diff --git a/packages/@uppy/xhr-upload/src/index.test.ts b/packages/@uppy/xhr-upload/src/index.test.ts index 25156f088..19cb8b605 100644 --- a/packages/@uppy/xhr-upload/src/index.test.ts +++ b/packages/@uppy/xhr-upload/src/index.test.ts @@ -4,7 +4,7 @@ import Core from '@uppy/core' import XHRUpload from './index.ts' describe('XHRUpload', () => { - it('should leverage hooks from fetcher', () => { + it('should leverage hooks from fetcher', async () => { nock('https://fake-endpoint.uppy.io') .defaultReplyHeaders({ 'access-control-allow-method': 'POST', @@ -17,12 +17,16 @@ describe('XHRUpload', () => { .options('/') .reply(204, {}) .post('/') - .reply(200, {}) + .reply(200, 'https://fake-endpoint.uppy.io/random-id') - const core = new Core() + const core = new Core() const shouldRetry = vi.fn(() => true) const onBeforeRequest = vi.fn(() => {}) const onAfterResponse = vi.fn(() => {}) + // Test that we can turn a text response into a JSON response. + const getResponseData = vi.fn((xhr: XMLHttpRequest) => ({ + url: xhr.responseText, + })) core.use(XHRUpload, { id: 'XHRUpload', @@ -30,6 +34,7 @@ describe('XHRUpload', () => { shouldRetry, onBeforeRequest, onAfterResponse, + getResponseData, }) const id = core.addFile({ type: 'image/png', @@ -45,10 +50,14 @@ describe('XHRUpload', () => { endpoint: 'https://fake-endpoint.uppy.io', }, }) - return core.upload().then(() => { - expect(shouldRetry).toHaveBeenCalledTimes(1) - expect(onAfterResponse).toHaveBeenCalledTimes(2) - expect(onBeforeRequest).toHaveBeenCalledTimes(2) + + await core.upload() + + expect(shouldRetry).toHaveBeenCalledTimes(1) + expect(onAfterResponse).toHaveBeenCalledTimes(2) + expect(onBeforeRequest).toHaveBeenCalledTimes(2) + expect(core.getFile(id).response!.body).toEqual({ + url: 'https://fake-endpoint.uppy.io/random-id', }) }) diff --git a/packages/@uppy/xhr-upload/src/index.ts b/packages/@uppy/xhr-upload/src/index.ts index 8bd161eb3..418085d45 100644 --- a/packages/@uppy/xhr-upload/src/index.ts +++ b/packages/@uppy/xhr-upload/src/index.ts @@ -55,6 +55,7 @@ export interface XhrUploadOpts onBeforeRequest?: FetcherOptions['onBeforeRequest'] shouldRetry?: FetcherOptions['shouldRetry'] onAfterResponse?: FetcherOptions['onAfterResponse'] + getResponseData?: (xhr: XMLHttpRequest) => B | Promise allowedMetaFields?: boolean | string[] bundle?: boolean } @@ -218,7 +219,16 @@ export default class XHRUpload< }, }) - const body = JSON.parse(res.responseText) as B + let body = await this.opts.getResponseData?.(res) + try { + body ??= JSON.parse(res.responseText) as B + } catch (cause) { + throw new Error( + '@uppy/xhr-upload expects a JSON response (with a `url` property). To parse non-JSON responses, use `getResponseData` to turn your response into JSON.', + { cause }, + ) + } + const uploadURL = typeof body?.url === 'string' ? body.url : undefined for (const file of files) {