diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index 022fb99fd2..7ed1714d3c 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -6832,7 +6832,7 @@ describe('ParseGraphQLServer', () => { describe('Files Mutations', () => { describe('Create', () => { - it_only_node_version('<17')('should return File object', async () => { + it('should return File object', async () => { const clientMutationId = uuidv4(); parseServer = await global.reconfigureServer({ @@ -9298,7 +9298,7 @@ describe('ParseGraphQLServer', () => { expect(result6[0].node.name).toEqual('imACountry3'); }); - it_only_node_version('<17')('should support files', async () => { + it('should support files', async () => { try { parseServer = await global.reconfigureServer({ publicServerURL: 'http://localhost:13377/parse', @@ -9546,7 +9546,115 @@ describe('ParseGraphQLServer', () => { } }); - it_only_node_version('<17')('should not upload if file is too large', async () => { + it('should support file upload for on fly creation through pointer and relation', async () => { + parseServer = await global.reconfigureServer({ + publicServerURL: 'http://localhost:13377/parse', + }); + const schema = new Parse.Schema('SomeClass'); + schema.addFile('someFileField'); + schema.addPointer('somePointerField', 'SomeClass'); + schema.addRelation('someRelationField', 'SomeClass'); + await schema.save(); + + const body = new FormData(); + body.append( + 'operations', + JSON.stringify({ + query: ` + mutation UploadFiles( + $fields: CreateSomeClassFieldsInput + ) { + createSomeClass( + input: { fields: $fields } + ) { + someClass { + id + someFileField { + name + url + } + somePointerField { + id + someFileField { + name + url + } + } + someRelationField { + edges { + node { + id + someFileField { + name + url + } + } + } + } + } + } + } + `, + variables: { + fields: { + someFileField: { upload: null }, + somePointerField: { + createAndLink: { + someFileField: { upload: null }, + }, + }, + someRelationField: { + createAndAdd: [ + { + someFileField: { upload: null }, + }, + ], + }, + }, + }, + }) + ); + body.append( + 'map', + JSON.stringify({ + 1: ['variables.fields.someFileField.upload'], + 2: ['variables.fields.somePointerField.createAndLink.someFileField.upload'], + 3: ['variables.fields.someRelationField.createAndAdd.0.someFileField.upload'], + }) + ); + body.append('1', 'My File Content someFileField', { + filename: 'someFileField.txt', + contentType: 'text/plain', + }); + body.append('2', 'My File Content somePointerField', { + filename: 'somePointerField.txt', + contentType: 'text/plain', + }); + body.append('3', 'My File Content someRelationField', { + filename: 'someRelationField.txt', + contentType: 'text/plain', + }); + + const res = await fetch('http://localhost:13377/graphql', { + method: 'POST', + headers, + body, + }); + expect(res.status).toEqual(200); + const result = await res.json(); + console.log(result); + expect(result.data.createSomeClass.someClass.someFileField.name).toEqual( + jasmine.stringMatching(/_someFileField.txt$/) + ); + expect(result.data.createSomeClass.someClass.somePointerField.someFileField.name).toEqual( + jasmine.stringMatching(/_somePointerField.txt$/) + ); + expect( + result.data.createSomeClass.someClass.someRelationField.edges[0].node.someFileField.name + ).toEqual(jasmine.stringMatching(/_someRelationField.txt$/)); + }); + + it('should not upload if file is too large', async () => { parseGraphQLServer.parseServer.config.maxUploadSize = '1kb'; const body = new FormData(); diff --git a/src/GraphQL/loaders/parseClassMutations.js b/src/GraphQL/loaders/parseClassMutations.js index 41d7c09dde..aa15f70505 100644 --- a/src/GraphQL/loaders/parseClassMutations.js +++ b/src/GraphQL/loaders/parseClassMutations.js @@ -82,6 +82,7 @@ const load = function (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseG const parseFields = await transformTypes('create', fields, { className, parseGraphQLSchema, + originalFields: args.fields, req: { config, auth, info }, }); @@ -190,6 +191,7 @@ const load = function (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseG const parseFields = await transformTypes('update', fields, { className, parseGraphQLSchema, + originalFields: args.fields, req: { config, auth, info }, }); diff --git a/src/GraphQL/loaders/usersMutations.js b/src/GraphQL/loaders/usersMutations.js index 183268a191..58236cdbd4 100644 --- a/src/GraphQL/loaders/usersMutations.js +++ b/src/GraphQL/loaders/usersMutations.js @@ -38,6 +38,7 @@ const load = parseGraphQLSchema => { const parseFields = await transformTypes('create', fields, { className: '_User', parseGraphQLSchema, + originalFields: args.fields, req: { config, auth, info }, }); @@ -114,6 +115,7 @@ const load = parseGraphQLSchema => { const parseFields = await transformTypes('create', fields, { className: '_User', parseGraphQLSchema, + originalFields: args.fields, req: { config, auth, info }, }); diff --git a/src/GraphQL/transformers/mutation.js b/src/GraphQL/transformers/mutation.js index 5b72d6f05d..17dd6a8d4b 100644 --- a/src/GraphQL/transformers/mutation.js +++ b/src/GraphQL/transformers/mutation.js @@ -7,7 +7,7 @@ import * as objectsMutations from '../helpers/objectsMutations'; const transformTypes = async ( inputType: 'create' | 'update', fields, - { className, parseGraphQLSchema, req } + { className, parseGraphQLSchema, req, originalFields } ) => { const { classGraphQLCreateType, @@ -44,13 +44,16 @@ const transformTypes = async ( fields[field] = transformers.polygon(fields[field]); break; case inputTypeField.type === defaultGraphQLTypes.FILE_INPUT: - fields[field] = await transformers.file(fields[field], req); + // Use `originalFields` to handle file upload since fields are a deepcopy and do not + // keep the file object + fields[field] = await transformers.file(originalFields[field], req); break; case parseClass.fields[field].type === 'Relation': fields[field] = await transformers.relation( parseClass.fields[field].targetClass, field, fields[field], + originalFields[field], parseGraphQLSchema, req ); @@ -64,6 +67,7 @@ const transformTypes = async ( parseClass.fields[field].targetClass, field, fields[field], + originalFields[field], parseGraphQLSchema, req ); @@ -135,7 +139,14 @@ const transformers = { } return parseACL; }, - relation: async (targetClass, field, value, parseGraphQLSchema, { config, auth, info }) => { + relation: async ( + targetClass, + field, + value, + originalValue, + parseGraphQLSchema, + { config, auth, info } + ) => { if (Object.keys(value).length === 0) throw new Parse.Error( Parse.Error.INVALID_POINTER, @@ -151,9 +162,10 @@ const transformers = { if (value.createAndAdd) { nestedObjectsToAdd = ( await Promise.all( - value.createAndAdd.map(async input => { + value.createAndAdd.map(async (input, i) => { const parseFields = await transformTypes('create', input, { className: targetClass, + originalFields: originalValue.createAndAdd[i], parseGraphQLSchema, req: { config, auth, info }, }); @@ -204,7 +216,14 @@ const transformers = { } return op; }, - pointer: async (targetClass, field, value, parseGraphQLSchema, { config, auth, info }) => { + pointer: async ( + targetClass, + field, + value, + originalValue, + parseGraphQLSchema, + { config, auth, info } + ) => { if (Object.keys(value).length > 1 || Object.keys(value).length === 0) throw new Parse.Error( Parse.Error.INVALID_POINTER, @@ -216,6 +235,7 @@ const transformers = { const parseFields = await transformTypes('create', value.createAndLink, { className: targetClass, parseGraphQLSchema, + originalFields: originalValue.createAndLink, req: { config, auth, info }, }); nestedObjectToAdd = await objectsMutations.createObject(