From a77ebeec00e54ae4094701053ab35621d628f48f Mon Sep 17 00:00:00 2001 From: juliusmarminge Date: Tue, 5 Nov 2024 23:35:20 +0100 Subject: [PATCH] forward adapter args to callbacks too --- packages/uploadthing/src/effect-platform.ts | 4 +- packages/uploadthing/src/express.ts | 4 +- packages/uploadthing/src/fastify.ts | 4 +- packages/uploadthing/src/h3.ts | 4 +- packages/uploadthing/src/internal/handler.ts | 57 ++++++++++--------- packages/uploadthing/src/internal/types.ts | 49 +++++++++------- .../src/internal/upload-builder.ts | 12 ++-- packages/uploadthing/src/next-legacy.ts | 4 +- packages/uploadthing/src/next.ts | 4 +- packages/uploadthing/src/remix.ts | 4 +- packages/uploadthing/src/server.ts | 4 +- .../uploadthing/test/request-handler.test.ts | 22 +++---- 12 files changed, 92 insertions(+), 80 deletions(-) diff --git a/packages/uploadthing/src/effect-platform.ts b/packages/uploadthing/src/effect-platform.ts index af33d94226..6b1437a723 100644 --- a/packages/uploadthing/src/effect-platform.ts +++ b/packages/uploadthing/src/effect-platform.ts @@ -5,7 +5,7 @@ import * as Layer from "effect/Layer"; import type { Json } from "@uploadthing/shared"; import { configProvider } from "./internal/config"; -import { createRequestHandler, MiddlewareArguments } from "./internal/handler"; +import { AdapterArguments, createRequestHandler } from "./internal/handler"; import type { CreateBuilderOptions } from "./internal/upload-builder"; import { createBuilder } from "./internal/upload-builder"; import type { FileRouter, RouteHandlerConfig } from "./types"; @@ -52,7 +52,7 @@ export const createRouteHandler = (opts: { return HttpRouter.provideServiceEffect( router, - MiddlewareArguments, + AdapterArguments, Effect.map(HttpServerRequest.HttpServerRequest, (serverRequest) => ({ req: serverRequest, res: undefined, diff --git a/packages/uploadthing/src/express.ts b/packages/uploadthing/src/express.ts index 32e7bedf3a..e3c2b38ef8 100644 --- a/packages/uploadthing/src/express.ts +++ b/packages/uploadthing/src/express.ts @@ -17,7 +17,7 @@ import type { FileRouter, RouteHandlerOptions } from "./types"; export { UTFiles } from "./internal/types"; export type { FileRouter }; -type MiddlewareArgs = { +type AdapterArgs = { req: ExpressRequest; res: ExpressResponse; event: undefined; @@ -25,7 +25,7 @@ type MiddlewareArgs = { export const createUploadthing = ( opts?: CreateBuilderOptions, -) => createBuilder(opts); +) => createBuilder(opts); export const createRouteHandler = ( opts: RouteHandlerOptions, diff --git a/packages/uploadthing/src/fastify.ts b/packages/uploadthing/src/fastify.ts index df246ea5aa..8aa3fc6b23 100644 --- a/packages/uploadthing/src/fastify.ts +++ b/packages/uploadthing/src/fastify.ts @@ -12,7 +12,7 @@ import type { FileRouter, RouteHandlerOptions } from "./types"; export { UTFiles } from "./internal/types"; export type { FileRouter }; -type MiddlewareArgs = { +type AdapterArgs = { req: FastifyRequest; res: FastifyReply; event: undefined; @@ -20,7 +20,7 @@ type MiddlewareArgs = { export const createUploadthing = ( opts?: CreateBuilderOptions, -) => createBuilder(opts); +) => createBuilder(opts); export const createRouteHandler = ( fastify: FastifyInstance, diff --git a/packages/uploadthing/src/h3.ts b/packages/uploadthing/src/h3.ts index 7b6881c568..ab9ebf170a 100644 --- a/packages/uploadthing/src/h3.ts +++ b/packages/uploadthing/src/h3.ts @@ -12,11 +12,11 @@ import type { FileRouter, RouteHandlerOptions } from "./types"; export { UTFiles } from "./internal/types"; export type { FileRouter }; -type MiddlewareArgs = { req: undefined; res: undefined; event: H3Event }; +type AdapterArgs = { req: undefined; res: undefined; event: H3Event }; export const createUploadthing = ( opts?: CreateBuilderOptions, -) => createBuilder(opts); +) => createBuilder(opts); export const createRouteHandler = ( opts: RouteHandlerOptions, diff --git a/packages/uploadthing/src/internal/handler.ts b/packages/uploadthing/src/internal/handler.ts index 15960b6136..c2a29b6413 100644 --- a/packages/uploadthing/src/internal/handler.ts +++ b/packages/uploadthing/src/internal/handler.ts @@ -13,7 +13,7 @@ import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Match from "effect/Match"; import * as Redacted from "effect/Redacted"; -import * as S from "effect/Schema"; +import * as Schema from "effect/Schema"; import { fillInputRouteConfig, @@ -45,16 +45,16 @@ import { UploadThingHook, } from "./shared-schemas"; import { UTFiles } from "./types"; -import type { AnyFileRoute, MiddlewareFnArgs, UTEvents } from "./types"; +import type { AdapterFnArgs, AnyFileRoute, UTEvents } from "./types"; -export class MiddlewareArguments extends Context.Tag( - "uploadthing/MiddlewareArguments", -)>() {} +export class AdapterArguments extends Context.Tag( + "uploadthing/AdapterArguments", +)>() {} export const makeAdapterHandler = ( - makeMiddlewareArgs: ( + makeAdapterArgs: ( ...args: Args - ) => Effect.Effect>, + ) => Effect.Effect>, toRequest: (...args: Args) => Effect.Effect, opts: RouteHandlerOptions, beAdapter: string, @@ -72,10 +72,7 @@ export const makeAdapterHandler = ( Effect.promise(() => managed.runPromise(createRequestHandler(opts, beAdapter)), ), - Effect.provideServiceEffect( - MiddlewareArguments, - makeMiddlewareArgs(...args), - ), + Effect.provideServiceEffect(AdapterArguments, makeAdapterArgs(...args)), ); return async (...args: Args) => { @@ -121,13 +118,13 @@ export const createRequestHandler = ( "x-uploadthing-package": fePackage, "x-uploadthing-version": clientVersion, } = yield* HttpServerRequest.schemaHeaders( - S.Struct({ - "uploadthing-hook": UploadThingHook.pipe(S.optional), - "x-uploadthing-package": S.String.pipe( - S.optionalWith({ default: () => "unknown" }), + Schema.Struct({ + "uploadthing-hook": UploadThingHook.pipe(Schema.optional), + "x-uploadthing-package": Schema.String.pipe( + Schema.optionalWith({ default: () => "unknown" }), ), - "x-uploadthing-version": S.String.pipe( - S.optionalWith({ default: () => pkgJson.version }), + "x-uploadthing-version": Schema.String.pipe( + Schema.optionalWith({ default: () => pkgJson.version }), ), }), ); @@ -140,9 +137,9 @@ export const createRequestHandler = ( } const { slug, actionType } = yield* HttpRouter.schemaParams( - S.Struct({ - actionType: ActionType.pipe(S.optional), - slug: S.String, + Schema.Struct({ + actionType: ActionType.pipe(Schema.optional), + slug: Schema.String, }), ); @@ -248,18 +245,20 @@ const handleErrorRequest = (opts: { uploadable: AnyFileRoute }) => } const requestInput = yield* HttpServerRequest.schemaBodyJson( - S.Struct({ - fileKey: S.String, - error: S.String, + Schema.Struct({ + fileKey: Schema.String, + error: Schema.String, }), ); yield* Effect.logDebug("Handling error callback request with input:").pipe( Effect.annotateLogs("json", requestInput), ); + const adapterArgs = yield* AdapterArguments; const fiber = yield* Effect.tryPromise({ try: async () => uploadable.onUploadError({ + ...adapterArgs, error: new UploadThingError({ code: "UPLOAD_FAILED", message: `Upload failed for ${requestInput.fileKey}: ${requestInput.error}`, @@ -312,10 +311,10 @@ const handleCallbackRequest = (opts: { } const requestInput = yield* HttpServerRequest.schemaBodyJson( - S.Struct({ - status: S.String, + Schema.Struct({ + status: Schema.String, file: UploadedFileData, - metadata: S.Record({ key: S.String, value: S.Unknown }), + metadata: Schema.Record({ key: Schema.String, value: Schema.Unknown }), }), ); yield* Effect.logDebug("Handling callback request with input:").pipe( @@ -326,10 +325,12 @@ const handleCallbackRequest = (opts: { * Run `.onUploadComplete` as a daemon to prevent the * request from UT to potentially timeout. */ + const adapterArgs = yield* AdapterArguments; const fiber = yield* Effect.gen(function* () { const serverData = yield* Effect.tryPromise({ try: async () => uploadable.onUploadComplete({ + ...adapterArgs, file: requestInput.file, metadata: requestInput.metadata, }) as Promise, @@ -384,7 +385,7 @@ const runRouteMiddleware = (opts: { uploadable: AnyFileRoute; }) => Effect.gen(function* () { - const middlewareArgs = yield* MiddlewareArguments; + const adapterArgs = yield* AdapterArguments; const { json: { files, input }, uploadable, @@ -394,7 +395,7 @@ const runRouteMiddleware = (opts: { const metadata = yield* Effect.tryPromise({ try: async () => uploadable.middleware({ - ...middlewareArgs, + ...adapterArgs, input, files, }), diff --git a/packages/uploadthing/src/internal/types.ts b/packages/uploadthing/src/internal/types.ts index dc6a6cc868..2a3c410611 100644 --- a/packages/uploadthing/src/internal/types.ts +++ b/packages/uploadthing/src/internal/types.ts @@ -48,7 +48,7 @@ export type ValidMiddlewareObject = { /** * Different frameworks have different request and response types */ -export type MiddlewareFnArgs = { +export type AdapterFnArgs = { req: TRequest; res: TResponse; event: TEvent; @@ -61,7 +61,7 @@ export interface AnyParams { out: any; }; _metadata: any; // imaginary field used to bind metadata return type to an Upload resolver - _middlewareArgs: MiddlewareFnArgs; + _adapterFnArgs: AdapterFnArgs; _errorShape: any; _errorFn: any; // used for onUploadError _output: any; @@ -70,7 +70,7 @@ export interface AnyParams { type MiddlewareFn< TInput extends Json | UnsetMarker, TOutput extends ValidMiddlewareObject, - TArgs extends MiddlewareFnArgs, + TArgs extends AdapterFnArgs, > = ( opts: TArgs & { files: Schema.Type["files"]; @@ -78,15 +78,23 @@ type MiddlewareFn< }, ) => MaybePromise; -type UploadCompleteFn = (opts: { - metadata: TMetadata; - file: UploadedFileData; -}) => MaybePromise; +type UploadCompleteFn< + TMetadata, + TOutput extends JsonObject | void, + TArgs extends AdapterFnArgs, +> = ( + opts: TArgs & { + metadata: TMetadata; + file: UploadedFileData; + }, +) => MaybePromise; -type UploadErrorFn = (input: { - error: UploadThingError; - fileKey: string; -}) => MaybePromise; +type UploadErrorFn> = ( + input: TArgs & { + error: UploadThingError; + fileKey: string; + }, +) => MaybePromise; export interface UploadBuilder { input: ( @@ -97,7 +105,7 @@ export interface UploadBuilder { _routeOptions: TParams["_routeOptions"]; _input: { in: TParser["_input"]; out: TParser["_output"] }; _metadata: TParams["_metadata"]; - _middlewareArgs: TParams["_middlewareArgs"]; + _adapterFnArgs: TParams["_adapterFnArgs"]; _errorShape: TParams["_errorShape"]; _errorFn: TParams["_errorFn"]; _output: UnsetMarker; @@ -107,29 +115,29 @@ export interface UploadBuilder { ? MiddlewareFn< TParams["_input"]["out"], TOutput, - TParams["_middlewareArgs"] + TParams["_adapterFnArgs"] > : ErrorMessage<"middleware is already set">, ) => UploadBuilder<{ _routeOptions: TParams["_routeOptions"]; _input: TParams["_input"]; _metadata: TOutput; - _middlewareArgs: TParams["_middlewareArgs"]; + _adapterFnArgs: TParams["_adapterFnArgs"]; _errorShape: TParams["_errorShape"]; _errorFn: TParams["_errorFn"]; _output: UnsetMarker; }>; onUploadError: ( fn: TParams["_errorFn"] extends UnsetMarker - ? UploadErrorFn + ? UploadErrorFn : ErrorMessage<"onUploadError is already set">, ) => UploadBuilder<{ _routeOptions: TParams["_routeOptions"]; _input: TParams["_input"]; _metadata: TParams["_metadata"]; - _middlewareArgs: TParams["_middlewareArgs"]; + _adapterFnArgs: TParams["_adapterFnArgs"]; _errorShape: TParams["_errorShape"]; - _errorFn: UploadErrorFn; + _errorFn: UploadErrorFn; _output: UnsetMarker; }>; onUploadComplete: ( @@ -139,7 +147,8 @@ export interface UploadBuilder { ? undefined : Omit >, - TOutput + TOutput, + TParams["_adapterFnArgs"] >, ) => FileRoute<{ input: TParams["_input"]["in"] extends UnsetMarker @@ -166,9 +175,9 @@ export interface FileRoute { routeOptions: RouteOptions; inputParser: JsonParser; middleware: MiddlewareFn; - onUploadError: UploadErrorFn; + onUploadError: UploadErrorFn; errorFormatter: (err: UploadThingError) => any; - onUploadComplete: UploadCompleteFn; + onUploadComplete: UploadCompleteFn; } export type AnyFileRoute = FileRoute; diff --git a/packages/uploadthing/src/internal/upload-builder.ts b/packages/uploadthing/src/internal/upload-builder.ts index 4f490949a2..fb85c90c49 100644 --- a/packages/uploadthing/src/internal/upload-builder.ts +++ b/packages/uploadthing/src/internal/upload-builder.ts @@ -7,15 +7,15 @@ import type { import { defaultErrorFormatter } from "./error-formatter"; import type { + AdapterFnArgs, AnyBuiltUploaderTypes, AnyFileRoute, - MiddlewareFnArgs, UnsetMarker, UploadBuilder, } from "./types"; function internalCreateBuilder< - TMiddlewareArgs extends MiddlewareFnArgs, + TAdapterFnArgs extends AdapterFnArgs, TRouteOptions extends RouteOptions, TErrorShape extends Json = { message: string }, >( @@ -24,7 +24,7 @@ function internalCreateBuilder< _routeOptions: TRouteOptions; _input: { in: UnsetMarker; out: UnsetMarker }; _metadata: UnsetMarker; - _middlewareArgs: TMiddlewareArgs; + _adapterFnArgs: TAdapterFnArgs; _errorShape: TErrorShape; _errorFn: UnsetMarker; _output: UnsetMarker; @@ -92,7 +92,7 @@ export type CreateBuilderOptions = { }; export function createBuilder< - TMiddlewareArgs extends MiddlewareFnArgs, + TAdapterFnArgs extends AdapterFnArgs, TErrorShape extends Json = { message: string }, >(opts?: CreateBuilderOptions) { return ( @@ -102,12 +102,12 @@ export function createBuilder< _routeOptions: TRouteOptions; _input: { in: UnsetMarker; out: UnsetMarker }; _metadata: UnsetMarker; - _middlewareArgs: TMiddlewareArgs; + _adapterFnArgs: TAdapterFnArgs; _errorShape: TErrorShape; _errorFn: UnsetMarker; _output: UnsetMarker; }> => { - return internalCreateBuilder({ + return internalCreateBuilder({ routerConfig: input, routeOptions: config ?? {}, ...opts, diff --git a/packages/uploadthing/src/next-legacy.ts b/packages/uploadthing/src/next-legacy.ts index 4229fdab30..afe7511c94 100644 --- a/packages/uploadthing/src/next-legacy.ts +++ b/packages/uploadthing/src/next-legacy.ts @@ -12,7 +12,7 @@ import type { FileRouter, RouteHandlerOptions } from "./types"; export { UTFiles } from "./internal/types"; export type { FileRouter }; -type MiddlewareArgs = { +type AdapterArgs = { req: NextApiRequest; res: NextApiResponse; event: undefined; @@ -20,7 +20,7 @@ type MiddlewareArgs = { export const createUploadthing = ( opts?: CreateBuilderOptions, -) => createBuilder(opts); +) => createBuilder(opts); export const createRouteHandler = ( opts: RouteHandlerOptions, diff --git a/packages/uploadthing/src/next.ts b/packages/uploadthing/src/next.ts index 4872f4ae04..bd74eb4a77 100644 --- a/packages/uploadthing/src/next.ts +++ b/packages/uploadthing/src/next.ts @@ -11,11 +11,11 @@ import type { FileRouter, RouteHandlerOptions } from "./types"; export type { FileRouter }; export { UTFiles } from "./internal/types"; -type MiddlewareArgs = { req: NextRequest; res: undefined; event: undefined }; +type AdapterArgs = { req: NextRequest; res: undefined; event: undefined }; export const createUploadthing = ( opts?: CreateBuilderOptions, -) => createBuilder(opts); +) => createBuilder(opts); export const createRouteHandler = ( opts: RouteHandlerOptions, diff --git a/packages/uploadthing/src/remix.ts b/packages/uploadthing/src/remix.ts index ec6f066123..c72a99a3ee 100644 --- a/packages/uploadthing/src/remix.ts +++ b/packages/uploadthing/src/remix.ts @@ -11,7 +11,7 @@ import type { FileRouter, RouteHandlerOptions } from "./types"; export type { FileRouter }; export { UTFiles } from "./internal/types"; -type MiddlewareArgs = { +type AdapterArgs = { req: undefined; res: undefined; event: ActionFunctionArgs; @@ -19,7 +19,7 @@ type MiddlewareArgs = { export const createUploadthing = ( opts?: CreateBuilderOptions, -) => createBuilder(opts); +) => createBuilder(opts); export const createRouteHandler = ( opts: RouteHandlerOptions, diff --git a/packages/uploadthing/src/server.ts b/packages/uploadthing/src/server.ts index 8f01b61b5a..70458f80d0 100644 --- a/packages/uploadthing/src/server.ts +++ b/packages/uploadthing/src/server.ts @@ -14,11 +14,11 @@ export { UTApi } from "./sdk"; export { UTFile } from "./sdk/ut-file"; export { UploadThingError, type FileRouter }; -type MiddlewareArgs = { req: Request; res: undefined; event: undefined }; +type AdapterArgs = { req: Request; res: undefined; event: undefined }; export const createUploadthing = ( opts?: CreateBuilderOptions, -) => createBuilder(opts); +) => createBuilder(opts); export const createRouteHandler = ( opts: RouteHandlerOptions, diff --git a/packages/uploadthing/test/request-handler.test.ts b/packages/uploadthing/test/request-handler.test.ts index 8fd975aa69..46a17fb53a 100644 --- a/packages/uploadthing/test/request-handler.test.ts +++ b/packages/uploadthing/test/request-handler.test.ts @@ -426,21 +426,23 @@ describe(".onUploadComplete()", () => { signPayload(payload, testToken.decoded.apiKey), ); - const res = await handler( - new Request(createApiUrl("imageUploader"), { - method: "POST", - headers: { - "uploadthing-hook": "callback", - "x-uploadthing-signature": signature, - }, - body: payload, - }), - ); + const request = new Request(createApiUrl("imageUploader"), { + method: "POST", + headers: { + "uploadthing-hook": "callback", + "x-uploadthing-signature": signature, + }, + body: payload, + }); + const res = await handler(request); expect(res.status).toBe(200); await expect(res.json()).resolves.toBe(null); expect(uploadCompleteMock).toHaveBeenCalledOnce(); expect(uploadCompleteMock).toHaveBeenCalledWith({ + event: undefined, + res: undefined, + req: request, file: { customId: null, key: "some-random-key.png",