Kaynağa Gözat

@uppy/xhr-upload: bring back getResponseData (#5354)

Merlijn Vos 9 ay önce
ebeveyn
işleme
7b92d9d2cf

+ 10 - 0
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?

+ 16 - 7
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<any, { url: string }>()
     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',
     })
   })
 

+ 11 - 1
packages/@uppy/xhr-upload/src/index.ts

@@ -55,6 +55,7 @@ export interface XhrUploadOpts<M extends Meta, B extends Body>
   onBeforeRequest?: FetcherOptions['onBeforeRequest']
   shouldRetry?: FetcherOptions['shouldRetry']
   onAfterResponse?: FetcherOptions['onAfterResponse']
+  getResponseData?: (xhr: XMLHttpRequest) => B | Promise<B>
   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) {