Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Required option not handled correctly for special fields (File, GeoPoint, Polygon) on GraphQL API mutations #8915

Merged
134 changes: 101 additions & 33 deletions spec/ParseGraphQLServer.spec.js
Expand Up @@ -88,8 +88,8 @@

it('should initialize parseGraphQLSchema with a log controller', async () => {
const loggerAdapter = {
log: () => {},
error: () => {},
log: () => { },
mtrezza marked this conversation as resolved.
Show resolved Hide resolved
error: () => { },
};
const parseServer = await global.reconfigureServer({
loggerAdapter,
Expand Down Expand Up @@ -124,10 +124,10 @@
info: new Object(),
config: new Object(),
auth: new Object(),
get: () => {},
get: () => { },
};
const res = {
set: () => {},
set: () => { },
};

it("should return schema and context with req's info, config and auth", async () => {
Expand Down Expand Up @@ -473,8 +473,8 @@
},
},
});
spyOn(console, 'warn').and.callFake(() => {});
spyOn(console, 'error').and.callFake(() => {});
spyOn(console, 'warn').and.callFake(() => { });
spyOn(console, 'error').and.callFake(() => { });
});

afterEach(async () => {
Expand Down Expand Up @@ -853,7 +853,7 @@
});

it('should have clientMutationId in call function input', async () => {
Parse.Cloud.define('hello', () => {});
Parse.Cloud.define('hello', () => { });

const callFunctionInputFields = (
await apolloClient.query({
Expand All @@ -875,7 +875,7 @@
});

it('should have clientMutationId in call function payload', async () => {
Parse.Cloud.define('hello', () => {});
Parse.Cloud.define('hello', () => { });

const callFunctionPayloadFields = (
await apolloClient.query({
Expand Down Expand Up @@ -6541,7 +6541,7 @@
);
expect(
(await deleteObject(object4.className, object4.id)).data.delete[
object4.className.charAt(0).toLowerCase() + object4.className.slice(1)
object4.className.charAt(0).toLowerCase() + object4.className.slice(1)

Check failure on line 6544 in spec/ParseGraphQLServer.spec.js

View workflow job for this annotation

GitHub Actions / Lint

Expected indentation of 16 spaces but found 14
]
).toEqual({ objectId: object4.id, __typename: 'PublicClass' });
await expectAsync(object4.fetch({ useMasterKey: true })).toBeRejectedWith(
Expand Down Expand Up @@ -7432,9 +7432,9 @@
it('should send reset password', async () => {
const clientMutationId = uuidv4();
const emailAdapter = {
sendVerificationEmail: () => {},
sendVerificationEmail: () => { },
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
sendMail: () => { },
};
parseServer = await global.reconfigureServer({
appName: 'test',
Expand Down Expand Up @@ -7472,11 +7472,11 @@
const clientMutationId = uuidv4();
let resetPasswordToken;
const emailAdapter = {
sendVerificationEmail: () => {},
sendVerificationEmail: () => { },
sendPasswordResetEmail: ({ link }) => {
resetPasswordToken = link.split('token=')[1].split('&')[0];
},
sendMail: () => {},
sendMail: () => { },
};
parseServer = await global.reconfigureServer({
appName: 'test',
Expand Down Expand Up @@ -7541,9 +7541,9 @@
it('should send verification email again', async () => {
const clientMutationId = uuidv4();
const emailAdapter = {
sendVerificationEmail: () => {},
sendVerificationEmail: () => { },
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
sendMail: () => { },
};
parseServer = await global.reconfigureServer({
appName: 'test',
Expand Down Expand Up @@ -9548,6 +9548,74 @@
}
});

it('should support files on required file', async () => {
try {
parseServer = await global.reconfigureServer({
publicServerURL: 'http://localhost:13377/parse',
});

const schemaController = await parseServer.config.databaseController.loadSchema();
await schemaController.addClassIfNotExists('SomeClassWithRequiredFile', {
someField: { type: 'File', required: true },
});

await resetGraphQLCache();

await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();

const body = new FormData();
body.append(
'operations',
JSON.stringify({
query: `
mutation CreateSomeObject(
$fields: CreateSomeClassWithRequiredFileFieldsInput
) {
createSomeClassWithRequiredFile(
input: { fields: $fields }
) {
someClassWithRequiredFile {
id
someField {
name
url
}
}
}
}
`,
variables: {
fields: {
someField: { upload: null },
},
},
})
);
body.append('map', JSON.stringify({ 1: ['variables.fields.someField.upload'] }));
body.append('1', 'My File Content', {
filename: 'myFileName.txt',
contentType: 'text/plain',
});

const res = await fetch('http://localhost:13377/graphql', {
method: 'POST',
headers,
body,
});
expect(res.status).toEqual(200);
const resText = await res.text();
const result = JSON.parse(resText);
expect(
result.data.createSomeClassWithRequiredFile.someClassWithRequiredFile.someField.name
).toEqual(jasmine.stringMatching(/_myFileName.txt$/));
expect(
result.data.createSomeClassWithRequiredFile.someClassWithRequiredFile.someField.url
).toEqual(jasmine.stringMatching(/_myFileName.txt$/));
} catch (e) {
handleError(e);
}
});

it('should support file upload for on fly creation through pointer and relation', async () => {
parseServer = await global.reconfigureServer({
publicServerURL: 'http://localhost:13377/parse',
Expand Down Expand Up @@ -11074,25 +11142,25 @@
},
});
const SomeClassType = new GraphQLObjectType({
name: 'SomeClass',
fields: {
nameUpperCase: {
type: new GraphQLNonNull(GraphQLString),
resolve: p => p.name.toUpperCase(),
},
type: { type: TypeEnum },
language: {
type: new GraphQLEnumType({
name: 'LanguageEnum',
values: {
fr: { value: 'fr' },
en: { value: 'en' },
},
}),
resolve: () => 'fr',
},
name: 'SomeClass',

Check failure on line 11145 in spec/ParseGraphQLServer.spec.js

View workflow job for this annotation

GitHub Actions / Lint

Expected indentation of 12 spaces but found 10
fields: {

Check failure on line 11146 in spec/ParseGraphQLServer.spec.js

View workflow job for this annotation

GitHub Actions / Lint

Expected indentation of 12 spaces but found 10
nameUpperCase: {

Check failure on line 11147 in spec/ParseGraphQLServer.spec.js

View workflow job for this annotation

GitHub Actions / Lint

Expected indentation of 14 spaces but found 12
type: new GraphQLNonNull(GraphQLString),

Check failure on line 11148 in spec/ParseGraphQLServer.spec.js

View workflow job for this annotation

GitHub Actions / Lint

Expected indentation of 16 spaces but found 14
resolve: p => p.name.toUpperCase(),

Check failure on line 11149 in spec/ParseGraphQLServer.spec.js

View workflow job for this annotation

GitHub Actions / Lint

Expected indentation of 16 spaces but found 14
},

Check failure on line 11150 in spec/ParseGraphQLServer.spec.js

View workflow job for this annotation

GitHub Actions / Lint

Expected indentation of 14 spaces but found 12
}),
type: { type: TypeEnum },

Check failure on line 11151 in spec/ParseGraphQLServer.spec.js

View workflow job for this annotation

GitHub Actions / Lint

Expected indentation of 14 spaces but found 12
language: {

Check failure on line 11152 in spec/ParseGraphQLServer.spec.js

View workflow job for this annotation

GitHub Actions / Lint

Expected indentation of 14 spaces but found 12
type: new GraphQLEnumType({

Check failure on line 11153 in spec/ParseGraphQLServer.spec.js

View workflow job for this annotation

GitHub Actions / Lint

Expected indentation of 16 spaces but found 14
name: 'LanguageEnum',
values: {
fr: { value: 'fr' },
en: { value: 'en' },
},
}),
resolve: () => 'fr',
},
},
}),
parseGraphQLServer = new ParseGraphQLServer(parseServer, {
graphQLPath: '/graphql',
graphQLCustomTypeDefs: new GraphQLSchema({
Expand Down
18 changes: 9 additions & 9 deletions src/GraphQL/transformers/mutation.js
@@ -1,7 +1,6 @@
import Parse from 'parse/node';
import { fromGlobalId } from 'graphql-relay';
import { handleUpload } from '../loaders/filesMutations';
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
import * as objectsMutations from '../helpers/objectsMutations';

const transformTypes = async (
Expand All @@ -28,27 +27,28 @@ const transformTypes = async (
inputTypeField = classGraphQLUpdateTypeFields[field];
}
if (inputTypeField) {
switch (true) {
case inputTypeField.type === defaultGraphQLTypes.GEO_POINT_INPUT:
const parseFieldType = parseClass.fields[field].type;
switch (parseFieldType) {
case 'GeoPoint':
if (fields[field] === null) {
fields[field] = { __op: 'Delete' };
break;
}
fields[field] = transformers.geoPoint(fields[field]);
break;
case inputTypeField.type === defaultGraphQLTypes.POLYGON_INPUT:
case 'Polygon':
if (fields[field] === null) {
fields[field] = { __op: 'Delete' };
break;
}
fields[field] = transformers.polygon(fields[field]);
break;
case inputTypeField.type === defaultGraphQLTypes.FILE_INPUT:
// Use `originalFields` to handle file upload since fields are a deepcopy and do not
// keep the file object
case 'File':
// We need to use the originalFields to handle the 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':
case 'Relation':
fields[field] = await transformers.relation(
parseClass.fields[field].targetClass,
field,
Expand All @@ -58,7 +58,7 @@ const transformTypes = async (
req
);
break;
case parseClass.fields[field].type === 'Pointer':
case 'Pointer':
if (fields[field] === null) {
fields[field] = { __op: 'Delete' };
break;
Expand Down