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

@uppy/companion-client: type changes for provider-views #4938

Merged
merged 6 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
42 changes: 19 additions & 23 deletions packages/@uppy/companion-client/src/Provider.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { Uppy, BasePlugin } from '@uppy/core'
import type { Body, Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
import type { Uppy } from '@uppy/core'
import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
import type { PluginOpts } from '@uppy/core/lib/BasePlugin.ts'
import RequestClient, {
authErrorStatusCode,
type RequestOptions,
} from './RequestClient.ts'
import type {
RequestOptions,
CompanionClientProvider,
} from '@uppy/utils/lib/CompanionClientProvider'
import type { UnknownProviderPlugin } from '@uppy/core/lib/Uppy.ts'
import RequestClient, { authErrorStatusCode } from './RequestClient.ts'
import * as tokenStorage from './tokenStorage.ts'

// TODO: remove deprecated options in next major release
Expand All @@ -22,13 +24,6 @@ export interface Opts extends PluginOpts {
provider: string
}

interface ProviderPlugin<M extends Meta, B extends Body>
extends BasePlugin<Opts, M, B> {
files: UppyFile<M, B>[]

storage: typeof tokenStorage
}

const getName = (id: string) => {
return id
.split('-')
Expand Down Expand Up @@ -64,10 +59,10 @@ function isOriginAllowed(
) // allowing for trailing '/'
}

export default class Provider<
M extends Meta,
B extends Body,
> extends RequestClient<M, B> {
export default class Provider<M extends Meta, B extends Body>
extends RequestClient<M, B>
implements CompanionClientProvider
{
#refreshingTokenPromise: Promise<void> | undefined

provider: string
Expand Down Expand Up @@ -141,7 +136,10 @@ export default class Provider<
}

#getPlugin() {
const plugin = this.uppy.getPlugin(this.pluginId) as ProviderPlugin<M, B>
const plugin = this.uppy.getPlugin(this.pluginId) as UnknownProviderPlugin<
M,
B
>
if (plugin == null) throw new Error('Plugin was nullish')
return plugin
}
Expand Down Expand Up @@ -375,23 +373,21 @@ export default class Provider<
}
}

