Skip to content

Commit 0b86a86

Browse files
davimacedodouglasmuraoka
authored andcommitted
GraphQL { functions { call } } generic mutation (#5818)
* Generic call function mutation * Change function return type to any * First passing test * Testing errors * Testing different data types
1 parent 6be1533 commit 0b86a86

File tree

3 files changed

+202
-0
lines changed

3 files changed

+202
-0
lines changed

spec/ParseGraphQLServer.spec.js

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3372,6 +3372,149 @@ describe('ParseGraphQLServer', () => {
33723372
});
33733373
});
33743374

3375+
describe('Functions Mutations', () => {
3376+
it('can be called', async () => {
3377+
Parse.Cloud.define('hello', async () => {
3378+
return 'Hello world!';
3379+
});
3380+
3381+
const result = await apolloClient.mutate({
3382+
mutation: gql`
3383+
mutation CallFunction {
3384+
functions {
3385+
call(functionName: "hello")
3386+
}
3387+
}
3388+
`,
3389+
});
3390+
3391+
expect(result.data.functions.call).toEqual('Hello world!');
3392+
});
3393+
3394+
it('can throw errors', async () => {
3395+
Parse.Cloud.define('hello', async () => {
3396+
throw new Error('Some error message.');
3397+
});
3398+
3399+
try {
3400+
await apolloClient.mutate({
3401+
mutation: gql`
3402+
mutation CallFunction {
3403+
functions {
3404+
call(functionName: "hello")
3405+
}
3406+
}
3407+
`,
3408+
});
3409+
fail('Should throw an error');
3410+
} catch (e) {
3411+
const { graphQLErrors } = e;
3412+
expect(graphQLErrors.length).toBe(1);
3413+
expect(graphQLErrors[0].message).toBe('Some error message.');
3414+
}
3415+
});
3416+
3417+
it('should accept different params', done => {
3418+
Parse.Cloud.define('hello', async req => {
3419+
expect(req.params.date instanceof Date).toBe(true);
3420+
expect(req.params.date.getTime()).toBe(1463907600000);
3421+
expect(req.params.dateList[0] instanceof Date).toBe(true);
3422+
expect(req.params.dateList[0].getTime()).toBe(1463907600000);
3423+
expect(req.params.complexStructure.date[0] instanceof Date).toBe(
3424+
true
3425+
);
3426+
expect(req.params.complexStructure.date[0].getTime()).toBe(
3427+
1463907600000
3428+
);
3429+
expect(
3430+
req.params.complexStructure.deepDate.date[0] instanceof Date
3431+
).toBe(true);
3432+
expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe(
3433+
1463907600000
3434+
);
3435+
expect(
3436+
req.params.complexStructure.deepDate2[0].date instanceof Date
3437+
).toBe(true);
3438+
expect(
3439+
req.params.complexStructure.deepDate2[0].date.getTime()
3440+
).toBe(1463907600000);
3441+
// Regression for #2294
3442+
expect(req.params.file instanceof Parse.File).toBe(true);
3443+
expect(req.params.file.url()).toEqual('https://some.url');
3444+
// Regression for #2204
3445+
expect(req.params.array).toEqual(['a', 'b', 'c']);
3446+
expect(Array.isArray(req.params.array)).toBe(true);
3447+
expect(req.params.arrayOfArray).toEqual([
3448+
['a', 'b', 'c'],
3449+
['d', 'e', 'f'],
3450+
]);
3451+
expect(Array.isArray(req.params.arrayOfArray)).toBe(true);
3452+
expect(Array.isArray(req.params.arrayOfArray[0])).toBe(true);
3453+
expect(Array.isArray(req.params.arrayOfArray[1])).toBe(true);
3454+
3455+
done();
3456+
});
3457+
3458+
const params = {
3459+
date: {
3460+
__type: 'Date',
3461+
iso: '2016-05-22T09:00:00.000Z',
3462+
},
3463+
dateList: [
3464+
{
3465+
__type: 'Date',
3466+
iso: '2016-05-22T09:00:00.000Z',
3467+
},
3468+
],
3469+
lol: 'hello',
3470+
complexStructure: {
3471+
date: [
3472+
{
3473+
__type: 'Date',
3474+
iso: '2016-05-22T09:00:00.000Z',
3475+
},
3476+
],
3477+
deepDate: {
3478+
date: [
3479+
{
3480+
__type: 'Date',
3481+
iso: '2016-05-22T09:00:00.000Z',
3482+
},
3483+
],
3484+
},
3485+
deepDate2: [
3486+
{
3487+
date: {
3488+
__type: 'Date',
3489+
iso: '2016-05-22T09:00:00.000Z',
3490+
},
3491+
},
3492+
],
3493+
},
3494+
file: Parse.File.fromJSON({
3495+
__type: 'File',
3496+
name: 'name',
3497+
url: 'https://some.url',
3498+
}),
3499+
array: ['a', 'b', 'c'],
3500+
arrayOfArray: [['a', 'b', 'c'], ['d', 'e', 'f']],
3501+
};
3502+
3503+
apolloClient.mutate({
3504+
mutation: gql`
3505+
mutation CallFunction($params: Object) {
3506+
functions {
3507+
call(functionName: "hello", params: $params)
3508+
}
3509+
}
3510+
`,
3511+
variables: {
3512+
params,
3513+
},
3514+
});
3515+
});
3516+
});
3517+
33753518
describe('Data Types', () => {
33763519
it('should support String', async () => {
33773520
const someFieldValue = 'some string';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import * as objectsMutations from './objectsMutations';
22
import * as filesMutations from './filesMutations';
33
import * as usersMutations from './usersMutations';
4+
import * as functionsMutations from './functionsMutations';
45

56
const load = parseGraphQLSchema => {
67
objectsMutations.load(parseGraphQLSchema);
78
filesMutations.load(parseGraphQLSchema);
89
usersMutations.load(parseGraphQLSchema);
10+
functionsMutations.load(parseGraphQLSchema);
911
};
1012

1113
export { load };
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { GraphQLObjectType, GraphQLNonNull, GraphQLString } from 'graphql';
2+
import { FunctionsRouter } from '../../Routers/FunctionsRouter';
3+
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
4+
5+
const load = parseGraphQLSchema => {
6+
const fields = {};
7+
8+
fields.call = {
9+
description:
10+
'The call mutation can be used to invoke a cloud code function.',
11+
args: {
12+
functionName: {
13+
description: 'This is the name of the function to be called.',
14+
type: new GraphQLNonNull(GraphQLString),
15+
},
16+
params: {
17+
description: 'These are the params to be passed to the function.',
18+
type: defaultGraphQLTypes.OBJECT,
19+
},
20+
},
21+
type: defaultGraphQLTypes.ANY,
22+
async resolve(_source, args, context) {
23+
try {
24+
const { functionName, params } = args;
25+
const { config, auth, info } = context;
26+
27+
return (await FunctionsRouter.handleCloudFunction({
28+
params: {
29+
functionName,
30+
},
31+
config,
32+
auth,
33+
info,
34+
body: params,
35+
})).response.result;
36+
} catch (e) {
37+
parseGraphQLSchema.handleError(e);
38+
}
39+
},
40+
};
41+
42+
const functionsMutation = new GraphQLObjectType({
43+
name: 'FunctionsMutation',
44+
description:
45+
'FunctionsMutation is the top level type for functions mutations.',
46+
fields,
47+
});
48+
parseGraphQLSchema.graphQLTypes.push(functionsMutation);
49+
50+
parseGraphQLSchema.graphQLMutations.functions = {
51+
description: 'This is the top level for functions mutations.',
52+
type: functionsMutation,
53+
resolve: () => new Object(),
54+
};
55+
};
56+
57+
export { load };

0 commit comments

Comments
 (0)