From 7e5b81224e86c8dcc39b0ab9048d27157b7e8a1b Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Fri, 28 Jan 2022 18:20:30 +0800 Subject: [PATCH 1/6] feat: add create graphql migration Signed-off-by: Brent Hoover --- guides/create-graphql-mutation.md | 230 ++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 guides/create-graphql-mutation.md diff --git a/guides/create-graphql-mutation.md b/guides/create-graphql-mutation.md new file mode 100644 index 0000000..2b9bb5f --- /dev/null +++ b/guides/create-graphql-mutation.md @@ -0,0 +1,230 @@ +# How To: Create a new GraphQL mutation + +## Step 1: Identify which plugin owns the mutation + +The complete Reaction Commerce GraphQL API is created by stitching together domain-specific APIs from all of the API plugins. So when adding a new mutation, the first step is to decide which plugin should own it. This is usually obvious, but not always. You should think about whether any other plugins or services will need to call your mutation. If the mutation is fundamental to the system, then it may need to go in the "core" plugin, if no better alternative exists. + +## Step 2: Understand the difference between a plugin mutation function and a GraphQL mutation resolver + +See [Resolver Mutations and Queries vs. Plugin Mutations and Queries](../guides/developers-guide/core/developing-graphql.md#resolver-mutations-and-queries-vs-plugin-mutations-and-queries) + +## Step 3: Define the mutation in the schema + +1. If it doesn't already exist, create `schemas` folder in the plugin, and add an `index.js` file there. +1. If it doesn't already exist, create `schema.graphql` in `schemas` in the plugin. +1. Import the GraphQL file into `index.js` and default export it in an array: + + ```js + import importAsString from "@reactioncommerce/api-utils/importAsString.js"; + + const schema = importAsString("./schema.graphql"); + + export default [schema]; + ``` + + > NOTE: For large plugins, you can split to multiple `.graphql` files and export a multi-item array. + +1. In the `.graphql` file, add your mutation within `extend type Mutation { }`. Add an `extend type Mutation` section near the top if the file doesn't have it yet. +1. Follow [the Relay recommendations](https://facebook.github.io/relay/graphql/mutations.htm) for mutation input arguments, which is to have only one argument named `input` that takes an input type that is the capitalized mutation name plus the suffix "Input", and to return a type that is the capitalized mutation name plus the suffix "Payload". + + Example: `addAccountEmailRecord(input: AddAccountEmailRecordInput!): AddAccountEmailRecordPayload` + +1. Add the Input and Payload types to the schema. Both must have `clientMutationId: String` field and may have any other fields as necessary. The mutation response payload should include whatever object was mutated. +1. Document your mutation, the new types, and all fields in those types using string literals. See [Documenting a GraphQL Schema](../guides/developers-guide/core/developing-graphql.md#documenting-a-graphql-schema). +1. If not already done, register your schemas in the plugin's `index.js` file: + + ```js + import schemas from "./schemas"; + + export default async function register(app) { + await app.registerPlugin({ + graphQL: { + schemas + }, + // other props + }); + } + ``` + +## Step 4: Create the plugin mutation file + +1. If it doesn't already exist, create `mutations` folder in the plugin, and add an `index.js` file there. +2. In `mutations`, create a file for the mutation, e.g. `createSomething.js` for the `createSomething` mutation. The file should look something like this: + +```js +import Logger from "@reactioncommerce/logger"; + +/** + * @method createSomething + * @summary TODO + * @param {Object} context - an object containing the per-request state + * @return {Promise} TODO + */ +export default async function createSomething(context) { + Logger.info("createSomething mutation is not yet implemented"); + return null; +} +``` + +## Step 5: Add the plugin mutation to the mutations context + +In `mutations/index.js` in the plugin, import your mutation and add it to the default export object. Example: + +```js +import createSomething from "./createSomething" + +export default { + createSomething +}; +``` + +If this is the first mutation for the plugin, you'll also need to pass the full `mutations` object to `registerPlugin` in the plugin's `index.js` file: + +```js +import mutations from "./mutations"; + +export default async function register(app) { + await app.registerPlugin({ + mutations, + // other props + }); +} +``` + +Your plugin mutation function is now available in the GraphQL context as `context.mutations.createSomething`. + +> NOTE: The mutations objects from all plugins are merged, so be sure that another plugin does not have a mutation with the same name. The last one registered with that name will win, and plugins are generally registered in alphabetical order by plugin name. Tip: You can use this to your advantage if you want to override the mutation function of a core plugin without modifying core code. + +## Step 6: Add a test file for your mutation + +If your mutation is in a file named `createSomething.js`, your Jest tests should be in a file named `createSomething.test.js` in the same folder. Initially you can copy and paste the following test: + +```js +import mockContext from "/imports/test-utils/helpers/mockContext"; +import createSomething from "./createSomething"; + +test("expect to return a Promise that resolves to null", async () => { + const result = await createSomething(mockContext); + expect(result).toEqual(null); +}); +``` + +## Step 7: Create the GraphQL mutation resolver file + +1. If it doesn't already exist, create `resolvers` folder in the plugin, and add an `index.js` file there. +2. If it doesn't already exist, create `resolvers/Mutation` folder in the plugin, and add an `index.js` file there. "Mutation" must be capitalized. +3. In `resolvers/Mutation`, create a file for the mutation resolver, e.g. `createSomething.js` for the `createSomething` mutation. The file should look something like this initially: + +```js +/** + * @name "Mutation.createSomething" + * @method + * @memberof MyPlugin/GraphQL + * @summary resolver for the createSomething GraphQL mutation + * @param {Object} parentResult - unused + * @param {Object} args.input - an object of all mutation arguments that were sent by the client + * @param {String} [args.input.clientMutationId] - An optional string identifying the mutation call + * @param {Object} context - an object containing the per-request state + * @return {Promise} CreateSomethingPayload + */ +export default async function createSomething(parentResult, { input }, context) { + const { clientMutationId = null } = input; + // TODO: decode incoming IDs here + const renameMe = await context.mutations.createSomething(context); + return { + renameMe, + clientMutationId + }; +} +``` + +Make adjustments to the resolver function so that it reads and passes along the parameters correctly. The general pattern is: +- Destructure `input`. Include `clientMutationId`. +- Decode any opaque IDs that are in the input object +- Call `context.mutations.` (your new plugin mutation) with the necessary arguments, and `await` a response. +- Return an object that contains the `clientMutationId` and the object returned by the plugin mutation. (This must match the "Payload" type from the schema.) + +## Step 8: Register the resolver + +In `resolvers/Mutation/index.js` in the plugin, import your mutation resolver and add it to the default export object. Example: + +```js +import createSomething from "./createSomething" + +export default { + createSomething +}; +``` + +If this is the first mutation for the plugin, you'll also need to import the `Mutation` object into the `resolvers` object. In `resolvers/index.js` in the plugin, import `Mutation` and add it to the default export object. + +```js +import Mutation from "./Mutation" + +export default { + Mutation +}; +``` + +Then pass the full `resolvers` object to `registerPlugin` in the plugin's `index.js` file: + +```js +import resolvers from "./resolvers"; +import schemas from "./schemas"; + +export default async function register(app) { + await app.registerPlugin({ + graphQL: { + resolvers, + schemas + }, + // other props + }); +} +``` + +Calling your mutation with GraphQL should now work. + +## Step 9: Add a test file for your mutation resolver + +If your mutation resolver is in a file named `createSomething.js`, your Jest tests should be in a file named `createSomething.test.js` in the same folder. Initially you can copy and paste the following test: + +```js +import createSomething from "./createSomething"; + +test("correctly passes through to mutations.createSomething", async () => { + const fakeResult = { /* TODO */ }; + + const mockMutation = jest.fn().mockName("mutations.createSomething"); + mockMutation.mockReturnValueOnce(Promise.resolve(fakeResult)); + const context = { + mutations: { + createSomething: mockMutation + } + }; + + const result = await createSomething(null, { + input: { + /* TODO */ + clientMutationId: "clientMutationId" + } + }, context); + + expect(result).toEqual({ + renameMe: fakeResult, + clientMutationId: "clientMutationId" + }); +}); +``` + +This of course should be updated with tests that are appropriate for whatever your mutation resolver does. For example, verify that all ID and schema transformations happen. + +## Step 10: Finish implementing your mutation + +Adjust the mutation function and the mutation resolver function until they work as expected, with tests that prove it. This will likely involve adding additional input fields, ID transformations, permission checks, MongoDB calls, and event emitting. + +Refer to [Developing the GraphQL API](../docs/developing-graphql.md) for answers to any questions you might have while implementing your mutation. + +## Step 11: Update the JSDoc comments + +Write/update jsdoc comments for the plugin mutation function, the mutation resolver, and any util functions. The resolver function must have `@memberof /GraphQL` in the jsdoc, and the `@name` must be the full GraphQL schema path in quotation marks, e.g., "Mutation.createSomething". (The quotation marks are necessary for the output API documentation to be correct due to the periods.) From 60c857aed3fa420133c1cadc48cd051959edf701 Mon Sep 17 00:00:00 2001 From: Alvaro Bueno Date: Fri, 4 Feb 2022 13:51:36 -0500 Subject: [PATCH 2/6] fix: giving proper format to guide on GraphQL queries Signed-off-by: Alvaro Bueno --- guides/create-graphql-mutation.md | 33 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/guides/create-graphql-mutation.md b/guides/create-graphql-mutation.md index 2b9bb5f..7b32103 100644 --- a/guides/create-graphql-mutation.md +++ b/guides/create-graphql-mutation.md @@ -1,14 +1,15 @@ -# How To: Create a new GraphQL mutation +## At a glance -## Step 1: Identify which plugin owns the mutation +In this guide, we’ll guide you through the steps you need to create a successful mutation in GraphQL with Mailchimp Open Commerce +## Identify which plugin owns the mutation -The complete Reaction Commerce GraphQL API is created by stitching together domain-specific APIs from all of the API plugins. So when adding a new mutation, the first step is to decide which plugin should own it. This is usually obvious, but not always. You should think about whether any other plugins or services will need to call your mutation. If the mutation is fundamental to the system, then it may need to go in the "core" plugin, if no better alternative exists. +When adding a new mutation, the first step is to decide which plugin should own it. This is usually obvious, but not always. You should think about whether any other plugins or services will need to call your mutation. If the mutation is fundamental to the system, then it may need to go in the "core" plugin, if no better alternative exists. -## Step 2: Understand the difference between a plugin mutation function and a GraphQL mutation resolver +## Understand the difference between a plugin mutation function and a GraphQL mutation resolver See [Resolver Mutations and Queries vs. Plugin Mutations and Queries](../guides/developers-guide/core/developing-graphql.md#resolver-mutations-and-queries-vs-plugin-mutations-and-queries) -## Step 3: Define the mutation in the schema +## Define the mutation in the schema 1. If it doesn't already exist, create `schemas` folder in the plugin, and add an `index.js` file there. 1. If it doesn't already exist, create `schema.graphql` in `schemas` in the plugin. @@ -46,7 +47,7 @@ See [Resolver Mutations and Queries vs. Plugin Mutations and Queries](../guides/ } ``` -## Step 4: Create the plugin mutation file +## Create the plugin mutation file 1. If it doesn't already exist, create `mutations` folder in the plugin, and add an `index.js` file there. 2. In `mutations`, create a file for the mutation, e.g. `createSomething.js` for the `createSomething` mutation. The file should look something like this: @@ -66,7 +67,7 @@ export default async function createSomething(context) { } ``` -## Step 5: Add the plugin mutation to the mutations context +## Add the plugin mutation to the mutations context In `mutations/index.js` in the plugin, import your mutation and add it to the default export object. Example: @@ -95,7 +96,7 @@ Your plugin mutation function is now available in the GraphQL context as `contex > NOTE: The mutations objects from all plugins are merged, so be sure that another plugin does not have a mutation with the same name. The last one registered with that name will win, and plugins are generally registered in alphabetical order by plugin name. Tip: You can use this to your advantage if you want to override the mutation function of a core plugin without modifying core code. -## Step 6: Add a test file for your mutation +## Add a test file for your mutation If your mutation is in a file named `createSomething.js`, your Jest tests should be in a file named `createSomething.test.js` in the same folder. Initially you can copy and paste the following test: @@ -109,7 +110,7 @@ test("expect to return a Promise that resolves to null", async () => { }); ``` -## Step 7: Create the GraphQL mutation resolver file +## Create the GraphQL mutation resolver file 1. If it doesn't already exist, create `resolvers` folder in the plugin, and add an `index.js` file there. 2. If it doesn't already exist, create `resolvers/Mutation` folder in the plugin, and add an `index.js` file there. "Mutation" must be capitalized. @@ -144,7 +145,7 @@ Make adjustments to the resolver function so that it reads and passes along the - Call `context.mutations.` (your new plugin mutation) with the necessary arguments, and `await` a response. - Return an object that contains the `clientMutationId` and the object returned by the plugin mutation. (This must match the "Payload" type from the schema.) -## Step 8: Register the resolver +## Register the resolver In `resolvers/Mutation/index.js` in the plugin, import your mutation resolver and add it to the default export object. Example: @@ -185,7 +186,7 @@ export default async function register(app) { Calling your mutation with GraphQL should now work. -## Step 9: Add a test file for your mutation resolver +## Add a test file for your mutation resolver If your mutation resolver is in a file named `createSomething.js`, your Jest tests should be in a file named `createSomething.test.js` in the same folder. Initially you can copy and paste the following test: @@ -219,12 +220,14 @@ test("correctly passes through to mutations.createSomething", async () => { This of course should be updated with tests that are appropriate for whatever your mutation resolver does. For example, verify that all ID and schema transformations happen. -## Step 10: Finish implementing your mutation +## Finish implementing your mutation Adjust the mutation function and the mutation resolver function until they work as expected, with tests that prove it. This will likely involve adding additional input fields, ID transformations, permission checks, MongoDB calls, and event emitting. -Refer to [Developing the GraphQL API](../docs/developing-graphql.md) for answers to any questions you might have while implementing your mutation. - -## Step 11: Update the JSDoc comments +## Update the JSDoc comments Write/update jsdoc comments for the plugin mutation function, the mutation resolver, and any util functions. The resolver function must have `@memberof /GraphQL` in the jsdoc, and the `@name` must be the full GraphQL schema path in quotation marks, e.g., "Mutation.createSomething". (The quotation marks are necessary for the output API documentation to be correct due to the periods.) + +## More resources + +[Build an API plugin guide](https://mailchimp.com/developer/open-commerce/guides/build-api-plugin/) \ No newline at end of file From 18b90f2aa2f601508fc513b8410d1a213999bc8f Mon Sep 17 00:00:00 2001 From: Alvaro Bueno Date: Fri, 4 Feb 2022 14:08:10 -0500 Subject: [PATCH 3/6] fix: giving proper format to guide on GraphQL queries Signed-off-by: Alvaro Bueno --- guides/create-graphql-mutation.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/guides/create-graphql-mutation.md b/guides/create-graphql-mutation.md index 7b32103..78d3fc2 100644 --- a/guides/create-graphql-mutation.md +++ b/guides/create-graphql-mutation.md @@ -7,13 +7,13 @@ When adding a new mutation, the first step is to decide which plugin should own ## Understand the difference between a plugin mutation function and a GraphQL mutation resolver -See [Resolver Mutations and Queries vs. Plugin Mutations and Queries](../guides/developers-guide/core/developing-graphql.md#resolver-mutations-and-queries-vs-plugin-mutations-and-queries) +See [Resolver mutations and queries vs. plugin mutations and queries in the GraphQL concepts docs](.docs/graphql-concepts.md) ## Define the mutation in the schema -1. If it doesn't already exist, create `schemas` folder in the plugin, and add an `index.js` file there. -1. If it doesn't already exist, create `schema.graphql` in `schemas` in the plugin. -1. Import the GraphQL file into `index.js` and default export it in an array: +- If it doesn't already exist, create `schemas` folder in the plugin, and add an `index.js` file there. +- If it doesn't already exist, create `schema.graphql` in `schemas` in the plugin. +- Import the GraphQL file into `index.js` and default export it in an array: ```js import importAsString from "@reactioncommerce/api-utils/importAsString.js"; @@ -25,14 +25,14 @@ See [Resolver Mutations and Queries vs. Plugin Mutations and Queries](../guides/ > NOTE: For large plugins, you can split to multiple `.graphql` files and export a multi-item array. -1. In the `.graphql` file, add your mutation within `extend type Mutation { }`. Add an `extend type Mutation` section near the top if the file doesn't have it yet. -1. Follow [the Relay recommendations](https://facebook.github.io/relay/graphql/mutations.htm) for mutation input arguments, which is to have only one argument named `input` that takes an input type that is the capitalized mutation name plus the suffix "Input", and to return a type that is the capitalized mutation name plus the suffix "Payload". +- In the `.graphql` file, add your mutation within `extend type Mutation { }`. Add an `extend type Mutation` section near the top if the file doesn't have it yet. +- Follow [the Relay recommendations](https://facebook.github.io/relay/graphql/mutations.htm) for mutation input arguments, which is to have only one argument named `input` that takes an input type that is the capitalized mutation name plus the suffix "Input", and to return a type that is the capitalized mutation name plus the suffix "Payload". Example: `addAccountEmailRecord(input: AddAccountEmailRecordInput!): AddAccountEmailRecordPayload` -1. Add the Input and Payload types to the schema. Both must have `clientMutationId: String` field and may have any other fields as necessary. The mutation response payload should include whatever object was mutated. -1. Document your mutation, the new types, and all fields in those types using string literals. See [Documenting a GraphQL Schema](../guides/developers-guide/core/developing-graphql.md#documenting-a-graphql-schema). -1. If not already done, register your schemas in the plugin's `index.js` file: +- Add the Input and Payload types to the schema. Both must have `clientMutationId: String` field and may have any other fields as necessary. The mutation response payload should include whatever object was mutated. +- Document your mutation, the new types, and all fields in those types using string literals. +- If not already done, register your schemas in the plugin's `index.js` file: ```js import schemas from "./schemas"; @@ -49,8 +49,8 @@ See [Resolver Mutations and Queries vs. Plugin Mutations and Queries](../guides/ ## Create the plugin mutation file -1. If it doesn't already exist, create `mutations` folder in the plugin, and add an `index.js` file there. -2. In `mutations`, create a file for the mutation, e.g. `createSomething.js` for the `createSomething` mutation. The file should look something like this: +- If it doesn't already exist, create `mutations` folder in the plugin, and add an `index.js` file there. +- In `mutations`, create a file for the mutation, e.g. `createSomething.js` for the `createSomething` mutation. The file should look something like this: ```js import Logger from "@reactioncommerce/logger"; @@ -112,9 +112,9 @@ test("expect to return a Promise that resolves to null", async () => { ## Create the GraphQL mutation resolver file -1. If it doesn't already exist, create `resolvers` folder in the plugin, and add an `index.js` file there. -2. If it doesn't already exist, create `resolvers/Mutation` folder in the plugin, and add an `index.js` file there. "Mutation" must be capitalized. -3. In `resolvers/Mutation`, create a file for the mutation resolver, e.g. `createSomething.js` for the `createSomething` mutation. The file should look something like this initially: +- If it doesn't already exist, create `resolvers` folder in the plugin, and add an `index.js` file there. +- If it doesn't already exist, create `resolvers/Mutation` folder in the plugin, and add an `index.js` file there. "Mutation" must be capitalized. +- In `resolvers/Mutation`, create a file for the mutation resolver, e.g. `createSomething.js` for the `createSomething` mutation. The file should look something like this initially: ```js /** From 8c38925a576c7791250b3a8d897d3c9ef03810aa Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Tue, 8 Feb 2022 11:32:03 +0800 Subject: [PATCH 4/6] fix: Relay link and link text Signed-off-by: Brent Hoover --- guides/create-graphql-mutation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/create-graphql-mutation.md b/guides/create-graphql-mutation.md index 78d3fc2..d0400f6 100644 --- a/guides/create-graphql-mutation.md +++ b/guides/create-graphql-mutation.md @@ -26,7 +26,7 @@ See [Resolver mutations and queries vs. plugin mutations and queries in the Grap > NOTE: For large plugins, you can split to multiple `.graphql` files and export a multi-item array. - In the `.graphql` file, add your mutation within `extend type Mutation { }`. Add an `extend type Mutation` section near the top if the file doesn't have it yet. -- Follow [the Relay recommendations](https://facebook.github.io/relay/graphql/mutations.htm) for mutation input arguments, which is to have only one argument named `input` that takes an input type that is the capitalized mutation name plus the suffix "Input", and to return a type that is the capitalized mutation name plus the suffix "Payload". +- Follow [Relay recommendations](https://relay.dev/docs/guided-tour/updating-data/graphql-mutations/) for mutation input arguments, which is to have only one argument named `input` that takes an input type that is the capitalized mutation name plus the suffix "Input", and to return a type that is the capitalized mutation name plus the suffix "Payload". Example: `addAccountEmailRecord(input: AddAccountEmailRecordInput!): AddAccountEmailRecordPayload` @@ -230,4 +230,4 @@ Write/update jsdoc comments for the plugin mutation function, the mutation resol ## More resources -[Build an API plugin guide](https://mailchimp.com/developer/open-commerce/guides/build-api-plugin/) \ No newline at end of file +[Build an API plugin guide](https://mailchimp.com/developer/open-commerce/guides/build-api-plugin/) From 7d88ebe492c10e418f36e723930d98618946c292 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 21 Feb 2022 11:54:41 +0800 Subject: [PATCH 5/6] feat: make content fit content guide better Signed-off-by: Brent Hoover --- guides/create-graphql-mutation.md | 95 ++++++++++++++++--------------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/guides/create-graphql-mutation.md b/guides/create-graphql-mutation.md index d0400f6..7203e2f 100644 --- a/guides/create-graphql-mutation.md +++ b/guides/create-graphql-mutation.md @@ -1,13 +1,14 @@ ## At a glance -In this guide, we’ll guide you through the steps you need to create a successful mutation in GraphQL with Mailchimp Open Commerce -## Identify which plugin owns the mutation +We're working on an implementation for Leader of the Pack, an outdoor equipment retailer. In the GraphQL query guide we described data we added that contains whether we can ship a category of products to a particular zip. But now we need a way to update that information. For this we will create a GraphQL mutation + +## What you need -When adding a new mutation, the first step is to decide which plugin should own it. This is usually obvious, but not always. You should think about whether any other plugins or services will need to call your mutation. If the mutation is fundamental to the system, then it may need to go in the "core" plugin, if no better alternative exists. +We need to understand what key you will use to query the data, and what the payload of the data is. -## Understand the difference between a plugin mutation function and a GraphQL mutation resolver +## Identify which plugin owns the mutation -See [Resolver mutations and queries vs. plugin mutations and queries in the GraphQL concepts docs](.docs/graphql-concepts.md) +When adding a new mutation, the first step is to decide which plugin should own it. This is usually obvious, but not always. You should think about whether any other plugins or services will need to call your mutation. ## Define the mutation in the schema @@ -15,15 +16,15 @@ See [Resolver mutations and queries vs. plugin mutations and queries in the Grap - If it doesn't already exist, create `schema.graphql` in `schemas` in the plugin. - Import the GraphQL file into `index.js` and default export it in an array: - ```js - import importAsString from "@reactioncommerce/api-utils/importAsString.js"; +```js +import importAsString from "@reactioncommerce/api-utils/importAsString.js"; - const schema = importAsString("./schema.graphql"); +const schema = importAsString("./schema.graphql"); - export default [schema]; - ``` +export default [schema]; +``` - > NOTE: For large plugins, you can split to multiple `.graphql` files and export a multi-item array. +> NOTE: For large plugins, you can split to multiple `.graphql` files and export a multi-item array. - In the `.graphql` file, add your mutation within `extend type Mutation { }`. Add an `extend type Mutation` section near the top if the file doesn't have it yet. - Follow [Relay recommendations](https://relay.dev/docs/guided-tour/updating-data/graphql-mutations/) for mutation input arguments, which is to have only one argument named `input` that takes an input type that is the capitalized mutation name plus the suffix "Input", and to return a type that is the capitalized mutation name plus the suffix "Payload". @@ -34,35 +35,35 @@ See [Resolver mutations and queries vs. plugin mutations and queries in the Grap - Document your mutation, the new types, and all fields in those types using string literals. - If not already done, register your schemas in the plugin's `index.js` file: - ```js - import schemas from "./schemas"; +```js +import schemas from "./schemas"; - export default async function register(app) { - await app.registerPlugin({ - graphQL: { - schemas - }, - // other props - }); - } - ``` +export default async function register(app) { + await app.registerPlugin({ + graphQL: { + schemas + }, + // other props + }); +} +``` ## Create the plugin mutation file - If it doesn't already exist, create `mutations` folder in the plugin, and add an `index.js` file there. -- In `mutations`, create a file for the mutation, e.g. `createSomething.js` for the `createSomething` mutation. The file should look something like this: +- In `mutations`, create a file for the mutation, e.g. `updateRestrictions.js` for the `updateRestrictions` mutation. The file should look something like this: ```js import Logger from "@reactioncommerce/logger"; /** - * @method createSomething + * @method updateRestrictions * @summary TODO * @param {Object} context - an object containing the per-request state * @return {Promise} TODO */ -export default async function createSomething(context) { - Logger.info("createSomething mutation is not yet implemented"); +export default async function updateRestrictions(context) { + Logger.info("updateRestrictions mutation is not yet implemented"); return null; } ``` @@ -72,10 +73,10 @@ export default async function createSomething(context) { In `mutations/index.js` in the plugin, import your mutation and add it to the default export object. Example: ```js -import createSomething from "./createSomething" +import updateRestrictions from "./updateRestrictions.js" export default { - createSomething + updateRestrictions }; ``` @@ -92,20 +93,20 @@ export default async function register(app) { } ``` -Your plugin mutation function is now available in the GraphQL context as `context.mutations.createSomething`. +Your plugin mutation function is now available in the GraphQL context as `context.mutations.updateRestrictions`. > NOTE: The mutations objects from all plugins are merged, so be sure that another plugin does not have a mutation with the same name. The last one registered with that name will win, and plugins are generally registered in alphabetical order by plugin name. Tip: You can use this to your advantage if you want to override the mutation function of a core plugin without modifying core code. ## Add a test file for your mutation -If your mutation is in a file named `createSomething.js`, your Jest tests should be in a file named `createSomething.test.js` in the same folder. Initially you can copy and paste the following test: +If your mutation is in a file named `updateRestrictions.js`, your Jest tests should be in a file named `updateRestrictions.test.js` in the same folder. Initially you can copy and paste the following test: ```js import mockContext from "/imports/test-utils/helpers/mockContext"; -import createSomething from "./createSomething"; +import updateRestrictions from "./updateRestrictions.js"; test("expect to return a Promise that resolves to null", async () => { - const result = await createSomething(mockContext); + const result = await updateRestrictions(mockContext); expect(result).toEqual(null); }); ``` @@ -114,24 +115,24 @@ test("expect to return a Promise that resolves to null", async () => { - If it doesn't already exist, create `resolvers` folder in the plugin, and add an `index.js` file there. - If it doesn't already exist, create `resolvers/Mutation` folder in the plugin, and add an `index.js` file there. "Mutation" must be capitalized. -- In `resolvers/Mutation`, create a file for the mutation resolver, e.g. `createSomething.js` for the `createSomething` mutation. The file should look something like this initially: +- In `resolvers/Mutation`, create a file for the mutation resolver, e.g. `updateRestrictions.js` for the `updateRestrictions` mutation. The file should look something like this initially: ```js /** - * @name "Mutation.createSomething" + * @name "Mutation.updateRestrictions" * @method * @memberof MyPlugin/GraphQL - * @summary resolver for the createSomething GraphQL mutation + * @summary resolver for the updateRestrictions GraphQL mutation * @param {Object} parentResult - unused * @param {Object} args.input - an object of all mutation arguments that were sent by the client * @param {String} [args.input.clientMutationId] - An optional string identifying the mutation call * @param {Object} context - an object containing the per-request state - * @return {Promise} CreateSomethingPayload + * @return {Promise} UpdateRestrictionsgPayload */ -export default async function createSomething(parentResult, { input }, context) { +export default async function updateRestrictions(parentResult, { input }, context) { const { clientMutationId = null } = input; // TODO: decode incoming IDs here - const renameMe = await context.mutations.createSomething(context); + const renameMe = await context.mutations.updateRestrictions(context); return { renameMe, clientMutationId @@ -150,10 +151,10 @@ Make adjustments to the resolver function so that it reads and passes along the In `resolvers/Mutation/index.js` in the plugin, import your mutation resolver and add it to the default export object. Example: ```js -import createSomething from "./createSomething" +import updateRestrictions from "./updateRestrictions.js" export default { - createSomething + updateRestrictions }; ``` @@ -188,23 +189,23 @@ Calling your mutation with GraphQL should now work. ## Add a test file for your mutation resolver -If your mutation resolver is in a file named `createSomething.js`, your Jest tests should be in a file named `createSomething.test.js` in the same folder. Initially you can copy and paste the following test: +If your mutation resolver is in a file named `updateRestrictions.js`, your Jest tests should be in a file named `updateRestrictions.test.js` in the same folder. Initially you can copy and paste the following test: ```js -import createSomething from "./createSomething"; +import updateRestrictions from "./updateRestrictions.js"; -test("correctly passes through to mutations.createSomething", async () => { +test("correctly passes through to mutations.updateRestrictions", async () => { const fakeResult = { /* TODO */ }; - const mockMutation = jest.fn().mockName("mutations.createSomething"); + const mockMutation = jest.fn().mockName("mutations.updateRestrictions"); mockMutation.mockReturnValueOnce(Promise.resolve(fakeResult)); const context = { mutations: { - createSomething: mockMutation + updateRestrictions: mockMutation } }; - const result = await createSomething(null, { + const result = await updateRestrictions(null, { input: { /* TODO */ clientMutationId: "clientMutationId" @@ -226,7 +227,7 @@ Adjust the mutation function and the mutation resolver function until they work ## Update the JSDoc comments -Write/update jsdoc comments for the plugin mutation function, the mutation resolver, and any util functions. The resolver function must have `@memberof /GraphQL` in the jsdoc, and the `@name` must be the full GraphQL schema path in quotation marks, e.g., "Mutation.createSomething". (The quotation marks are necessary for the output API documentation to be correct due to the periods.) +Write/update jsdoc comments for the plugin mutation function, the mutation resolver, and any util functions. The resolver function must have `@memberof /GraphQL` in the jsdoc, and the `@name` must be the full GraphQL schema path in quotation marks, e.g., "Mutation.updateRestrictions". (The quotation marks are necessary for the output API documentation to be correct due to the periods.) ## More resources From eb67629ea4bf0a5e6e13d8b4a0b356ccdb0aa2db Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 21 Feb 2022 11:56:59 +0800 Subject: [PATCH 6/6] fix: make pronouns consistent Signed-off-by: Brent Hoover --- guides/create-graphql-mutation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/create-graphql-mutation.md b/guides/create-graphql-mutation.md index 7203e2f..f64113e 100644 --- a/guides/create-graphql-mutation.md +++ b/guides/create-graphql-mutation.md @@ -1,10 +1,10 @@ ## At a glance -We're working on an implementation for Leader of the Pack, an outdoor equipment retailer. In the GraphQL query guide we described data we added that contains whether we can ship a category of products to a particular zip. But now we need a way to update that information. For this we will create a GraphQL mutation +You're working on an implementation for Leader of the Pack, an outdoor equipment retailer. In the GraphQL query guide we described data we added that contains whether we can ship a category of products to a particular zip. But now we need a way to update that information. For this we will create a GraphQL mutation ## What you need -We need to understand what key you will use to query the data, and what the payload of the data is. +You need to understand what key you will use to query the data, and what the payload of the data is. ## Identify which plugin owns the mutation