Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce ValidateableFile & move MinimalRequiredUppyFile into utils #4944

Merged
merged 1 commit into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/@uppy/audio/src/Audio.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { h } from 'preact'

import { UIPlugin, type UIPluginOptions } from '@uppy/core'
import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
import type { Uppy, MinimalRequiredUppyFile } from '@uppy/core/lib/Uppy.ts'
import type {
Body,
Meta,
MinimalRequiredUppyFile,
} from '@uppy/utils/lib/UppyFile'
import type { Uppy } from '@uppy/core/lib/Uppy.ts'

import getFileTypeExtension from '@uppy/utils/lib/getFileTypeExtension'
import supportsMediaRecorder from './supportsMediaRecorder.ts'
Expand Down Expand Up @@ -159,26 +163,26 @@
#startRecording = (): void => {
// only used if supportsMediaRecorder() returned true
// eslint-disable-next-line compat/compat
this.#recorder = new MediaRecorder(this.#stream!)

Check warning on line 166 in packages/@uppy/audio/src/Audio.tsx

View workflow job for this annotation

GitHub Actions / Lint JavaScript/TypeScript

Forbidden non-null assertion
this.#recordingChunks = []
let stoppingBecauseOfMaxSize = false
this.#recorder.addEventListener('dataavailable', (event) => {
this.#recordingChunks!.push(event.data)

Check warning on line 170 in packages/@uppy/audio/src/Audio.tsx

View workflow job for this annotation

GitHub Actions / Lint JavaScript/TypeScript

Forbidden non-null assertion

