fix: GraphQL file upload fails in case of use of pointer or relation (#8721)
This commit is contained in:
@@ -6832,7 +6832,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
|
|
||||||
describe('Files Mutations', () => {
|
describe('Files Mutations', () => {
|
||||||
describe('Create', () => {
|
describe('Create', () => {
|
||||||
it_only_node_version('<17')('should return File object', async () => {
|
it('should return File object', async () => {
|
||||||
const clientMutationId = uuidv4();
|
const clientMutationId = uuidv4();
|
||||||
|
|
||||||
parseServer = await global.reconfigureServer({
|
parseServer = await global.reconfigureServer({
|
||||||
@@ -9298,7 +9298,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
expect(result6[0].node.name).toEqual('imACountry3');
|
expect(result6[0].node.name).toEqual('imACountry3');
|
||||||
});
|
});
|
||||||
|
|
||||||
it_only_node_version('<17')('should support files', async () => {
|
it('should support files', async () => {
|
||||||
try {
|
try {
|
||||||
parseServer = await global.reconfigureServer({
|
parseServer = await global.reconfigureServer({
|
||||||
publicServerURL: 'http://localhost:13377/parse',
|
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';
|
parseGraphQLServer.parseServer.config.maxUploadSize = '1kb';
|
||||||
|
|
||||||
const body = new FormData();
|
const body = new FormData();
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ const load = function (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseG
|
|||||||
const parseFields = await transformTypes('create', fields, {
|
const parseFields = await transformTypes('create', fields, {
|
||||||
className,
|
className,
|
||||||
parseGraphQLSchema,
|
parseGraphQLSchema,
|
||||||
|
originalFields: args.fields,
|
||||||
req: { config, auth, info },
|
req: { config, auth, info },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -190,6 +191,7 @@ const load = function (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseG
|
|||||||
const parseFields = await transformTypes('update', fields, {
|
const parseFields = await transformTypes('update', fields, {
|
||||||
className,
|
className,
|
||||||
parseGraphQLSchema,
|
parseGraphQLSchema,
|
||||||
|
originalFields: args.fields,
|
||||||
req: { config, auth, info },
|
req: { config, auth, info },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ const load = parseGraphQLSchema => {
|
|||||||
const parseFields = await transformTypes('create', fields, {
|
const parseFields = await transformTypes('create', fields, {
|
||||||
className: '_User',
|
className: '_User',
|
||||||
parseGraphQLSchema,
|
parseGraphQLSchema,
|
||||||
|
originalFields: args.fields,
|
||||||
req: { config, auth, info },
|
req: { config, auth, info },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -114,6 +115,7 @@ const load = parseGraphQLSchema => {
|
|||||||
const parseFields = await transformTypes('create', fields, {
|
const parseFields = await transformTypes('create', fields, {
|
||||||
className: '_User',
|
className: '_User',
|
||||||
parseGraphQLSchema,
|
parseGraphQLSchema,
|
||||||
|
originalFields: args.fields,
|
||||||
req: { config, auth, info },
|
req: { config, auth, info },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import * as objectsMutations from '../helpers/objectsMutations';
|
|||||||
const transformTypes = async (
|
const transformTypes = async (
|
||||||
inputType: 'create' | 'update',
|
inputType: 'create' | 'update',
|
||||||
fields,
|
fields,
|
||||||
{ className, parseGraphQLSchema, req }
|
{ className, parseGraphQLSchema, req, originalFields }
|
||||||
) => {
|
) => {
|
||||||
const {
|
const {
|
||||||
classGraphQLCreateType,
|
classGraphQLCreateType,
|
||||||
@@ -44,13 +44,16 @@ const transformTypes = async (
|
|||||||
fields[field] = transformers.polygon(fields[field]);
|
fields[field] = transformers.polygon(fields[field]);
|
||||||
break;
|
break;
|
||||||
case inputTypeField.type === defaultGraphQLTypes.FILE_INPUT:
|
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;
|
break;
|
||||||
case parseClass.fields[field].type === 'Relation':
|
case parseClass.fields[field].type === 'Relation':
|
||||||
fields[field] = await transformers.relation(
|
fields[field] = await transformers.relation(
|
||||||
parseClass.fields[field].targetClass,
|
parseClass.fields[field].targetClass,
|
||||||
field,
|
field,
|
||||||
fields[field],
|
fields[field],
|
||||||
|
originalFields[field],
|
||||||
parseGraphQLSchema,
|
parseGraphQLSchema,
|
||||||
req
|
req
|
||||||
);
|
);
|
||||||
@@ -64,6 +67,7 @@ const transformTypes = async (
|
|||||||
parseClass.fields[field].targetClass,
|
parseClass.fields[field].targetClass,
|
||||||
field,
|
field,
|
||||||
fields[field],
|
fields[field],
|
||||||
|
originalFields[field],
|
||||||
parseGraphQLSchema,
|
parseGraphQLSchema,
|
||||||
req
|
req
|
||||||
);
|
);
|
||||||
@@ -135,7 +139,14 @@ const transformers = {
|
|||||||
}
|
}
|
||||||
return parseACL;
|
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)
|
if (Object.keys(value).length === 0)
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.INVALID_POINTER,
|
Parse.Error.INVALID_POINTER,
|
||||||
@@ -151,9 +162,10 @@ const transformers = {
|
|||||||
if (value.createAndAdd) {
|
if (value.createAndAdd) {
|
||||||
nestedObjectsToAdd = (
|
nestedObjectsToAdd = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
value.createAndAdd.map(async input => {
|
value.createAndAdd.map(async (input, i) => {
|
||||||
const parseFields = await transformTypes('create', input, {
|
const parseFields = await transformTypes('create', input, {
|
||||||
className: targetClass,
|
className: targetClass,
|
||||||
|
originalFields: originalValue.createAndAdd[i],
|
||||||
parseGraphQLSchema,
|
parseGraphQLSchema,
|
||||||
req: { config, auth, info },
|
req: { config, auth, info },
|
||||||
});
|
});
|
||||||
@@ -204,7 +216,14 @@ const transformers = {
|
|||||||
}
|
}
|
||||||
return op;
|
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)
|
if (Object.keys(value).length > 1 || Object.keys(value).length === 0)
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.INVALID_POINTER,
|
Parse.Error.INVALID_POINTER,
|
||||||
@@ -216,6 +235,7 @@ const transformers = {
|
|||||||
const parseFields = await transformTypes('create', value.createAndLink, {
|
const parseFields = await transformTypes('create', value.createAndLink, {
|
||||||
className: targetClass,
|
className: targetClass,
|
||||||
parseGraphQLSchema,
|
parseGraphQLSchema,
|
||||||
|
originalFields: originalValue.createAndLink,
|
||||||
req: { config, auth, info },
|
req: { config, auth, info },
|
||||||
});
|
});
|
||||||
nestedObjectToAdd = await objectsMutations.createObject(
|
nestedObjectToAdd = await objectsMutations.createObject(
|
||||||
|
|||||||
Reference in New Issue
Block a user