@uppy/aws-s3: use default Body generic & export AwsBody (#5353)

This commit is contained in:
Merlijn Vos 2024-07-18 16:27:50 +02:00 committed by GitHub
parent 7b92d9d2cf
commit 1ef39db22c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 61 additions and 37 deletions

View file

@ -169,6 +169,36 @@ const uppy = new Uppy()
});
```
### Use with TypeScript
Uppy always puts the response to an upload in `file.response.body`. If you want
this to be type safe with `@uppy/aws-s3`, you can import the `AwsBody` type and
pass it as the second genric to `Uppy`.
```ts {3,12} showLineNumbers
import Uppy from '@uppy/core';
import Dashboard from '@uppy/dashboard';
import AwsS3, { type AwsBody } from '@uppy/aws-s3';
import '@uppy/core/dist/style.min.css';
import '@uppy/dashboard/dist/style.min.css';
// Set this to `any` or `Record<string, unknown>`
// if you do not set any metadata yourself
type Meta = { license: string };
const uppy = new Uppy<Meta, AwsBody>()
.use(Dashboard, { inline: true, target: 'body' })
.use(AwsS3, { endpoint: '...' });
const id = uppy.addFile(/* ... */);
await uppy.upload();
const body = uppy.getFile(id).response.body!;
const { location } = body; // This is now type safe
```
## API
### Options

View file

@ -1,4 +1,4 @@
import type { Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
import type { Meta, Body, UppyFile } from '@uppy/utils/lib/UppyFile'
import type {
RateLimitedQueue,
WrapPromiseFunctionType,
@ -6,7 +6,7 @@ import type {
import { pausingUploadReason, type Chunk } from './MultipartUploader.ts'
import type AwsS3Multipart from './index.js'
import { throwIfAborted } from './utils.ts'
import type { Body, UploadPartBytesResult, UploadResult } from './utils.js'
import type { UploadPartBytesResult, UploadResult } from './utils.js'
import type { AwsS3MultipartOptions, uploadPartBytes } from './index.js'
function removeMetadataFromURL(urlString: string) {

View file

@ -1,8 +1,7 @@
import type { Uppy } from '@uppy/core'
import { AbortController } from '@uppy/utils/lib/AbortController'
import type { Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
import type { Meta, Body, UppyFile } from '@uppy/utils/lib/UppyFile'
import type { HTTPCommunicationQueue } from './HTTPCommunicationQueue.js'
import type { Body } from './utils.js'
const MB = 1024 * 1024

View file

@ -3,8 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import 'whatwg-fetch'
import nock from 'nock'
import Core from '@uppy/core'
import AwsS3Multipart from './index.ts'
import type { Body } from './utils.js'
import AwsS3Multipart, { type AwsBody } from './index.ts'
const KB = 1024
const MB = KB * KB
@ -13,20 +12,18 @@ describe('AwsS3Multipart', () => {
beforeEach(() => nock.disableNetConnect())
it('Registers AwsS3Multipart upload plugin', () => {
const core = new Core<any, Body>()
core.use(AwsS3Multipart)
const core = new Core().use(AwsS3Multipart)
// @ts-expect-error private property
const pluginNames = core[Symbol.for('uppy test: getPlugins')](
'uploader',
).map((plugin: AwsS3Multipart<any, Body>) => plugin.constructor.name)
).map((plugin: AwsS3Multipart<any, AwsBody>) => plugin.constructor.name)
expect(pluginNames).toContain('AwsS3Multipart')
})
describe('companionUrl assertion', () => {
it('Throws an error for main functions if configured without companionUrl', () => {
const core = new Core<any, Body>()
core.use(AwsS3Multipart)
const core = new Core().use(AwsS3Multipart)
const awsS3Multipart = core.getPlugin('AwsS3Multipart')!
const err = 'Expected a `endpoint` option'
@ -47,8 +44,7 @@ describe('AwsS3Multipart', () => {
describe('non-multipart upload', () => {
it('should handle POST uploads', async () => {
const core = new Core<any, Body>()
core.use(AwsS3Multipart, {
const core = new Core().use(AwsS3Multipart, {
shouldUseMultipart: false,
limit: 0,
getUploadParameters: () => ({
@ -103,11 +99,11 @@ describe('AwsS3Multipart', () => {
})
describe('without companionUrl (custom main functions)', () => {
let core: Core<any, Body>
let awsS3Multipart: AwsS3Multipart<any, Body>
let core: Core<any, AwsBody>
let awsS3Multipart: AwsS3Multipart<any, AwsBody>
beforeEach(() => {
core = new Core<any, Body>()
core = new Core<any, AwsBody>()
core.use(AwsS3Multipart, {
shouldUseMultipart: true,
limit: 0,
@ -254,7 +250,7 @@ describe('AwsS3Multipart', () => {
})
it('retries uploadPartBytes when it fails once', async () => {
const core = new Core<any, Body>().use(AwsS3Multipart, {
const core = new Core().use(AwsS3Multipart, {
shouldUseMultipart: true,
createMultipartUpload,
completeMultipartUpload: vi.fn(async () => ({ location: 'test' })),
@ -287,7 +283,7 @@ describe('AwsS3Multipart', () => {
})
it('calls `upload-error` when uploadPartBytes fails after all retries', async () => {
const core = new Core<any, Body>().use(AwsS3Multipart, {
const core = new Core().use(AwsS3Multipart, {
shouldUseMultipart: true,
retryDelays: [10],
createMultipartUpload,
@ -328,7 +324,7 @@ describe('AwsS3Multipart', () => {
const newToken = 'new token'
beforeEach(() => {
core = new Core<any, Body>()
core = new Core()
core.use(AwsS3Multipart, {
endpoint: '',
headers: {
@ -358,19 +354,17 @@ describe('AwsS3Multipart', () => {
})
describe('dynamic companionHeader using setOption', () => {
let core: Core<any, Body>
let awsS3Multipart: AwsS3Multipart<any, Body>
let core: Core<any, AwsBody>
let awsS3Multipart: AwsS3Multipart<any, AwsBody>
const newToken = 'new token'
it('companionHeader is updated before uploading file', async () => {
core = new Core<any, Body>()
core = new Core()
core.use(AwsS3Multipart)
/* Set up preprocessor */
core.addPreProcessor(() => {
awsS3Multipart = core.getPlugin('AwsS3Multipart') as AwsS3Multipart<
any,
Body
>
awsS3Multipart =
core.getPlugin<AwsS3Multipart<any, AwsBody>>('AwsS3Multipart')!
awsS3Multipart.setOptions({
endpoint: 'http://localhost',
headers: {
@ -391,7 +385,7 @@ describe('AwsS3Multipart', () => {
})
describe('file metadata across custom main functions', () => {
let core: Core<any, Body>
let core: Core
const createMultipartUpload = vi.fn((file) => {
core.setFileMeta(file.id, {
...file.meta,
@ -459,7 +453,7 @@ describe('AwsS3Multipart', () => {
})
it('preserves file metadata if upload is completed', async () => {
core = new Core<any, Body>().use(AwsS3Multipart, {
core = new Core().use(AwsS3Multipart, {
shouldUseMultipart: true,
createMultipartUpload,
signPart,
@ -520,7 +514,7 @@ describe('AwsS3Multipart', () => {
}
})
core = new Core<any, Body>().use(AwsS3Multipart, {
core = new Core().use(AwsS3Multipart, {
shouldUseMultipart: true,
createMultipartUpload,
signPart: signPartWithAbort,
@ -590,7 +584,7 @@ describe('AwsS3Multipart', () => {
}
})
core = new Core<any, Body>().use(AwsS3Multipart, {
core = new Core().use(AwsS3Multipart, {
shouldUseMultipart: true,
createMultipartUpload,
signPart: signPartWithPause,

View file

@ -4,7 +4,7 @@ import BasePlugin, {
} from '@uppy/core/lib/BasePlugin.js'
import { RequestClient } from '@uppy/companion-client'
import type { RequestOptions } from '@uppy/utils/lib/CompanionClientProvider'
import type { Body as _Body, Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
import type { Body, Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
import type { Uppy } from '@uppy/core'
import EventManager from '@uppy/core/lib/EventManager.js'
import { RateLimitedQueue } from '@uppy/utils/lib/RateLimitedQueue'
@ -21,7 +21,6 @@ import type {
UploadResultWithSignal,
MultipartUploadResultWithSignal,
UploadPartBytesResult,
Body,
} from './utils.js'
import createSignedURL from './createSignedURL.ts'
import { HTTPCommunicationQueue } from './HTTPCommunicationQueue.ts'
@ -33,13 +32,13 @@ interface MultipartFile<M extends Meta, B extends Body> extends UppyFile<M, B> {
s3Multipart: UploadResult
}
type PartUploadedCallback<M extends Meta, B extends _Body> = (
type PartUploadedCallback<M extends Meta, B extends Body> = (
file: UppyFile<M, B>,
part: { PartNumber: number; ETag: string },
) => void
declare module '@uppy/core' {
export interface UppyEventMap<M extends Meta, B extends _Body> {
export interface UppyEventMap<M extends Meta, B extends Body> {
's3-multipart:part-uploaded': PartUploadedCallback<M, B>
}
}
@ -289,6 +288,8 @@ const defaultOptions = {
retryDelays: [0, 1000, 3000, 5000],
} satisfies Partial<AwsS3MultipartOptions<any, any>>
export type { AwsBody } from './utils.ts'
export default class AwsS3Multipart<
M extends Meta,
B extends Body,
@ -839,7 +840,7 @@ export default class AwsS3Multipart<
...result,
},
status: 200,
uploadURL: result.location,
uploadURL: result.location as string,
}
this.resetUploaderReferences(file.id)

View file

@ -1,5 +1,5 @@
import { createAbortError } from '@uppy/utils/lib/AbortController'
import type { Body as _Body } from '@uppy/utils/lib/UppyFile'
import type { Body } from '@uppy/utils/lib/UppyFile'
import type { AwsS3Part } from './index.js'
@ -23,6 +23,6 @@ export type UploadPartBytesResult = {
location?: string
}
export interface Body extends _Body {
export interface AwsBody extends Body {
location: string
}