const { restrictions } = this.uppy.opts
if (
this.#recordingChunks!.length > 1 &&

Check warning on line 174 in packages/@uppy/audio/src/Audio.tsx

View workflow job for this annotation

GitHub Actions / Lint JavaScript/TypeScript

Forbidden non-null assertion
restrictions.maxFileSize != null &&
!stoppingBecauseOfMaxSize
) {
const totalSize = this.#recordingChunks!.reduce(

Check warning on line 178 in packages/@uppy/audio/src/Audio.tsx

View workflow job for this annotation

GitHub Actions / Lint JavaScript/TypeScript

Forbidden non-null assertion
(acc, chunk) => acc + chunk.size,
0,
)
// Exclude the initial chunk from the average size calculation because it is likely to be a very small outlier
const averageChunkSize =
(totalSize - this.#recordingChunks![0].size) /

Check warning on line 184 in packages/@uppy/audio/src/Audio.tsx

View workflow job for this annotation

GitHub Actions / Lint JavaScript/TypeScript

Forbidden non-null assertion
(this.#recordingChunks!.length - 1)

Check warning on line 185 in packages/@uppy/audio/src/Audio.tsx

View workflow job for this annotation

GitHub Actions / Lint JavaScript/TypeScript

Forbidden non-null assertion
const expectedEndChunkSize = averageChunkSize * 3
const maxSize = Math.max(
0,
Expand Down Expand Up @@ -214,10 +218,10 @@

#stopRecording = (): Promise<void> => {
const stopped = new Promise<void>((resolve) => {
this.#recorder!.addEventListener('stop', () => {

Check warning on line 221 in packages/@uppy/audio/src/Audio.tsx

View workflow job for this annotation

GitHub Actions / Lint JavaScript/TypeScript

Forbidden non-null assertion
resolve()
})
this.#recorder!.stop()

Check warning on line 224 in packages/@uppy/audio/src/Audio.tsx

View workflow job for this annotation

GitHub Actions / Lint JavaScript/TypeScript

Forbidden non-null assertion

clearInterval(this.recordingLengthTimer)
this.setPluginState({ recordingLengthSeconds: 0 })
Expand Down Expand Up @@ -283,8 +287,8 @@

if (this.#recorder) {
await new Promise((resolve) => {
this.#recorder!.addEventListener('stop', resolve, { once: true })

Check warning on line 290 in packages/@uppy/audio/src/Audio.tsx

View workflow job for this annotation

GitHub Actions / Lint JavaScript/TypeScript

Forbidden non-null assertion
this.#recorder!.stop()

Check warning on line 291 in packages/@uppy/audio/src/Audio.tsx

View workflow job for this annotation

GitHub Actions / Lint JavaScript/TypeScript

Forbidden non-null assertion

clearInterval(this.recordingLengthTimer)
})
Expand Down
28 changes: 19 additions & 9 deletions packages/@uppy/core/src/Restricter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ export type Restrictions = {
requiredMetaFields: string[]
}

/**
* The minimal required properties to be present from UppyFile in order to validate it.
*/
export type ValidateableFile<M extends Meta, B extends Body> = Pick<
UppyFile<M, B>,
'type' | 'extension' | 'size' | 'name'
// Both UppyFile and CompanionFile need to be passable as a ValidateableFile
// CompanionFile's do not have `isGhost`, so we mark it optional.
> & { isGhost?: boolean }

const defaultOptions = {
maxFileSize: null,
minFileSize: null,
Expand Down Expand Up @@ -69,8 +79,8 @@ class Restricter<M extends Meta, B extends Body> {

// Because these operations are slow, we cannot run them for every file (if we are adding multiple files)
validateAggregateRestrictions(
existingFiles: UppyFile<M, B>[],
addingFiles: UppyFile<M, B>[],
existingFiles: ValidateableFile<M, B>[],
addingFiles: ValidateableFile<M, B>[],
): void {
const { maxTotalFileSize, maxNumberOfFiles } = this.getOpts().restrictions

Expand Down Expand Up @@ -109,7 +119,7 @@ class Restricter<M extends Meta, B extends Body> {
}
}

validateSingleFile(file: UppyFile<M, B>): void {
validateSingleFile(file: ValidateableFile<M, B>): void {
const { maxFileSize, minFileSize, allowedFileTypes } =
this.getOpts().restrictions

Expand All @@ -134,7 +144,7 @@ class Restricter<M extends Meta, B extends Body> {
this.i18n('youCanOnlyUploadFileTypes', {
types: allowedFileTypesString,
}),
{ file },
{ file } as { file: UppyFile<M, B> },
)
}
}
Expand All @@ -146,7 +156,7 @@ class Restricter<M extends Meta, B extends Body> {
size: prettierBytes(maxFileSize),
file: file.name,
}),
{ file },
{ file } as { file: UppyFile<M, B> },
)
}

Expand All @@ -156,14 +166,14 @@ class Restricter<M extends Meta, B extends Body> {
this.i18n('inferiorSize', {
size: prettierBytes(minFileSize),
}),
{ file },
{ file } as { file: UppyFile<M, B> },
)
}
}

validate(
existingFiles: UppyFile<M, B>[],
addingFiles: UppyFile<M, B>[],
existingFiles: ValidateableFile<M, B>[],
addingFiles: ValidateableFile<M, B>[],
): void {
addingFiles.forEach((addingFile) => {
this.validateSingleFile(addingFile)
Expand All @@ -180,7 +190,7 @@ class Restricter<M extends Meta, B extends Body> {
}
}

getMissingRequiredMetaFields(file: UppyFile<M, B>): {
getMissingRequiredMetaFields(file: ValidateableFile<M, B> & { meta: M }): {
missingFields: string[]
error: RestrictionError<M, B>
} {
Expand Down
33 changes: 15 additions & 18 deletions packages/@uppy/core/src/Uppy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import DefaultStore from '@uppy/store-default'
import getFileType from '@uppy/utils/lib/getFileType'
import getFileNameAndExtension from '@uppy/utils/lib/getFileNameAndExtension'
import { getSafeFileId } from '@uppy/utils/lib/generateFileID'
import type { UppyFile, Meta, Body } from '@uppy/utils/lib/UppyFile'
import type {
UppyFile,
Meta,
Body,
MinimalRequiredUppyFile,
} from '@uppy/utils/lib/UppyFile'
import type { CompanionFile } from '@uppy/utils/lib/CompanionFile'
import type {
CompanionClientProvider,
Expand Down Expand Up @@ -43,7 +48,7 @@ import locale from './locale.ts'

import type BasePlugin from './BasePlugin.ts'
import type UIPlugin from './UIPlugin.ts'
import type { Restrictions } from './Restricter.ts'
import type { Restrictions, ValidateableFile } from './Restricter.ts'

type Processor = (fileIDs: string[], uploadID: string) => Promise<void> | void

Expand Down Expand Up @@ -128,17 +133,6 @@ export type UnknownSearchProviderPlugin<
provider: CompanionClientSearchProvider
}

// The user facing type for UppyFile used in uppy.addFile() and uppy.setOptions()
export type MinimalRequiredUppyFile<M extends Meta, B extends Body> = Required<
Pick<UppyFile<M, B>, 'name' | 'data' | 'type' | 'source'>
> &
Partial<
Omit<UppyFile<M, B>, 'name' | 'data' | 'type' | 'source' | 'meta'>
// We want to omit the 'meta' from UppyFile because of internal metadata
// (see InternalMetadata in `UppyFile.ts`), as when adding a new file
// that is not required.
> & { meta?: M }

interface UploadResult<M extends Meta, B extends Body> {
successful?: UppyFile<M, B>[]
failed?: UppyFile<M, B>[]
Expand Down Expand Up @@ -844,8 +838,8 @@ export class Uppy<M extends Meta, B extends Body> {
}

validateRestrictions(
file: UppyFile<M, B>,
files = this.getFiles(),
file: ValidateableFile<M, B>,
files: ValidateableFile<M, B>[] = this.getFiles(),
): RestrictionError<M, B> | null {
try {
this.#restricter.validate(files, [file])
Expand Down Expand Up @@ -882,9 +876,12 @@ export class Uppy<M extends Meta, B extends Body> {
const { allowNewUpload } = this.getState()

if (allowNewUpload === false) {
const error = new RestrictionError(this.i18n('noMoreFilesAllowed'), {
file,
})
const error = new RestrictionError<M, B>(
this.i18n('noMoreFilesAllowed'),
{
file,
},
)
this.#informAndEmit([error])
throw error
}
Expand Down
16 changes: 14 additions & 2 deletions packages/@uppy/utils/src/UppyFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ export interface UppyFile<M extends Meta, B extends Body> {
remote?: {
body?: Record<string, unknown>
companionUrl: string
host: string
host?: string
provider?: string
providerName?: string
requestClientId: string
url: string
}
serverToken?: string | null
size: number | null
source?: string
type?: string
type: string
uploadURL?: string
response?: {
body: B
Expand All @@ -40,3 +41,14 @@ export interface UppyFile<M extends Meta, B extends Body> {
uploadURL?: string
}
}

// The user facing type for UppyFile used in uppy.addFile() and uppy.setOptions()
export type MinimalRequiredUppyFile<M extends Meta, B extends Body> = Required<
Pick<UppyFile<M, B>, 'name' | 'data'>
> &
Partial<
Omit<UppyFile<M, B>, 'name' | 'data' | 'meta'>
// We want to omit the 'meta' from UppyFile because of internal metadata
// (see InternalMetadata in `UppyFile.ts`), as when adding a new file
// that is not required.
> & { meta?: M }
12 changes: 7 additions & 5 deletions packages/@uppy/utils/src/generateFileID.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { UppyFile } from './UppyFile'
import type { MinimalRequiredUppyFile } from './UppyFile'
import getFileType from './getFileType.ts'

function encodeCharacter(character: string): string {
Expand All @@ -19,7 +19,9 @@ function encodeFilename(name: string): string {
* Takes a file object and turns it into fileID, by converting file.name to lowercase,
* removing extra characters and adding type, size and lastModified
*/
export default function generateFileID(file: UppyFile<any, any>): string {
export default function generateFileID(
file: MinimalRequiredUppyFile<any, any>,
): string {
// It's tempting to do `[items].filter(Boolean).join('-')` here, but that
// is slower! simple string concatenation is fast

Expand Down Expand Up @@ -48,7 +50,7 @@ export default function generateFileID(file: UppyFile<any, any>): string {

// If the provider has a stable, unique ID, then we can use that to identify the file.
// Then we don't have to generate our own ID, and we can add the same file many times if needed (different path)
function hasFileStableId(file: UppyFile<any, any>): boolean {
function hasFileStableId(file: MinimalRequiredUppyFile<any, any>): boolean {
if (!file.isRemote || !file.remote) return false
// These are the providers that it seems like have stable IDs for their files. The other's I haven't checked yet.
const stableIdProviders = new Set([
Expand All @@ -61,8 +63,8 @@ function hasFileStableId(file: UppyFile<any, any>): boolean {
return stableIdProviders.has(file.remote.provider as any)
}

export function getSafeFileId(file: UppyFile<any, any>): string {
if (hasFileStableId(file)) return file.id
export function getSafeFileId(file: MinimalRequiredUppyFile<any, any>): string {
if (hasFileStableId(file)) return file.id!

const fileType = getFileType(file)

Expand Down
6 changes: 4 additions & 2 deletions packages/@uppy/utils/src/getFileType.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type { UppyFile } from './UppyFile'
import getFileNameAndExtension from './getFileNameAndExtension.ts'
import mimeTypes from './mimeTypes.ts'

export default function getFileType(file: Partial<UppyFile<any, any>>): string {
export default function getFileType(file: {
type?: string
name?: string
}): string {
if (file.type) return file.type

const fileExtension =
Expand Down