From 2c807ddbfe5f294bbe75db2b8671bd625499ed8e Mon Sep 17 00:00:00 2001 From: Egor Yurtaev Date: Mon, 16 Aug 2021 15:40:26 +0800 Subject: [PATCH] fix(buildOperationNodeForField): mutation response return a field of type Query (#3383) * test: Test for buildOperationNodeForField #3380 * fix(buildOperationNodeForField): mutation response return a field of type Query * enhance(buildOperationNodeForField): mutation response return a field of type Query Co-authored-by: Arda TANRIKULU --- .changeset/shaggy-monkeys-smoke.md | 5 ++ .../utils/src/build-operation-for-field.ts | 22 ++++- .../build-operation-node-for-field.spec.ts | 81 +++++++++++++++++++ 3 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 .changeset/shaggy-monkeys-smoke.md diff --git a/.changeset/shaggy-monkeys-smoke.md b/.changeset/shaggy-monkeys-smoke.md new file mode 100644 index 00000000000..8e77c8097d5 --- /dev/null +++ b/.changeset/shaggy-monkeys-smoke.md @@ -0,0 +1,5 @@ +--- +'@graphql-tools/utils': patch +--- + +enhance(buildOperationNodeForField): mutation response return a field of type Query diff --git a/packages/utils/src/build-operation-for-field.ts b/packages/utils/src/build-operation-for-field.ts index c9f45f1d975..0be498e66a3 100644 --- a/packages/utils/src/build-operation-for-field.ts +++ b/packages/utils/src/build-operation-for-field.ts @@ -28,7 +28,7 @@ import { Kind, } from 'graphql'; -import { getDefinedRootType } from './rootTypes'; +import { getDefinedRootType, getRootTypeNames } from './rootTypes'; let operationVariables: VariableDefinitionNode[] = []; let fieldTypeMap = new Map(); @@ -60,7 +60,7 @@ export function buildOperationNodeForField({ kind, field, models, - ignore, + ignore = [], depthLimit, circularReferenceDepth, argNames, @@ -79,16 +79,19 @@ export function buildOperationNodeForField({ resetOperationVariables(); resetFieldMap(); + const rootTypeNames = getRootTypeNames(schema); + const operationNode = buildOperationAndCollectVariables({ schema, fieldName: field, kind, models: models || [], - ignore: ignore || [], + ignore, depthLimit: depthLimit || Infinity, circularReferenceDepth: circularReferenceDepth || 1, argNames, selectedFields, + rootTypeNames, }); // attach variables @@ -110,6 +113,7 @@ function buildOperationAndCollectVariables({ circularReferenceDepth, argNames, selectedFields, + rootTypeNames, }: { schema: GraphQLSchema; fieldName: string; @@ -120,6 +124,7 @@ function buildOperationAndCollectVariables({ circularReferenceDepth: number; argNames?: string[]; selectedFields: SelectedFields; + rootTypeNames: Set; }): OperationDefinitionNode { const type = getDefinedRootType(schema, kind); const field = type.getFields()[fieldName]; @@ -159,6 +164,7 @@ function buildOperationAndCollectVariables({ depth: 0, argNames, selectedFields, + rootTypeNames, }), ], }, @@ -179,6 +185,7 @@ function resolveSelectionSet({ depth, argNames, selectedFields, + rootTypeNames, }: { parent: GraphQLNamedType; type: GraphQLNamedType; @@ -193,6 +200,7 @@ function resolveSelectionSet({ depth: number; selectedFields: SelectedFields; argNames?: string[]; + rootTypeNames: Set; }): SelectionSetNode | void { if (typeof selectedFields === 'boolean' && depth > depthLimit) { return; @@ -232,6 +240,7 @@ function resolveSelectionSet({ depth, argNames, selectedFields, + rootTypeNames, }) as SelectionSetNode, }; }) @@ -276,6 +285,7 @@ function resolveSelectionSet({ depth, argNames, selectedFields, + rootTypeNames, }) as SelectionSetNode, }; }) @@ -283,7 +293,7 @@ function resolveSelectionSet({ }; } - if (isObjectType(type)) { + if (isObjectType(type) && !rootTypeNames.has(type.name)) { const isIgnored = ignore.includes(type.name) || ignore.includes(`${parent.name}.${path[path.length - 1]}`); const isModel = models.includes(type.name); @@ -328,6 +338,7 @@ function resolveSelectionSet({ depth, argNames, selectedFields: selectedSubFields, + rootTypeNames, }); } return null; @@ -403,6 +414,7 @@ function resolveField({ depth, argNames, selectedFields, + rootTypeNames, }: { type: GraphQLObjectType; field: GraphQLField; @@ -417,6 +429,7 @@ function resolveField({ depth: number; selectedFields: SelectedFields; argNames?: string[]; + rootTypeNames: Set; }): SelectionNode { const namedType = getNamedType(field.type); let args: ArgumentNode[] = []; @@ -489,6 +502,7 @@ function resolveField({ depth: depth + 1, argNames, selectedFields, + rootTypeNames, }) || undefined, arguments: args, }; diff --git a/packages/utils/tests/build-operation-node-for-field.spec.ts b/packages/utils/tests/build-operation-node-for-field.spec.ts index 53e9612e24f..2349867392b 100644 --- a/packages/utils/tests/build-operation-node-for-field.spec.ts +++ b/packages/utils/tests/build-operation-node-for-field.spec.ts @@ -260,6 +260,87 @@ test('should work with mutation', async () => { ); }); +test('should work with mutation + return a field of type Query', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Pizza { + dough: String! + toppings: [String!] + query: Query + } + + type Query { + pizza: Pizza + pizzaById(id: String!): Pizza + } + type Mutation { + addPizza(name: String!): Pizza + } + `) + const document = buildOperationNodeForField({ + schema, + kind: 'mutation', + field: 'addPizza', + models, + ignore: [], + })!; + + expect(clean(document)).toEqual( + clean(/* GraphQL */ ` + mutation addPizza_mutation($name: String!) { + addPizza(name: $name) { + dough + toppings + } + } + `) + ); +}); + +test('should work with mutation + Union + return a field of type Query', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Pizza { + dough: String! + toppings: [String!] + query: Query + } + type Salad { + ingredients: [String!]! + query: Query + } + union Food = Pizza | Salad + type Query { + pizza: Pizza + getPizzaById(id: String!): Pizza + } + type Mutation { + addRandomFood(name: String!): Food + } + `) + const document = buildOperationNodeForField({ + schema, + kind: 'mutation', + field: 'addRandomFood', + models, + ignore: [], + })!; + + expect(clean(document)).toEqual( + clean(/* GraphQL */ ` + mutation addRandomFood_mutation($name: String!) { + addRandomFood(name: $name) { + ... on Pizza { + dough + toppings + } + ... on Salad { + ingredients + } + } + } + `) + ); +}); + test('should work with mutation and unions', async () => { const document = buildOperationNodeForField({ schema,