From 47c981f594f96de5bc67575d46fd0887a043ae3b Mon Sep 17 00:00:00 2001 From: Lucas Coratger <73360179+coratgerl@users.noreply.github.com> Date: Sat, 25 Apr 2026 20:03:55 +0200 Subject: [PATCH] fix(wabe): file type on custom mutation --- packages/wabe/src/file/index.test.ts | 89 ++++++++++++++++++++++----- packages/wabe/src/graphql/parser.ts | 3 +- packages/wabe/src/utils/testHelper.ts | 3 + 3 files changed, 77 insertions(+), 18 deletions(-) diff --git a/packages/wabe/src/file/index.test.ts b/packages/wabe/src/file/index.test.ts index c28eb0c9..3e44073d 100644 --- a/packages/wabe/src/file/index.test.ts +++ b/packages/wabe/src/file/index.test.ts @@ -14,28 +14,52 @@ describe('File upload', () => { const mockBeforeUpload = mock() beforeAll(async () => { - const setup = await setupTests([ - { - name: 'Test3', - fields: { - file: { type: 'File' }, - }, - permissions: { - read: { - requireAuthentication: false, - }, - create: { - requireAuthentication: false, + const setup = await setupTests( + [ + { + name: 'Test3', + fields: { + file: { type: 'File' }, }, - update: { - requireAuthentication: false, + permissions: { + read: { + requireAuthentication: false, + }, + create: { + requireAuthentication: false, + }, + update: { + requireAuthentication: false, + }, + delete: { + requireAuthentication: false, + }, }, - delete: { - requireAuthentication: false, + }, + ], + { + resolvers: { + mutations: { + uploadFileCustom: { + required: true, + type: 'String', + args: { + input: { + attachment: { type: 'File', required: true }, + }, + }, + resolve: async ( + _parent: unknown, + args: { input: { attachment: { file: File } } }, + ) => { + const file = args.input.attachment.file + return `${file.name}|${await file.text()}` + }, + }, }, }, }, - ]) + ) wabe = setup.wabe port = setup.port @@ -301,6 +325,37 @@ describe('File upload', () => { expect(await fileArg2?.text()).toEqual('b') }) + it('should accept multipart file upload on a custom mutation File input field', async () => { + const formData = new FormData() + + formData.append( + 'operations', + JSON.stringify({ + query: + 'mutation ($file: File!) { uploadFileCustom(input: { attachment: { file: $file } }) }', + variables: { file: null }, + }), + ) + + formData.append('map', JSON.stringify({ 0: ['variables.file'] })) + formData.append( + '0', + new File(['custom-mutation-payload'], 'custom-doc.txt', { type: 'text/plain' }), + ) + + const res = await fetch(`http://127.0.0.1:${port}/graphql`, { + method: 'POST', + body: formData, + }) + + const jsonRes = await res.json() + + expect(jsonRes.errors).toBeUndefined() + expect(jsonRes.data.uploadFileCustom).toEqual('custom-doc.txt|custom-mutation-payload') + + expect(spyFileDevAdapterUploadFile).not.toHaveBeenCalled() + }) + it('should upload a file on request on type File on create request', async () => { const formData = new FormData() diff --git a/packages/wabe/src/graphql/parser.ts b/packages/wabe/src/graphql/parser.ts index 8ef2ea68..b0471d38 100644 --- a/packages/wabe/src/graphql/parser.ts +++ b/packages/wabe/src/graphql/parser.ts @@ -626,7 +626,8 @@ export const GraphqlParser: GraphqlParserConstructor = if ( graphqlObjectType === 'CreateFieldsInput' || - graphqlObjectType === 'UpdateFieldsInput' + graphqlObjectType === 'UpdateFieldsInput' || + graphqlObjectType === 'InputObject' ) { acc[key] = { type: currentField.required diff --git a/packages/wabe/src/utils/testHelper.ts b/packages/wabe/src/utils/testHelper.ts index 830e7ae0..f95af25e 100644 --- a/packages/wabe/src/utils/testHelper.ts +++ b/packages/wabe/src/utils/testHelper.ts @@ -1,6 +1,7 @@ import { v4 as uuid } from 'uuid' import type { RateLimitOptions } from 'wobe' import { type ClassInterface, EmailDevAdapter, FileDevAdapter } from '..' +import type { TypeResolver } from '../schema/Schema' import { Wabe } from '../server' import type { DevWabeTypes } from './helper' import getPort from 'get-port' @@ -26,6 +27,7 @@ export const setupTests = async ( rateLimit?: RateLimitOptions maxWhereRecursionDepth?: number security?: { maxWhereRecursionDepth?: number } + resolvers?: TypeResolver } = {}, ) => { const maxWhereRecursionDepth = @@ -122,6 +124,7 @@ export const setupTests = async ( description: 'Test scalar', }, ], + ...(options.resolvers && { resolvers: options.resolvers }), }, })