From 5df478ab89b7f009fd405e6eafa0bb18436e3ce6 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 24 Jan 2022 14:04:34 +0100 Subject: [PATCH 1/4] add the operation kind to FetchNode this will allow data sources to decide if they can cache or deduplicate a request --- .../src/__tests__/build-query-plan.feature | 52 ++++++++++++++++++ query-planner-js/src/QueryPlan.ts | 2 + .../features/basic/abstract-types.feature | 23 ++++++++ .../__tests__/features/basic/aliases.feature | 11 ++++ .../__tests__/features/basic/boolean.feature | 13 +++++ .../features/basic/build-query-plan.feature | 54 +++++++++++++++++++ .../features/basic/custom-directives.feature | 3 ++ .../features/basic/execution-style.feature | 2 + .../features/basic/fragments.feature | 15 +++++- .../features/basic/mutations.feature | 15 ++++++ .../__tests__/features/basic/provides.feature | 3 ++ .../__tests__/features/basic/requires.feature | 4 ++ .../features/basic/single-service.feature | 3 ++ .../features/basic/value-types.feature | 3 ++ .../features/basic/variables.feature | 11 ++++ .../multiple-keys/multiple-keys.feature | 3 ++ query-planner-js/src/buildPlan.ts | 3 +- 17 files changed, 217 insertions(+), 3 deletions(-) diff --git a/gateway-js/src/__tests__/build-query-plan.feature b/gateway-js/src/__tests__/build-query-plan.feature index 93a6e3e7f..19c6857b9 100644 --- a/gateway-js/src/__tests__/build-query-plan.feature +++ b/gateway-js/src/__tests__/build-query-plan.feature @@ -27,6 +27,7 @@ Scenario: should not confuse union types with overlapping field names "kind": "Fetch", "serviceName": "documents", "variableUsages": [], + "operationKind": "query", "operation": "{body{__typename ...on Image{attributes{url}}...on Text{attributes{bold text}}}}" } } @@ -51,6 +52,7 @@ Scenario: should use a single fetch when requesting a root field from one servic "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{name{first}}}" } } @@ -81,6 +83,7 @@ Scenario: should use two independent fetches when requesting root fields from tw "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{name{first}}}" }, { @@ -90,6 +93,7 @@ Scenario: should use two independent fetches when requesting root fields from tw "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{topProducts{__typename ...on Book{__typename isbn}...on Furniture{name}}}" }, { @@ -109,6 +113,7 @@ Scenario: should use two independent fetches when requesting root fields from tw } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -131,6 +136,7 @@ Scenario: should use two independent fetches when requesting root fields from tw } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}" } } @@ -164,6 +170,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{topProducts{__typename ...on Book{__typename isbn}...on Furniture{name}}product(upc:\"1\"){__typename ...on Book{__typename isbn}...on Furniture{name}}}" }, { @@ -189,6 +196,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -211,6 +219,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}" } } @@ -236,6 +245,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -258,6 +268,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}" } } @@ -292,6 +303,7 @@ Scenario: should use a single fetch when requesting relationship subfields from "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{body author{reviews{body}}}}" } } @@ -320,6 +332,7 @@ Scenario: should use a single fetch when requesting relationship subfields and p "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{body author{id reviews{body}}}}" } } @@ -350,6 +363,7 @@ Scenario: when requesting an extension field from another service, it should add "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id name{first}}}" }, { @@ -369,6 +383,7 @@ Scenario: when requesting an extension field from another service, it should add } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{body}}}}" } } @@ -399,6 +414,7 @@ Scenario: when requesting an extension field from another service, when the pare "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id}}" }, { @@ -418,6 +434,7 @@ Scenario: when requesting an extension field from another service, when the pare } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{body}}}}" } } @@ -449,6 +466,7 @@ Scenario: when requesting an extension field from another service, should only a "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id}}" }, { @@ -468,6 +486,7 @@ Scenario: when requesting an extension field from another service, should only a } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{body}numberOfReviews}}}" } } @@ -501,6 +520,7 @@ Scenario: when requesting a composite field with subfields from another service, "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{body author{__typename id}}}" }, { @@ -520,6 +540,7 @@ Scenario: when requesting a composite field with subfields from another service, } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{name{first}}}}" } } @@ -548,6 +569,7 @@ Scenario: when requesting a composite field with subfields from another service, "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{topCars{__typename id price}}" }, { @@ -568,6 +590,7 @@ Scenario: when requesting a composite field with subfields from another service, } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Car{retailPrice}}}" } } @@ -600,6 +623,7 @@ Scenario: when requesting a composite field with subfields from another service, "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{author{__typename id}}}" }, { @@ -619,6 +643,7 @@ Scenario: when requesting a composite field with subfields from another service, } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{name{first}}}}" } } @@ -649,6 +674,7 @@ Scenario: when requesting a relationship field with extension subfields from a d "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{author{__typename id}}}" }, { @@ -668,6 +694,7 @@ Scenario: when requesting a relationship field with extension subfields from a d } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{birthDate}}}" } } @@ -693,6 +720,7 @@ Scenario: for abstract types, it should add __typename when fetching objects of "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{topProducts{__typename price}}" } } @@ -721,6 +749,7 @@ Scenario: should break up when traversing an extension field on an interface typ "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{topProducts{__typename price ...on Book{__typename isbn}...on Furniture{__typename upc}}}" }, { @@ -748,6 +777,7 @@ Scenario: should break up when traversing an extension field on an interface typ } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{reviews{body}}...on Furniture{reviews{body}}}}" } } @@ -781,6 +811,7 @@ Scenario: interface fragments should expand into possible types only "kind": "Fetch", "serviceName": "books", "variableUsages": [], + "operationKind": "query", "operation": "{books{__typename isbn title year}}" }, { @@ -802,6 +833,7 @@ Scenario: interface fragments should expand into possible types only } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}" } } @@ -829,6 +861,7 @@ Scenario: interface inside interface should expand into possible types only "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{product(upc:\"\"){__typename details{__typename country}}}" } } @@ -870,6 +903,7 @@ Scenario: should properly expand nested unions with inline fragments "kind": "Fetch", "serviceName": "documents", "variableUsages": [], + "operationKind": "query", "operation": "{body{__typename ...on Image{attributes{url}}...on Text{attributes{bold}}}}" } } @@ -912,6 +946,7 @@ Scenario: deduplicates fields / selections regardless of adjacency and type cond "kind": "Fetch", "serviceName": "documents", "variableUsages": [], + "operationKind": "query", "operation": "{body{__typename ...on Text{attributes{bold text}}}}" } } @@ -947,6 +982,7 @@ Scenario: deduplicates fields / selections regardless of adjacency and type cond "kind": "Fetch", "serviceName": "documents", "variableUsages": [], + "operationKind": "query", "operation": "{body{__typename ...on Text{attributes{bold text}}}}" } } @@ -972,6 +1008,7 @@ Scenario: supports basic, single-service mutation "username", "password" ], + "operationKind": "mutation", "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){id}}" } } @@ -1005,6 +1042,7 @@ Scenario: supports mutations with a cross-service request "username", "password" ], + "operationKind": "mutation", "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){__typename id}}" }, { @@ -1032,6 +1070,7 @@ Scenario: supports mutations with a cross-service request } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{upc}}}}}}" } }, @@ -1063,6 +1102,7 @@ Scenario: supports mutations with a cross-service request } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{upc}}}" } } @@ -1097,6 +1137,7 @@ Scenario: returning across service boundaries "upc", "body" ], + "operationKind": "mutation", "operation": "mutation($upc:String!$body:String!){reviewProduct(input:{upc:$upc body:$body}){__typename ...on Furniture{__typename upc}}}" }, { @@ -1124,6 +1165,7 @@ Scenario: returning across service boundaries } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Furniture{name}}}" } } @@ -1170,6 +1212,7 @@ Scenario: supports multiple root mutations "username", "password" ], + "operationKind": "mutation", "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){__typename id}}" }, { @@ -1197,6 +1240,7 @@ Scenario: supports multiple root mutations } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{upc}}}}}}" } }, @@ -1228,6 +1272,7 @@ Scenario: supports multiple root mutations } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{upc}}}" } }, @@ -1238,6 +1283,7 @@ Scenario: supports multiple root mutations "upc", "body" ], + "operationKind": "mutation", "operation": "mutation($upc:String!$body:String!){reviewProduct(input:{upc:$upc body:$body}){__typename ...on Furniture{__typename upc}}}" }, { @@ -1265,6 +1311,7 @@ Scenario: supports multiple root mutations } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Furniture{name}}}" } } @@ -1319,6 +1366,7 @@ Scenario: multiple root mutations with correct service order "body", "updatedReview" ], + "operationKind": "mutation", "operation": "mutation($upc:String!$body:String!$updatedReview:UpdateReviewInput!){reviewProduct(input:{upc:$upc body:$body}){__typename ...on Furniture{upc}}updateReview(review:$updatedReview){id body}}" }, { @@ -1328,6 +1376,7 @@ Scenario: multiple root mutations with correct service order "username", "password" ], + "operationKind": "mutation", "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){__typename id}}" }, { @@ -1355,6 +1404,7 @@ Scenario: multiple root mutations with correct service order } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{upc}}}}}}" } }, @@ -1386,6 +1436,7 @@ Scenario: multiple root mutations with correct service order } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{upc}}}" } }, @@ -1395,6 +1446,7 @@ Scenario: multiple root mutations with correct service order "variableUsages": [ "reviewId" ], + "operationKind": "mutation", "operation": "mutation($reviewId:ID!){deleteReview(id:$reviewId)}" } ] diff --git a/query-planner-js/src/QueryPlan.ts b/query-planner-js/src/QueryPlan.ts index 71abb9541..5cd4e6e51 100644 --- a/query-planner-js/src/QueryPlan.ts +++ b/query-planner-js/src/QueryPlan.ts @@ -1,6 +1,7 @@ import { Kind, SelectionNode as GraphQLJSSelectionNode, + OperationTypeNode, } from 'graphql'; import prettyFormat from 'pretty-format'; import { queryPlanSerializer, astSerializer } from './snapshotSerializers'; @@ -30,6 +31,7 @@ export interface FetchNode { variableUsages?: string[]; requires?: QueryPlanSelectionNode[]; operation: string; + operationKind: OperationTypeNode; } export interface FlattenNode { diff --git a/query-planner-js/src/__tests__/features/basic/abstract-types.feature b/query-planner-js/src/__tests__/features/basic/abstract-types.feature index 171bc13ef..c34606280 100644 --- a/query-planner-js/src/__tests__/features/basic/abstract-types.feature +++ b/query-planner-js/src/__tests__/features/basic/abstract-types.feature @@ -22,6 +22,7 @@ Scenario: handles an abstract type from the base service "kind": "Fetch", "serviceName": "product", "variableUsages": ["upc"], + "operationKind": "query", "operation": "query($upc:String!){product(upc:$upc){__typename ...on Book{__typename isbn upc price}...on Furniture{upc name price}}}" }, { @@ -41,6 +42,7 @@ Scenario: handles an abstract type from the base service } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -63,6 +65,7 @@ Scenario: handles an abstract type from the base service } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}" } } @@ -91,6 +94,7 @@ Scenario: can request fields on extended interfaces "kind": "Fetch", "serviceName": "product", "variableUsages": ["upc"], + "operationKind": "query", "operation": "query($upc:String!){product(upc:$upc){__typename ...on Book{__typename isbn}...on Furniture{__typename sku}}}" }, { @@ -118,6 +122,7 @@ Scenario: can request fields on extended interfaces } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{inStock}...on Furniture{inStock}}}" } } @@ -149,6 +154,7 @@ Scenario: can request fields on extended types that implement an interface "kind": "Fetch", "serviceName": "product", "variableUsages": ["upc"], + "operationKind": "query", "operation": "query($upc:String!){product(upc:$upc){__typename ...on Book{__typename isbn}...on Furniture{__typename sku}}}" }, { @@ -176,6 +182,7 @@ Scenario: can request fields on extended types that implement an interface } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{inStock}...on Furniture{inStock isHeavy}}}" } } @@ -210,6 +217,7 @@ Scenario: prunes unfilled type conditions "kind": "Fetch", "serviceName": "product", "variableUsages": ["upc"], + "operationKind": "query", "operation": "query($upc:String!){product(upc:$upc){__typename ...on Book{__typename isbn}...on Furniture{__typename sku}}}" }, { @@ -237,6 +245,7 @@ Scenario: prunes unfilled type conditions } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{inStock isCheckedOut}...on Furniture{inStock isHeavy}}}" } } @@ -272,6 +281,7 @@ Scenario: fetches interfaces returned from other services "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id}}" }, { @@ -291,6 +301,7 @@ Scenario: fetches interfaces returned from other services } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{__typename upc}}}}}}" } }, @@ -314,6 +325,7 @@ Scenario: fetches interfaces returned from other services } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title}}}" } }, @@ -342,6 +354,7 @@ Scenario: fetches interfaces returned from other services } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{price}...on Furniture{price}}}" } } @@ -379,6 +392,7 @@ Scenario: fetches composite fields from a foreign type casted to an interface [@ "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id}}" }, { @@ -398,6 +412,7 @@ Scenario: fetches composite fields from a foreign type casted to an interface [@ } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{__typename upc}}}}}}" } }, @@ -429,6 +444,7 @@ Scenario: fetches composite fields from a foreign type casted to an interface [@ } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{price}...on Furniture{price}}}" } }, @@ -452,6 +468,7 @@ Scenario: fetches composite fields from a foreign type casted to an interface [@ } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -474,6 +491,7 @@ Scenario: fetches composite fields from a foreign type casted to an interface [@ } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}" } } @@ -508,6 +526,7 @@ Scenario: allows for extending an interface from another service with fields "kind": "Fetch", "serviceName": "product", "variableUsages": ["upc"], + "operationKind": "query", "operation": "query($upc:String!){product(upc:$upc){__typename ...on Book{__typename isbn}...on Furniture{__typename upc}}}" }, { @@ -535,6 +554,7 @@ Scenario: allows for extending an interface from another service with fields } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{reviews{body}}...on Furniture{reviews{body}}}}" } } @@ -577,6 +597,7 @@ Scenario: handles unions from the same service "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id}}" }, { @@ -596,6 +617,7 @@ Scenario: handles unions from the same service } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{__typename upc}}}}}}" } }, @@ -624,6 +646,7 @@ Scenario: handles unions from the same service } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{price}...on Furniture{price brand{__typename ...on Ikea{asile}...on Amazon{referrer}}}}}" } } diff --git a/query-planner-js/src/__tests__/features/basic/aliases.feature b/query-planner-js/src/__tests__/features/basic/aliases.feature index 87a0b9c93..ed2ff480c 100644 --- a/query-planner-js/src/__tests__/features/basic/aliases.feature +++ b/query-planner-js/src/__tests__/features/basic/aliases.feature @@ -22,6 +22,7 @@ Scenario: supports simple aliases "kind": "Fetch", "serviceName": "product", "variableUsages": ["upc"], + "operationKind": "query", "operation": "query($upc:String!){product(upc:$upc){__typename ...on Book{__typename isbn}...on Furniture{name title:name}}}" }, { @@ -41,6 +42,7 @@ Scenario: supports simple aliases } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -63,6 +65,7 @@ Scenario: supports simple aliases } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title:name name}}}" } } @@ -98,6 +101,7 @@ Scenario: supports aliases of root fields on subservices "kind": "Fetch", "serviceName": "product", "variableUsages": ["upc"], + "operationKind": "query", "operation": "query($upc:String!){product(upc:$upc){__typename ...on Book{__typename isbn}...on Furniture{__typename upc name title:name}}}" }, { @@ -128,6 +132,7 @@ Scenario: supports aliases of root fields on subservices } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{reviews{body}productReviews:reviews{body}}...on Furniture{reviews{body}productReviews:reviews{body}}}}" } }, @@ -151,6 +156,7 @@ Scenario: supports aliases of root fields on subservices } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -173,6 +179,7 @@ Scenario: supports aliases of root fields on subservices } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title:name name}}}" } } @@ -216,6 +223,7 @@ Scenario: supports aliases of nested fields on subservices "kind": "Fetch", "serviceName": "product", "variableUsages": ["upc"], + "operationKind": "query", "operation": "query($upc:String!){product(upc:$upc){__typename ...on Book{__typename isbn}...on Furniture{__typename upc name title:name}}}" }, { @@ -246,6 +254,7 @@ Scenario: supports aliases of nested fields on subservices } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{reviews{content:body body}productReviews:reviews{body reviewer:author{name:username}}}...on Furniture{reviews{content:body body}productReviews:reviews{body reviewer:author{name:username}}}}}" } }, @@ -269,6 +278,7 @@ Scenario: supports aliases of nested fields on subservices } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -291,6 +301,7 @@ Scenario: supports aliases of nested fields on subservices } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title:name name}}}" } } diff --git a/query-planner-js/src/__tests__/features/basic/boolean.feature b/query-planner-js/src/__tests__/features/basic/boolean.feature index f2d83d0fd..988fb56e1 100644 --- a/query-planner-js/src/__tests__/features/basic/boolean.feature +++ b/query-planner-js/src/__tests__/features/basic/boolean.feature @@ -25,6 +25,7 @@ Scenario: supports @skip when a boolean condition is met "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{body author@skip(if:true){__typename id}}}" }, { @@ -44,6 +45,7 @@ Scenario: supports @skip when a boolean condition is met } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User@skip(if:true){name{first}}}}" } } @@ -72,6 +74,7 @@ Scenario: supports @skip when a boolean condition is met (variable driven) "kind": "Fetch", "serviceName": "reviews", "variableUsages": ["skip"], + "operationKind": "query", "operation": "query($skip:Boolean!=true){topReviews{body author@skip(if:$skip){username}}}" } } @@ -102,6 +105,7 @@ Scenario: supports @skip when a boolean condition is not met "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{body author@skip(if:false){__typename id}}}" }, { @@ -121,6 +125,7 @@ Scenario: supports @skip when a boolean condition is not met } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User@skip(if:false){name{first}}}}" } } @@ -154,6 +159,7 @@ Scenario: supports @skip when a boolean condition is not met (variable driven) "kind": "Fetch", "serviceName": "reviews", "variableUsages": ["skip"], + "operationKind": "query", "operation": "query($skip:Boolean!){topReviews{body author@skip(if:$skip){__typename id}}}" }, { @@ -173,6 +179,7 @@ Scenario: supports @skip when a boolean condition is not met (variable driven) } ], "variableUsages": ["skip"], + "operationKind": "query", "operation": "query($representations:[_Any!]!$skip:Boolean!){_entities(representations:$representations){...on User@skip(if:$skip){name{first}}}}" } } @@ -201,6 +208,7 @@ Scenario: supports @include when a boolean condition is not met "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{body author@include(if:false){username}}}" } } @@ -226,6 +234,7 @@ Scenario: supports @include when a boolean condition is not met (variable driven "kind": "Fetch", "serviceName": "reviews", "variableUsages": ["include"], + "operationKind": "query", "operation": "query($include:Boolean!=false){topReviews{body author@include(if:$include){username}}}" } } @@ -256,6 +265,7 @@ Scenario: supports @include when a boolean condition is met "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{body author@include(if:true){__typename id}}}" }, { @@ -275,6 +285,7 @@ Scenario: supports @include when a boolean condition is met } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User@include(if:true){name{first}}}}" } } @@ -310,6 +321,7 @@ Scenario: supports @include when a boolean condition is met (variable driven) "kind": "Fetch", "serviceName": "reviews", "variableUsages": ["include"], + "operationKind": "query", "operation": "query($include:Boolean!){topReviews{body author@include(if:$include){__typename id}}}" }, { @@ -329,6 +341,7 @@ Scenario: supports @include when a boolean condition is met (variable driven) } ], "variableUsages": ["include"], + "operationKind": "query", "operation": "query($representations:[_Any!]!$include:Boolean!){_entities(representations:$representations){...on User@include(if:$include){name{first}}}}" } } diff --git a/query-planner-js/src/__tests__/features/basic/build-query-plan.feature b/query-planner-js/src/__tests__/features/basic/build-query-plan.feature index f47e284ee..d002e3366 100644 --- a/query-planner-js/src/__tests__/features/basic/build-query-plan.feature +++ b/query-planner-js/src/__tests__/features/basic/build-query-plan.feature @@ -27,6 +27,7 @@ Scenario: should not confuse union types with overlapping field names "kind": "Fetch", "serviceName": "documents", "variableUsages": [], + "operationKind": "query", "operation": "{body{__typename ...on Image{attributes{url}}...on Text{attributes{bold text}}}}" } } @@ -51,6 +52,7 @@ Scenario: should use a single fetch when requesting a root field from one servic "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{name{first}}}" } } @@ -81,6 +83,7 @@ Scenario: should use two independent fetches when requesting root fields from tw "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{name{first}}}" }, { @@ -90,6 +93,7 @@ Scenario: should use two independent fetches when requesting root fields from tw "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{topProducts{__typename ...on Book{__typename isbn}...on Furniture{name}}}" }, { @@ -109,6 +113,7 @@ Scenario: should use two independent fetches when requesting root fields from tw } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -131,6 +136,7 @@ Scenario: should use two independent fetches when requesting root fields from tw } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}" } } @@ -164,6 +170,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{topProducts{__typename ...on Book{__typename isbn}...on Furniture{name}}product(upc:\"1\"){__typename ...on Book{__typename isbn}...on Furniture{name}}}" }, { @@ -189,6 +196,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -211,6 +219,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}" } } @@ -236,6 +245,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -258,6 +268,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}" } } @@ -292,6 +303,7 @@ Scenario: should use a single fetch when requesting relationship subfields from "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{body author{reviews{body}}}}" } } @@ -320,6 +332,7 @@ Scenario: should use a single fetch when requesting relationship subfields and p "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{body author{id reviews{body}}}}" } } @@ -350,6 +363,7 @@ Scenario: when requesting an extension field from another service, it should add "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id name{first}}}" }, { @@ -369,6 +383,7 @@ Scenario: when requesting an extension field from another service, it should add } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{body}}}}" } } @@ -399,6 +414,7 @@ Scenario: when requesting an extension field from another service, when the pare "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id}}" }, { @@ -418,6 +434,7 @@ Scenario: when requesting an extension field from another service, when the pare } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{body}}}}" } } @@ -449,6 +466,7 @@ Scenario: when requesting an extension field from another service, should only a "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id}}" }, { @@ -468,6 +486,7 @@ Scenario: when requesting an extension field from another service, should only a } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{body}numberOfReviews}}}" } } @@ -501,6 +520,7 @@ Scenario: when requesting a composite field with subfields from another service, "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{body author{__typename id}}}" }, { @@ -520,6 +540,7 @@ Scenario: when requesting a composite field with subfields from another service, } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{name{first}}}}" } } @@ -548,6 +569,7 @@ Scenario: when requesting a composite field with subfields from another service, "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{topCars{__typename id price}}" }, { @@ -568,6 +590,7 @@ Scenario: when requesting a composite field with subfields from another service, } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Car{retailPrice}}}" } } @@ -600,6 +623,7 @@ Scenario: when requesting a composite field with subfields from another service, "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{author{__typename id}}}" }, { @@ -619,6 +643,7 @@ Scenario: when requesting a composite field with subfields from another service, } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{name{first}}}}" } } @@ -649,6 +674,7 @@ Scenario: when requesting a relationship field with extension subfields from a d "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{author{__typename id}}}" }, { @@ -668,6 +694,7 @@ Scenario: when requesting a relationship field with extension subfields from a d } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{birthDate}}}" } } @@ -693,6 +720,7 @@ Scenario: for abstract types, it should add __typename when fetching objects of "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{topProducts{__typename ...on Book{price}...on Furniture{price}}}" } } @@ -721,6 +749,7 @@ Scenario: should break up when traversing an extension field on an interface typ "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{topProducts{__typename ...on Book{__typename isbn price}...on Furniture{__typename upc price}}}" }, { @@ -748,6 +777,7 @@ Scenario: should break up when traversing an extension field on an interface typ } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{reviews{body}}...on Furniture{reviews{body}}}}" } } @@ -781,6 +811,7 @@ Scenario: interface fragments should expand into possible types only "kind": "Fetch", "serviceName": "books", "variableUsages": [], + "operationKind": "query", "operation": "{books{__typename isbn title year}}" }, { @@ -802,6 +833,7 @@ Scenario: interface fragments should expand into possible types only } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}" } } @@ -829,6 +861,7 @@ Scenario: interface inside interface should expand into possible types only "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{product(upc:\"\"){__typename ...on Book{details{country}}...on Furniture{details{country}}}}" } } @@ -870,6 +903,7 @@ Scenario: should properly expand nested unions with inline fragments "kind": "Fetch", "serviceName": "documents", "variableUsages": [], + "operationKind": "query", "operation": "{body{__typename ...on Image{attributes{url}}...on Text{attributes{bold}}}}" } } @@ -912,6 +946,7 @@ Scenario: deduplicates fields / selections regardless of adjacency and type cond "kind": "Fetch", "serviceName": "documents", "variableUsages": [], + "operationKind": "query", "operation": "{body{__typename ...on Text{attributes{bold text}}}}" } } @@ -947,6 +982,7 @@ Scenario: deduplicates fields / selections regardless of adjacency and type cond "kind": "Fetch", "serviceName": "documents", "variableUsages": [], + "operationKind": "query", "operation": "{body{__typename ...on Text{attributes{bold text}}}}" } } @@ -972,6 +1008,7 @@ Scenario: supports basic, single-service mutation "username", "password" ], + "operationKind": "mutation", "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){id}}" } } @@ -1005,6 +1042,7 @@ Scenario: supports mutations with a cross-service request "username", "password" ], + "operationKind": "mutation", "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){__typename id}}" }, { @@ -1032,6 +1070,7 @@ Scenario: supports mutations with a cross-service request } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{upc}}}}}}" } }, @@ -1063,6 +1102,7 @@ Scenario: supports mutations with a cross-service request } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{upc}}}" } } @@ -1097,6 +1137,7 @@ Scenario: returning across service boundaries "upc", "body" ], + "operationKind": "mutation", "operation": "mutation($upc:String!$body:String!){reviewProduct(input:{upc:$upc body:$body}){__typename ...on Furniture{__typename upc}}}" }, { @@ -1124,6 +1165,7 @@ Scenario: returning across service boundaries } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Furniture{name}}}" } } @@ -1170,6 +1212,7 @@ Scenario: supports multiple root mutations "username", "password" ], + "operationKind": "mutation", "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){__typename id}}" }, { @@ -1197,6 +1240,7 @@ Scenario: supports multiple root mutations } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{upc}}}}}}" } }, @@ -1228,6 +1272,7 @@ Scenario: supports multiple root mutations } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{upc}}}" } }, @@ -1238,6 +1283,7 @@ Scenario: supports multiple root mutations "upc", "body" ], + "operationKind": "mutation", "operation": "mutation($upc:String!$body:String!){reviewProduct(input:{upc:$upc body:$body}){__typename ...on Furniture{__typename upc}}}" }, { @@ -1265,6 +1311,7 @@ Scenario: supports multiple root mutations } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Furniture{name}}}" } } @@ -1319,6 +1366,7 @@ Scenario: multiple root mutations with correct service order "body", "updatedReview" ], + "operationKind": "mutation", "operation": "mutation($upc:String!$body:String!$updatedReview:UpdateReviewInput!){reviewProduct(input:{upc:$upc body:$body}){__typename ...on Furniture{upc}}updateReview(review:$updatedReview){id body}}" }, { @@ -1328,6 +1376,7 @@ Scenario: multiple root mutations with correct service order "username", "password" ], + "operationKind": "mutation", "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){__typename id}}" }, { @@ -1355,6 +1404,7 @@ Scenario: multiple root mutations with correct service order } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{upc}}}}}}" } }, @@ -1386,6 +1436,7 @@ Scenario: multiple root mutations with correct service order } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{upc}}}" } }, @@ -1395,6 +1446,7 @@ Scenario: multiple root mutations with correct service order "variableUsages": [ "reviewId" ], + "operationKind": "mutation", "operation": "mutation($reviewId:ID!){deleteReview(id:$reviewId)}" } ] @@ -1426,6 +1478,7 @@ Scenario: supports arrays "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id metadata{description address}}}" }, { @@ -1463,6 +1516,7 @@ Scenario: supports arrays } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{goodDescription}}}" } } diff --git a/query-planner-js/src/__tests__/features/basic/custom-directives.feature b/query-planner-js/src/__tests__/features/basic/custom-directives.feature index 89ba93e56..b4fb6a8bd 100644 --- a/query-planner-js/src/__tests__/features/basic/custom-directives.feature +++ b/query-planner-js/src/__tests__/features/basic/custom-directives.feature @@ -17,6 +17,7 @@ Scenario: successfully passes directives along in requests to an underlying serv "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{body@stream}}" } } @@ -47,6 +48,7 @@ Scenario: successfully passes directives and their variables along in requests t "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{body@stream author@transform(from:\"JSON\"){__typename id}}}" }, { @@ -66,6 +68,7 @@ Scenario: successfully passes directives and their variables along in requests t } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{name@stream{first}}}}" } } diff --git a/query-planner-js/src/__tests__/features/basic/execution-style.feature b/query-planner-js/src/__tests__/features/basic/execution-style.feature index 87f86deb2..871536b0e 100644 --- a/query-planner-js/src/__tests__/features/basic/execution-style.feature +++ b/query-planner-js/src/__tests__/features/basic/execution-style.feature @@ -23,12 +23,14 @@ Scenario: supports parallel root fields "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{username}}" }, { "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{body}}" } ] diff --git a/query-planner-js/src/__tests__/features/basic/fragments.feature b/query-planner-js/src/__tests__/features/basic/fragments.feature index b6fea12b1..afe415ae9 100644 --- a/query-planner-js/src/__tests__/features/basic/fragments.feature +++ b/query-planner-js/src/__tests__/features/basic/fragments.feature @@ -14,7 +14,7 @@ Scenario: supports inline fragments (one level) """ Then query plan """ - {"kind":"QueryPlan","node":{"kind":"Fetch","serviceName":"accounts","variableUsages":[],"operation":"{me{username}}"}} + {"kind":"QueryPlan","node":{"kind":"Fetch","serviceName":"accounts","variableUsages":[],"operationKind": "query","operation":"{me{username}}"}} """ # important things: calls [accounts, reviews, products, books] @@ -55,6 +55,7 @@ Scenario: supports inline fragments (multi level) "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id username}}" }, { @@ -74,6 +75,7 @@ Scenario: supports inline fragments (multi level) } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{body product{__typename ...on Book{__typename isbn}...on Furniture{__typename upc}}}}}}" } }, @@ -97,6 +99,7 @@ Scenario: supports inline fragments (multi level) } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title}}}" } }, @@ -117,6 +120,7 @@ Scenario: supports inline fragments (multi level) } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Furniture{name}}}" } } @@ -148,6 +152,7 @@ Scenario: supports named fragments (one level) "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{username}}" } } @@ -181,6 +186,7 @@ Scenario: supports multiple named fragments (one level, mixed ordering) "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{username name{first}}}" } } @@ -216,6 +222,7 @@ Scenario: supports multiple named fragments (multi level, mixed ordering) "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id username}}" }, { @@ -235,6 +242,7 @@ Scenario: supports multiple named fragments (multi level, mixed ordering) } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{body}}}}" } } @@ -271,6 +279,7 @@ Scenario: supports variables within fragments "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id username}}" }, { @@ -290,6 +299,7 @@ Scenario: supports variables within fragments } ], "variableUsages": ["format"], + "operationKind": "query", "operation": "query($representations:[_Any!]!$format:Boolean){_entities(representations:$representations){...on User{reviews{body(format:$format)}}}}" } } @@ -317,6 +327,7 @@ Scenario: supports root fragments "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{username}}" } } @@ -343,5 +354,5 @@ Scenario: supports directives on inline fragments (https://github.com/apollograp """ Then query plan """ - {"kind":"QueryPlan","node":{"kind":"Fetch","serviceName":"product","variableUsages":[],"operation":"{vehicle(id:\"rav4\"){__typename ...on Car@fragmentDirective{price thing{__typename ...on Ikea{asile}}}...on Van{price@stream}}}"}} + {"kind":"QueryPlan","node":{"kind":"Fetch","serviceName":"product","variableUsages":[],"operationKind": "query","operation":"{vehicle(id:\"rav4\"){__typename ...on Car@fragmentDirective{price thing{__typename ...on Ikea{asile}}}...on Van{price@stream}}}"}} """ diff --git a/query-planner-js/src/__tests__/features/basic/mutations.feature b/query-planner-js/src/__tests__/features/basic/mutations.feature index 2841c82e2..d8a56a651 100644 --- a/query-planner-js/src/__tests__/features/basic/mutations.feature +++ b/query-planner-js/src/__tests__/features/basic/mutations.feature @@ -24,6 +24,7 @@ Scenario: supports mutations "kind": "Fetch", "serviceName": "accounts", "variableUsages": ["username", "password"], + "operationKind": "mutation", "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){__typename id}}" }, { @@ -43,6 +44,7 @@ Scenario: supports mutations } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{upc}}}}}}" } }, @@ -63,6 +65,7 @@ Scenario: supports mutations } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{upc}}}" } } @@ -93,6 +96,7 @@ Scenario: mutations across service boundaries "kind": "Fetch", "serviceName": "reviews", "variableUsages": ["upc", "body"], + "operationKind": "mutation", "operation": "mutation($upc:String!$body:String!){reviewProduct(input:{upc:$upc body:$body}){__typename ...on Furniture{__typename upc}}}" }, { @@ -112,6 +116,7 @@ Scenario: mutations across service boundaries } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Furniture{name}}}" } } @@ -155,6 +160,7 @@ Scenario: multiple root mutations "kind": "Fetch", "serviceName": "accounts", "variableUsages": ["username", "password"], + "operationKind": "mutation", "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){__typename id}}" }, { @@ -174,6 +180,7 @@ Scenario: multiple root mutations } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{upc}}}}}}" } }, @@ -194,6 +201,7 @@ Scenario: multiple root mutations } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{upc}}}" } }, @@ -201,6 +209,7 @@ Scenario: multiple root mutations "kind": "Fetch", "serviceName": "reviews", "variableUsages": ["upc", "body"], + "operationKind": "mutation", "operation": "mutation($upc:String!$body:String!){reviewProduct(input:{upc:$upc body:$body}){__typename ...on Furniture{__typename upc}}}" }, { @@ -220,6 +229,7 @@ Scenario: multiple root mutations } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Furniture{name}}}" } } @@ -270,12 +280,14 @@ Scenario: multiple root mutations with correct service order "kind": "Fetch", "serviceName": "reviews", "variableUsages": ["upc", "body", "updatedReview"], + "operationKind": "mutation", "operation": "mutation($upc:String!$body:String!$updatedReview:UpdateReviewInput!){reviewProduct(input:{upc:$upc body:$body}){__typename ...on Furniture{upc}}updateReview(review:$updatedReview){id body}}" }, { "kind": "Fetch", "serviceName": "accounts", "variableUsages": ["username", "password"], + "operationKind": "mutation", "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){__typename id}}" }, { @@ -295,6 +307,7 @@ Scenario: multiple root mutations with correct service order } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{upc}}}}}}" } }, @@ -315,6 +328,7 @@ Scenario: multiple root mutations with correct service order } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{upc}}}" } }, @@ -322,6 +336,7 @@ Scenario: multiple root mutations with correct service order "kind": "Fetch", "serviceName": "reviews", "variableUsages": ["reviewId"], + "operationKind": "mutation", "operation": "mutation($reviewId:ID!){deleteReview(id:$reviewId)}" } ] diff --git a/query-planner-js/src/__tests__/features/basic/provides.feature b/query-planner-js/src/__tests__/features/basic/provides.feature index 54f5d7988..58fbeb6e8 100644 --- a/query-planner-js/src/__tests__/features/basic/provides.feature +++ b/query-planner-js/src/__tests__/features/basic/provides.feature @@ -19,6 +19,7 @@ Scenario: does not have to go to another service when field is given "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{author{username}}}" } } @@ -50,6 +51,7 @@ Scenario: does not load fields provided even when going to other service "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{topReviews{author{__typename id username}}}" }, { @@ -69,6 +71,7 @@ Scenario: does not load fields provided even when going to other service } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{name{first}}}}" } } diff --git a/query-planner-js/src/__tests__/features/basic/requires.feature b/query-planner-js/src/__tests__/features/basic/requires.feature index 74aa90e15..23122094f 100644 --- a/query-planner-js/src/__tests__/features/basic/requires.feature +++ b/query-planner-js/src/__tests__/features/basic/requires.feature @@ -27,6 +27,7 @@ Scenario: supports passing additional fields defined by a requires "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id}}" }, { @@ -46,6 +47,7 @@ Scenario: supports passing additional fields defined by a requires } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}}}}}}" } }, @@ -66,6 +68,7 @@ Scenario: supports passing additional fields defined by a requires } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -88,6 +91,7 @@ Scenario: supports passing additional fields defined by a requires } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}" } } diff --git a/query-planner-js/src/__tests__/features/basic/single-service.feature b/query-planner-js/src/__tests__/features/basic/single-service.feature index 59cd7d9f3..3113caca6 100644 --- a/query-planner-js/src/__tests__/features/basic/single-service.feature +++ b/query-planner-js/src/__tests__/features/basic/single-service.feature @@ -35,6 +35,7 @@ Scenario: does not remove __typename if that is all that is requested on an enti "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename}}" } } @@ -59,6 +60,7 @@ Scenario: does not remove __typename if that is all that is requested on a value "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{account{__typename}}}" } } @@ -83,6 +85,7 @@ Scenario: does not remove __typename if that is all that is requested on a union "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{accountType{__typename}}}" } } diff --git a/query-planner-js/src/__tests__/features/basic/value-types.feature b/query-planner-js/src/__tests__/features/basic/value-types.feature index 69ca36c22..48d6bb983 100644 --- a/query-planner-js/src/__tests__/features/basic/value-types.feature +++ b/query-planner-js/src/__tests__/features/basic/value-types.feature @@ -46,6 +46,7 @@ Scenario: resolves value types within their respective services "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{topProducts(first:10){__typename ...on Book{__typename isbn upc}...on Furniture{__typename upc metadata{__typename ...on KeyValue{key value}...on Error{code message}}}}}" }, { @@ -76,6 +77,7 @@ Scenario: resolves value types within their respective services } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{reviews{metadata{__typename ...on KeyValue{key value}...on Error{code message}}}}...on Furniture{reviews{metadata{__typename ...on KeyValue{key value}...on Error{code message}}}}}}" } }, @@ -96,6 +98,7 @@ Scenario: resolves value types within their respective services } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{metadata{__typename ...on KeyValue{key value}...on Error{code message}}}}}" } } diff --git a/query-planner-js/src/__tests__/features/basic/variables.feature b/query-planner-js/src/__tests__/features/basic/variables.feature index 1368dd53e..c35238c9c 100644 --- a/query-planner-js/src/__tests__/features/basic/variables.feature +++ b/query-planner-js/src/__tests__/features/basic/variables.feature @@ -21,6 +21,7 @@ Scenario: passes variables to root fields "kind": "Fetch", "serviceName": "product", "variableUsages": ["upc"], + "operationKind": "query", "operation": "query($upc:String!){product(upc:$upc){__typename ...on Book{__typename isbn}...on Furniture{name}}}" }, { @@ -40,6 +41,7 @@ Scenario: passes variables to root fields } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -62,6 +64,7 @@ Scenario: passes variables to root fields } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}" } } @@ -91,6 +94,7 @@ Scenario: supports default variables in a variable definition "kind": "Fetch", "serviceName": "product", "variableUsages": ["upc"], + "operationKind": "query", "operation": "query($upc:String=\"1\"){product(upc:$upc){__typename ...on Book{__typename isbn}...on Furniture{name}}}" }, { @@ -110,6 +114,7 @@ Scenario: supports default variables in a variable definition } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}" } }, @@ -132,6 +137,7 @@ Scenario: supports default variables in a variable definition } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}" } } @@ -163,6 +169,7 @@ Scenario: passes variables to nested services "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], + "operationKind": "query", "operation": "{me{__typename id}}" }, { @@ -182,6 +189,7 @@ Scenario: passes variables to nested services } ], "variableUsages": ["format"], + "operationKind": "query", "operation": "query($representations:[_Any!]!$format:Boolean){_entities(representations:$representations){...on User{reviews{body(format:$format)}}}}" } } @@ -216,6 +224,7 @@ Scenario: works with default variables in the schema "kind": "Fetch", "serviceName": "books", "variableUsages": ["libraryId"], + "operationKind": "query", "operation": "query($libraryId:ID!){library(id:$libraryId){__typename id name}}" }, { @@ -236,6 +245,7 @@ Scenario: works with default variables in the schema } ], "variableUsages": ["userId"], + "operationKind": "query", "operation": "query($representations:[_Any!]!$userId:ID){_entities(representations:$representations){...on Library{userAccount(id:$userId){id name{first}}}}}" } } @@ -261,6 +271,7 @@ Scenario: String arguments with quotes that need to be escaped. "kind": "Fetch", "serviceName": "product", "variableUsages": [], + "operationKind": "query", "operation": "{vehicle(id:\"{\\\"make\\\":\\\"Toyota\\\",\\\"model\\\":\\\"Rav4\\\",\\\"trim\\\":\\\"Limited\\\"}\"){__typename ...on Car{description}...on Van{description}}}" } } diff --git a/query-planner-js/src/__tests__/features/multiple-keys/multiple-keys.feature b/query-planner-js/src/__tests__/features/multiple-keys/multiple-keys.feature index c6c374336..9d8d9b7c2 100644 --- a/query-planner-js/src/__tests__/features/multiple-keys/multiple-keys.feature +++ b/query-planner-js/src/__tests__/features/multiple-keys/multiple-keys.feature @@ -24,6 +24,7 @@ Feature: Query Planning > Multiple keys "kind": "Fetch", "serviceName": "reviews", "variableUsages": [], + "operationKind": "query", "operation": "{reviews{body author{__typename id}}}" }, { @@ -43,6 +44,7 @@ Feature: Query Planning > Multiple keys } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{ssn name}}}" } }, @@ -63,6 +65,7 @@ Feature: Query Planning > Multiple keys } ], "variableUsages": [], + "operationKind": "query", "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{risk}}}" } } diff --git a/query-planner-js/src/buildPlan.ts b/query-planner-js/src/buildPlan.ts index 581ec20a0..602a2c059 100644 --- a/query-planner-js/src/buildPlan.ts +++ b/query-planner-js/src/buildPlan.ts @@ -68,7 +68,7 @@ import { terminateWithNonRequestedTypenameField, getLocallySatisfiableKey, } from "@apollo/query-graphs"; -import { DocumentNode, stripIgnoredCharacters, print, GraphQLError, parse } from "graphql"; +import { DocumentNode, OperationDefinitionNode, stripIgnoredCharacters, print, GraphQLError, parse } from "graphql"; import { QueryPlan, ResponsePath, SequenceNode, PlanNode, ParallelNode, FetchNode, trimSelectionNodes } from "./QueryPlan"; const debug = newDebugLogger('plan'); @@ -801,6 +801,7 @@ class FetchGroup { requires: inputNodes ? trimSelectionNodes(inputNodes.selections) : undefined, variableUsages: this.selection.usedVariables().map(v => v.name), operation: stripIgnoredCharacters(print(operation)), + operationKind: (operation.definitions[0] as OperationDefinitionNode).operation, }; return this.isTopLevel From b6edfb668f9c2345905edfd7d010d6616349aecd Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 24 Jan 2022 14:31:20 +0100 Subject: [PATCH 2/4] update snapshots --- .../src/snapshots/router_bridge__plan__tests__it_works.snap | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/router-bridge/src/snapshots/router_bridge__plan__tests__it_works.snap b/router-bridge/src/snapshots/router_bridge__plan__tests__it_works.snap index ae93d461d..4ca4b7257 100644 --- a/router-bridge/src/snapshots/router_bridge__plan__tests__it_works.snap +++ b/router-bridge/src/snapshots/router_bridge__plan__tests__it_works.snap @@ -10,7 +10,8 @@ expression: "serde_json::to_string_pretty(&plan::(Operational "kind": "Fetch", "serviceName": "accounts", "variableUsages": [], - "operation": "{me{name{first last}}}" + "operation": "{me{name{first last}}}", + "operationKind": "query" } } } From 0e6e5358f46febbc745a050b92bba8a87da2eb16 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 10 Feb 2022 11:31:30 +0100 Subject: [PATCH 3/4] properly set the operation kind in FetchNode --- query-planner-js/src/buildPlan.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/query-planner-js/src/buildPlan.ts b/query-planner-js/src/buildPlan.ts index 602a2c059..dfbafc32e 100644 --- a/query-planner-js/src/buildPlan.ts +++ b/query-planner-js/src/buildPlan.ts @@ -68,7 +68,7 @@ import { terminateWithNonRequestedTypenameField, getLocallySatisfiableKey, } from "@apollo/query-graphs"; -import { DocumentNode, OperationDefinitionNode, stripIgnoredCharacters, print, GraphQLError, parse } from "graphql"; +import { stripIgnoredCharacters, print, GraphQLError, parse, OperationTypeNode } from "graphql"; import { QueryPlan, ResponsePath, SequenceNode, PlanNode, ParallelNode, FetchNode, trimSelectionNodes } from "./QueryPlan"; const debug = newDebugLogger('plan'); @@ -800,8 +800,8 @@ class FetchGroup { serviceName: this.subgraphName, requires: inputNodes ? trimSelectionNodes(inputNodes.selections) : undefined, variableUsages: this.selection.usedVariables().map(v => v.name), - operation: stripIgnoredCharacters(print(operation)), - operationKind: (operation.definitions[0] as OperationDefinitionNode).operation, + operation: stripIgnoredCharacters(print(operationToDocument(operation))), + operationKind: operation.rootKind as OperationTypeNode, }; return this.isTopLevel @@ -1672,7 +1672,7 @@ function operationForEntitiesFetch( selectionSet: SelectionSet, allVariableDefinitions: VariableDefinitions, fragments?: NamedFragments -): DocumentNode { +): Operation { const variableDefinitions = new VariableDefinitions(); variableDefinitions.add(representationsVariableDefinition(subgraphSchema)); variableDefinitions.addAll(allVariableDefinitions.filter(selectionSet.usedVariables())); @@ -1689,7 +1689,7 @@ function operationForEntitiesFetch( selectionSet )); - return operationToDocument(new Operation('query', entitiesCall, variableDefinitions).optimize(fragments)); + return new Operation('query', entitiesCall, variableDefinitions).optimize(fragments); } // Wraps the given nodes in a ParallelNode or SequenceNode, unless there's only @@ -1716,7 +1716,6 @@ function operationForQueryFetch( selectionSet: SelectionSet, allVariableDefinitions: VariableDefinitions, fragments?: NamedFragments -): DocumentNode { - const operation = new Operation(rootKind, selectionSet, allVariableDefinitions.filter(selectionSet.usedVariables())).optimize(fragments); - return operationToDocument(operation); +): Operation { + return new Operation(rootKind, selectionSet, allVariableDefinitions.filter(selectionSet.usedVariables())).optimize(fragments); } From 922fc987ccb588691a4087524ab5f6d5389a7116 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 10 Feb 2022 15:25:14 +0100 Subject: [PATCH 4/4] convert SchemaRootKind to OperationTypeNode --- query-planner-js/src/buildPlan.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/query-planner-js/src/buildPlan.ts b/query-planner-js/src/buildPlan.ts index dfbafc32e..12a98d532 100644 --- a/query-planner-js/src/buildPlan.ts +++ b/query-planner-js/src/buildPlan.ts @@ -801,7 +801,7 @@ class FetchGroup { requires: inputNodes ? trimSelectionNodes(inputNodes.selections) : undefined, variableUsages: this.selection.usedVariables().map(v => v.name), operation: stripIgnoredCharacters(print(operationToDocument(operation))), - operationKind: operation.rootKind as OperationTypeNode, + operationKind:schemaRootKindToOperationKind(operation.rootKind), }; return this.isTopLevel @@ -820,6 +820,14 @@ class FetchGroup { } } +function schemaRootKindToOperationKind(operation: SchemaRootKind): OperationTypeNode { + switch(operation) { + case "query": return OperationTypeNode.QUERY; + case "mutation": return OperationTypeNode.MUTATION; + case "subscription": return OperationTypeNode.SUBSCRIPTION; + } +} + function removeInPlace(value: T, array: T[]) { const idx = array.indexOf(value); if (idx >= 0) {