Skip to content

Commit

Permalink
GraphQL { functions { call } } generic mutation (parse-community#5818)
Browse files Browse the repository at this point in the history
* Generic call function mutation
* Change function return type to any
* First passing test
* Testing errors
* Testing different data types
  • Loading branch information
davimacedo authored and douglasmuraoka committed Jul 18, 2019
1 parent 1d8fa76 commit de0e35e
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 0 deletions.
143 changes: 143 additions & 0 deletions spec/ParseGraphQLServer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3372,6 +3372,149 @@ describe('ParseGraphQLServer', () => {
});
});

describe('Functions Mutations', () => {
it('can be called', async () => {
Parse.Cloud.define('hello', async () => {
return 'Hello world!';
});

const result = await apolloClient.mutate({
mutation: gql`
mutation CallFunction {
functions {
call(functionName: "hello")
}
}
`,
});

expect(result.data.functions.call).toEqual('Hello world!');
});

it('can throw errors', async () => {
Parse.Cloud.define('hello', async () => {
throw new Error('Some error message.');
});

try {
await apolloClient.mutate({
mutation: gql`
mutation CallFunction {
functions {
call(functionName: "hello")
}
}
`,
});
fail('Should throw an error');
} catch (e) {
const { graphQLErrors } = e;
expect(graphQLErrors.length).toBe(1);
expect(graphQLErrors[0].message).toBe('Some error message.');
}
});

it('should accept different params', done => {
Parse.Cloud.define('hello', async req => {
expect(req.params.date instanceof Date).toBe(true);
expect(req.params.date.getTime()).toBe(1463907600000);
expect(req.params.dateList[0] instanceof Date).toBe(true);
expect(req.params.dateList[0].getTime()).toBe(1463907600000);
expect(req.params.complexStructure.date[0] instanceof Date).toBe(
true
);
expect(req.params.complexStructure.date[0].getTime()).toBe(
1463907600000
);
expect(
req.params.complexStructure.deepDate.date[0] instanceof Date
).toBe(true);
expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe(
1463907600000
);
expect(
req.params.complexStructure.deepDate2[0].date instanceof Date
).toBe(true);
expect(
req.params.complexStructure.deepDate2[0].date.getTime()
).toBe(1463907600000);
// Regression for #2294
expect(req.params.file instanceof Parse.File).toBe(true);
expect(req.params.file.url()).toEqual('https://some.url');
// Regression for #2204
expect(req.params.array).toEqual(['a', 'b', 'c']);
expect(Array.isArray(req.params.array)).toBe(true);
expect(req.params.arrayOfArray).toEqual([
['a', 'b', 'c'],
['d', 'e', 'f'],
]);
expect(Array.isArray(req.params.arrayOfArray)).toBe(true);
expect(Array.isArray(req.params.arrayOfArray[0])).toBe(true);
expect(Array.isArray(req.params.arrayOfArray[1])).toBe(true);

done();
});

const params = {
date: {
__type: 'Date',
iso: '2016-05-22T09:00:00.000Z',
},
dateList: [
{
__type: 'Date',
iso: '2016-05-22T09:00:00.000Z',
},
],
lol: 'hello',
complexStructure: {
date: [
{
__type: 'Date',
iso: '2016-05-22T09:00:00.000Z',
},
],
deepDate: {
date: [
{
__type: 'Date',
iso: '2016-05-22T09:00:00.000Z',
},
],
},
deepDate2: [
{
date: {
__type: 'Date',
iso: '2016-05-22T09:00:00.000Z',
},
},
],
},
file: Parse.File.fromJSON({
__type: 'File',
name: 'name',
url: 'https://some.url',
}),
array: ['a', 'b', 'c'],
arrayOfArray: [['a', 'b', 'c'], ['d', 'e', 'f']],
};

apolloClient.mutate({
mutation: gql`
mutation CallFunction($params: Object) {
functions {
call(functionName: "hello", params: $params)
}
}
`,
variables: {
params,
},
});
});
});

describe('Data Types', () => {
it('should support String', async () => {
const someFieldValue = 'some string';
Expand Down
2 changes: 2 additions & 0 deletions src/GraphQL/loaders/defaultGraphQLMutations.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import * as objectsMutations from './objectsMutations';
import * as filesMutations from './filesMutations';
import * as usersMutations from './usersMutations';
import * as functionsMutations from './functionsMutations';

const load = parseGraphQLSchema => {
objectsMutations.load(parseGraphQLSchema);
filesMutations.load(parseGraphQLSchema);
usersMutations.load(parseGraphQLSchema);
functionsMutations.load(parseGraphQLSchema);
};

export { load };
57 changes: 57 additions & 0 deletions src/GraphQL/loaders/functionsMutations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { GraphQLObjectType, GraphQLNonNull, GraphQLString } from 'graphql';
import { FunctionsRouter } from '../../Routers/FunctionsRouter';
import * as defaultGraphQLTypes from './defaultGraphQLTypes';

const load = parseGraphQLSchema => {
const fields = {};

fields.call = {
description:
'The call mutation can be used to invoke a cloud code function.',
args: {
functionName: {
description: 'This is the name of the function to be called.',
type: new GraphQLNonNull(GraphQLString),
},
params: {
description: 'These are the params to be passed to the function.',
type: defaultGraphQLTypes.OBJECT,
},
},
type: defaultGraphQLTypes.ANY,
async resolve(_source, args, context) {
try {
const { functionName, params } = args;
const { config, auth, info } = context;

return (await FunctionsRouter.handleCloudFunction({
params: {
functionName,
},
config,
auth,
info,
body: params,
})).response.result;
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
};

const functionsMutation = new GraphQLObjectType({
name: 'FunctionsMutation',
description:
'FunctionsMutation is the top level type for functions mutations.',
fields,
});
parseGraphQLSchema.graphQLTypes.push(functionsMutation);

parseGraphQLSchema.graphQLMutations.functions = {
description: 'This is the top level for functions mutations.',
type: functionsMutation,
resolve: () => new Object(),
};
};

export { load };

0 comments on commit de0e35e

Please sign in to comment.