Skip to content

GraphQL { functions { call } } generic mutation #5818

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 };