list<ResBody extends Record<string, unknown>>(
list<ResBody>(
directory: string | undefined,
options: RequestOptions,
): Promise<ResBody> {
return this.get<ResBody>(`${this.id}/list/${directory || ''}`, options)
}

async logout<ResBody extends Record<string, unknown>>(
options: RequestOptions,
): Promise<ResBody> {
async logout<ResBody>(options?: RequestOptions): Promise<ResBody> {
const response = await this.get<ResBody>(`${this.id}/logout`, options)
await this.removeAuthToken()
return response
}

static initPlugin(
plugin: ProviderPlugin<any, any>, // any because static methods cannot use class generics
plugin: UnknownProviderPlugin<any, any>, // any because static methods cannot use class generics
opts: Opts,
defaultOpts: Record<string, unknown>,
): void {
Expand Down
8 changes: 1 addition & 7 deletions packages/@uppy/companion-client/src/RequestClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import emitSocketProgress from '@uppy/utils/lib/emitSocketProgress'
import getSocketHost from '@uppy/utils/lib/getSocketHost'

import type Uppy from '@uppy/core'
import type { RequestOptions } from '@uppy/utils/lib/CompanionClientProvider.ts'
import type { UppyFile, Meta, Body } from '@uppy/utils/lib/UppyFile'
aduh95 marked this conversation as resolved.
Show resolved Hide resolved
import AuthError from './AuthError.ts'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
Expand All @@ -28,13 +29,6 @@ export type Opts = {
companionKeysParams?: Record<string, string>
}

export type RequestOptions = {
method?: string
data?: Record<string, unknown>
skipPostResponse?: boolean
signal?: AbortSignal
qs?: Record<string, string>
}
type _RequestOptions =
| boolean // TODO: remove this on the next major
| RequestOptions
Expand Down
14 changes: 6 additions & 8 deletions packages/@uppy/companion-client/src/SearchProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import type { Body, Meta } from '@uppy/utils/lib/UppyFile.ts'
import type { Uppy } from '@uppy/core'
import type { CompanionClientSearchProvider } from '@uppy/utils/lib/CompanionClientProvider'
import RequestClient, { type Opts } from './RequestClient.ts'

const getName = (id: string): string => {
Expand All @@ -11,10 +12,10 @@ const getName = (id: string): string => {
.join(' ')
}

export default class SearchProvider<
M extends Meta,
B extends Body,
> extends RequestClient<M, B> {
export default class SearchProvider<M extends Meta, B extends Body>
extends RequestClient<M, B>
implements CompanionClientSearchProvider
{
provider: string

id: string
Expand All @@ -35,10 +36,7 @@ export default class SearchProvider<
return `${this.hostname}/search/${this.id}/get/${id}`
}

search<ResBody extends Record<string, unknown>>(
text: string,
queries?: string,
): Promise<ResBody> {
search<ResBody>(text: string, queries?: string): Promise<ResBody> {
return this.get<ResBody>(
`search/${this.id}/list?q=${encodeURIComponent(text)}${
queries ? `&${queries}` : ''
Expand Down
89 changes: 79 additions & 10 deletions packages/@uppy/core/src/Uppy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ 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 { CompanionFile } from '@uppy/utils/lib/CompanionFile'
import type {
CompanionClientProvider,
CompanionClientSearchProvider,
} from '@uppy/utils/lib/CompanionClientProvider'
import type {
FileProgressNotStarted,
FileProgressStarted,
Expand Down Expand Up @@ -46,19 +51,83 @@ type FileRemoveReason = 'user' | 'cancel-all'

type LogLevel = 'info' | 'warning' | 'error' | 'success'

export type UnknownPlugin<M extends Meta, B extends Body> = InstanceType<
typeof BasePlugin<any, M, B> | typeof UIPlugin<any, M, B>
>

type UnknownProviderPlugin<M extends Meta, B extends Body> = UnknownPlugin<
M,
B
> & {
provider: {
logout: () => void
export type UnknownPlugin<
M extends Meta,
B extends Body,
PluginState extends Record<string, unknown> = Record<string, unknown>,
> = BasePlugin<any, M, B, PluginState>

export type UnknownProviderPluginState = {
authenticated: boolean | undefined
breadcrumbs: {
requestPath: string
name: string
id?: string
}[]
didFirstRender: boolean
currentSelection: CompanionFile[]
filterInput: string
loading: boolean | string
folders: CompanionFile[]
files: CompanionFile[]
isSearchVisible: boolean
}
/*
* UnknownProviderPlugin can be any Companion plugin (such as Google Drive).
* As the plugins are passed around throughout Uppy we need a generic type for this.
* It may seems like duplication, but this type safe. Changing the type of `storage`
* will error in the `Provider` class of @uppy/companion-client and vice versa.
*
* Note that this is the *plugin* class, not a version of the `Provider` class.
* `Provider` does operate on Companion plugins with `uppy.getPlugin()`.
*/
export type UnknownProviderPlugin<
M extends Meta,
B extends Body,
> = UnknownPlugin<M, B, UnknownProviderPluginState> & {
onFirstRender: () => void
title: string
files: UppyFile<M, B>[]
icon: () => JSX.Element
provider: CompanionClientProvider
storage: {
getItem: (key: string) => Promise<string | null>
setItem: (key: string, value: string) => Promise<void>
removeItem: (key: string) => Promise<void>
}
}

/*
* UnknownSearchProviderPlugin can be any search Companion plugin (such as Unsplash).
* As the plugins are passed around throughout Uppy we need a generic type for this.
* It may seems like duplication, but this type safe. Changing the type of `title`
* will error in the `SearchProvider` class of @uppy/companion-client and vice versa.
*
* Note that this is the *plugin* class, not a version of the `SearchProvider` class.
* `SearchProvider` does operate on Companion plugins with `uppy.getPlugin()`.
*/
export type UnknownSearchProviderPluginState = {
isInputMode?: boolean
searchTerm?: string | null
} & Pick<
UnknownProviderPluginState,
| 'loading'
| 'files'
| 'folders'
| 'currentSelection'
| 'filterInput'
| 'didFirstRender'
>
export type UnknownSearchProviderPlugin<
M extends Meta,
B extends Body,
> = UnknownPlugin<M, B, UnknownSearchProviderPluginState> & {
onFirstRender: () => void
title: string
icon: () => JSX.Element
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'>
Expand Down
9 changes: 8 additions & 1 deletion packages/@uppy/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
export { default } from './Uppy.ts'
export { default as Uppy, type UppyEventMap, type State } from './Uppy.ts'
export {
default as Uppy,
type UppyEventMap,
type State,
type UnknownPlugin,
type UnknownProviderPlugin,
type UnknownSearchProviderPlugin,
} from './Uppy.ts'
export { default as UIPlugin } from './UIPlugin.ts'
export { default as BasePlugin } from './BasePlugin.ts'
export { debugLogger } from './loggers.ts'
Expand Down
35 changes: 35 additions & 0 deletions packages/@uppy/utils/src/CompanionClientProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export type RequestOptions = {
method?: string
data?: Record<string, unknown>
skipPostResponse?: boolean
signal?: AbortSignal
authFormData?: unknown
qs?: Record<string, string>
}

/**
* CompanionClientProvider is subset of the types of the `Provider`
* class from @uppy/companion-client.
*
* This is needed as the `Provider` class is passed around in Uppy and we
* need to have shared types for it. Although we are duplicating some types,
* this is still safe as `Provider implements CompanionClientProvider`
* so any changes here will error there and vice versa.
*
* TODO: remove this once companion-client and provider-views are merged into a single plugin.
*/
export interface CompanionClientProvider {
name: string
provider: string
login(options?: RequestOptions): Promise<void>
logout<ResBody>(options?: RequestOptions): Promise<ResBody>
list<ResBody>(
directory: string | undefined,
options: RequestOptions,
): Promise<ResBody>
}
export interface CompanionClientSearchProvider {
name: string
provider: string
search<ResBody>(text: string, queries?: string): Promise<ResBody>
}
25 changes: 25 additions & 0 deletions packages/@uppy/utils/src/CompanionFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* CompanionFile represents a file object returned by the Companion API.
*/
export type CompanionFile = {
id: string
name: string
/*
* Url to the thumbnail icon
*/
icon: string
type: string
mimeType: string
extension: string
size: number
isFolder: boolean
modifiedDate: string
thumbnail?: string
requestPath: string
relDirPath?: string
absDirPath?: string
author?: {
name?: string
url?: string
}
}