Skip to content

Commit

Permalink
fix: GraphQL file upload fails in case of use of pointer or relation (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Moumouls authored Feb 14, 2024
1 parent 6f21195 commit 1aba638
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 8 deletions.
114 changes: 111 additions & 3 deletions spec/ParseGraphQLServer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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();
Expand Down
2 changes: 2 additions & 0 deletions src/GraphQL/loaders/parseClassMutations.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
});

Expand Down Expand Up @@ -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 },
});

Expand Down
2 changes: 2 additions & 0 deletions src/GraphQL/loaders/usersMutations.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const load = parseGraphQLSchema => {
const parseFields = await transformTypes('create', fields, {
className: '_User',
parseGraphQLSchema,
originalFields: args.fields,
req: { config, auth, info },
});

Expand Down Expand Up @@ -114,6 +115,7 @@ const load = parseGraphQLSchema => {
const parseFields = await transformTypes('create', fields, {
className: '_User',
parseGraphQLSchema,
originalFields: args.fields,
req: { config, auth, info },
});

Expand Down
30 changes: 25 additions & 5 deletions src/GraphQL/transformers/mutation.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
);
Expand All @@ -64,6 +67,7 @@ const transformTypes = async (
parseClass.fields[field].targetClass,
field,
fields[field],
originalFields[field],
parseGraphQLSchema,
req
);
Expand Down Expand Up @@ -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,
Expand All @@ -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 },
});
Expand Down Expand Up @@ -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,
Expand All @@ -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(
Expand Down

0 comments on commit 1aba638

Please sign in to comment.