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

GraphQL { functions { call } } generic mutation #5818

Merged

Conversation

davimacedo
Copy link
Member

This PR empowers the GraphQL API with the ability to call cloud code functions through a generic mutation:

mutation CallFunction {
  functions {
    call(functionName: "myFunction" params: { someParam: "someValue" })
  }
}

@davimacedo
Copy link
Member Author

@omairvaiyani if you have a chance, please take a look.

@acinader @dplewis I'm not sure if you guys had the chance to play around with the GraphQL API, but, of course, feel free to also review.

@codecov
Copy link

codecov bot commented Jul 16, 2019

Codecov Report

Merging #5818 into master will increase coverage by 0.02%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #5818      +/-   ##
==========================================
+ Coverage   93.66%   93.68%   +0.02%     
==========================================
  Files         146      147       +1     
  Lines       10236    10253      +17     
==========================================
+ Hits         9588     9606      +18     
+ Misses        648      647       -1
Impacted Files Coverage Δ
src/GraphQL/loaders/functionsMutations.js 100% <100%> (ø)
src/GraphQL/loaders/defaultGraphQLMutations.js 100% <100%> (ø) ⬆️
src/RestWrite.js 93.51% <0%> (+0.17%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1b5c045...30722b9. Read the comment docs.

Copy link
Contributor

@douglasmuraoka douglasmuraoka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to have a test where we verify the error message when we call a function that doesn't exist.

That's all, LGTM! :)

@douglasmuraoka douglasmuraoka merged commit 0b86a86 into parse-community:master Jul 18, 2019
douglasmuraoka pushed a commit that referenced this pull request Jul 18, 2019
This PR empowers the Parse GraphQL API with custom user-defined schema. The developers can now write their own types, queries, and mutations, which will merged with the ones that are automatically generated. The new types are resolved by the application's cloud code functions.

Therefore, regarding #5777, this PR closes the cloud functions needs and also addresses the graphql customization topic. In my view, I think that this PR, together with #5782 and #5818, when merged, closes the issue.

How it works:

1. When initializing ParseGraphQLServer, now the developer can pass a custom schema that will be merged to the auto-generated one:
```
      parseGraphQLServer = new ParseGraphQLServer(parseServer, {
        graphQLPath: '/graphql',
        graphQLCustomTypeDefs: gql`
          extend type Query {
            custom: Custom @namespace
          }
           type Custom {
            hello: String @resolve
            hello2: String @resolve(to: "hello")
            userEcho(user: _UserFields!): _UserClass! @resolve
          }
        `,
      });
```

Note:
- This PR includes a @namespace directive that can be used to the top level field of the nested queries and mutations (it basically just returns an empty object);
- This PR includes a @resolve directive that can be used to notify the Parse GraphQL Server to resolve that field using a cloud code function. The `to` argument specifies the function name. If the `to` argument is not passed, the Parse GraphQL Server will look for a function with the same name of the field;
- This PR allows creating custom types using the auto-generated ones as in `userEcho(user: _UserFields!): _UserClass! @resolve`;
- This PR allows to extend the auto-generated types, as in `extend type Query { ... }`.

2. Once the schema was set, you just need to write regular cloud code functions:
```
      Parse.Cloud.define('hello', async () => {
        return 'Hello world!';
      });

      Parse.Cloud.define('userEcho', async req => {
        return req.params.user;
      });
```

3. Now you are ready to play with your new custom api:
```
query {
  custom {
    hello
    hello2
    userEcho(user: { username: "somefolk" }) {
      username
    }
  }
}
```
should return
```
{
  "data": {
    "custom": {
      "hello": "Hello world!",
      "hello2": "Hello world!",
      "userEcho": {
        "username": "somefolk"
      }
    }
  }
}
```
@omairvaiyani
Copy link
Contributor

Looks great!

Just a suggestion perhaps for later PRs, do you plan to add an option for pre-filling a list of defined functions under function. I know your [PR here](GraphQL Custom Schema #5821) allows users to specify which functions to explicitly show, but this pre-fill option would also be great for users to get started. Also a future discussion point that makes sense to think about here with this PR, could we extend the Cloud Function define method to also add params and response types that graphql could use to generate input and result fields for better dev experience?

@davimacedo
Copy link
Member Author

Hi @omairvaiyani

Thanks for your feedback.

At first glance, I was willed to list all functions. But I've realized that it would not be so useful to list them just like functionName(params: Object): Any. There is no advantage (no auto-complete, no data validation, no data selection) in using this version instead of the generic call(functionName: String! params: Object): Any. Thats why I turned out opening #5821 .

To make it useful, the developer needs to specify the input and output types (like you suggested) and it can be already done using the custom schemas. I also thought about changing the Parse.Cloud.define to allow the customization there. But the developers would need to use the schema.graphql anyways if they wanted to receive or return more complex data types. That's why I preferred to start with the schema customization approach.

But I think we can still open a new PR with the Parse.Cloud.define options. It may be more convenient for developers with not so much experience in GraphQL. What are your thoughts about how it should work? Something like this?

Parse.Cloud.define(
  'hello',
  async req => {
    return `Hello world, ${req.params.name}!`;
  },
  null,
  {
    namespace: 'mutation.functions', // optional - default to "mutation.functions"
    name: 'hello', // optional - default to function name - can be used when the function name does not fit GraphQL rules
    description: 'This is my hello query', // optional - we can have a default message here
    args: { // optional - default to Object
      name: new GraphQLNonNull(GraphQLString)
    }, 
    type: new GraphQLNonNull(GraphQLString) // optional - default to Any
  }
);

UnderratedDev pushed a commit to UnderratedDev/parse-server that referenced this pull request Mar 21, 2020
* Generic call function mutation
* Change function return type to any
* First passing test
* Testing errors
* Testing different data types
UnderratedDev pushed a commit to UnderratedDev/parse-server that referenced this pull request Mar 21, 2020
This PR empowers the Parse GraphQL API with custom user-defined schema. The developers can now write their own types, queries, and mutations, which will merged with the ones that are automatically generated. The new types are resolved by the application's cloud code functions.

Therefore, regarding parse-community#5777, this PR closes the cloud functions needs and also addresses the graphql customization topic. In my view, I think that this PR, together with parse-community#5782 and parse-community#5818, when merged, closes the issue.

How it works:

1. When initializing ParseGraphQLServer, now the developer can pass a custom schema that will be merged to the auto-generated one:
```
      parseGraphQLServer = new ParseGraphQLServer(parseServer, {
        graphQLPath: '/graphql',
        graphQLCustomTypeDefs: gql`
          extend type Query {
            custom: Custom @namespace
          }
           type Custom {
            hello: String @resolve
            hello2: String @resolve(to: "hello")
            userEcho(user: _UserFields!): _UserClass! @resolve
          }
        `,
      });
```

Note:
- This PR includes a @namespace directive that can be used to the top level field of the nested queries and mutations (it basically just returns an empty object);
- This PR includes a @resolve directive that can be used to notify the Parse GraphQL Server to resolve that field using a cloud code function. The `to` argument specifies the function name. If the `to` argument is not passed, the Parse GraphQL Server will look for a function with the same name of the field;
- This PR allows creating custom types using the auto-generated ones as in `userEcho(user: _UserFields!): _UserClass! @resolve`;
- This PR allows to extend the auto-generated types, as in `extend type Query { ... }`.

2. Once the schema was set, you just need to write regular cloud code functions:
```
      Parse.Cloud.define('hello', async () => {
        return 'Hello world!';
      });

      Parse.Cloud.define('userEcho', async req => {
        return req.params.user;
      });
```

3. Now you are ready to play with your new custom api:
```
query {
  custom {
    hello
    hello2
    userEcho(user: { username: "somefolk" }) {
      username
    }
  }
}
```
should return
```
{
  "data": {
    "custom": {
      "hello": "Hello world!",
      "hello2": "Hello world!",
      "userEcho": {
        "username": "somefolk"
      }
    }
  }
}
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants