Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/violet-jokes-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-hive/router-runtime': patch
---

Expose the query plan in graphql result extensions when setting `queryPlanInExtensions` to true in context
97 changes: 56 additions & 41 deletions packages/router-runtime/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,51 +84,66 @@ export function unifiedGraphHandler(
executionRequest.operationName || null,
),
(queryPlan) =>
executeQueryPlan({
supergraphSchema,
executionRequest,
onSubgraphExecute(subgraphName, executionRequest) {
const subschema = getSubschema(subgraphName);
if (subschema.transforms?.length) {
const transforms = subschema.transforms;
const transformationContext = Object.create(null);
for (const transform of transforms) {
if (transform.transformRequest) {
executionRequest = transform.transformRequest(
executionRequest,
undefined as any,
transformationContext,
);
}
}
return handleMaybePromise(
() => opts.onSubgraphExecute(subgraphName, executionRequest),
(executionResult) => {
function handleResult(executionResult: ExecutionResult) {
for (const transform of transforms.toReversed()) {
if (transform.transformResult) {
executionResult = transform.transformResult(
executionResult,
undefined as any,
transformationContext,
);
}
handleMaybePromise(
() =>
executeQueryPlan({
supergraphSchema,
executionRequest,
onSubgraphExecute(subgraphName, executionRequest) {
const subschema = getSubschema(subgraphName);
if (subschema.transforms?.length) {
const transforms = subschema.transforms;
const transformationContext = Object.create(null);
for (const transform of transforms) {
if (transform.transformRequest) {
executionRequest = transform.transformRequest(
executionRequest,
undefined as any,
transformationContext,
);
}
return executionResult;
}
if (isAsyncIterable(executionResult)) {
return mapAsyncIterator(executionResult, (result) =>
handleResult(result),
);
}
return handleResult(executionResult);
},
);
return handleMaybePromise(
() =>
opts.onSubgraphExecute(subgraphName, executionRequest),
(executionResult) => {
function handleResult(
executionResult: ExecutionResult,
) {
for (const transform of transforms.toReversed()) {
if (transform.transformResult) {
executionResult = transform.transformResult(
executionResult,
undefined as any,
transformationContext,
);
}
}
return executionResult;
}
if (isAsyncIterable(executionResult)) {
return mapAsyncIterator(executionResult, (result) =>
handleResult(result),
);
}
return handleResult(executionResult);
},
);
}
return opts.onSubgraphExecute(subgraphName, executionRequest);
},
queryPlan,
}),
(result) => {
if (isAsyncIterable(result)) {
return result;
}
if (executionRequest.context?.['queryPlanInExtensions']) {
result.extensions = { queryPlan };
}
return opts.onSubgraphExecute(subgraphName, executionRequest);
return result;
},
queryPlan,
}),
),
);
},
};
Expand Down
84 changes: 84 additions & 0 deletions packages/router-runtime/tests/handler.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { GatewayPlugin } from '@graphql-hive/gateway-runtime';
import { createGatewayTester } from '@graphql-hive/gateway-testing';
import { expect, it } from 'vitest';
import { unifiedGraphHandler } from '../src/handler';

it('should include the query plan in result extensions when context', async () => {
await using gw = createGatewayTester({
unifiedGraphHandler,
plugins: () => [
{
onContextBuilding({ context, extendContext }) {
if (context.headers['hive-expose-query-plan'] === 'true') {
extendContext({
queryPlanInExtensions: true,
});
}
},
} satisfies GatewayPlugin,
],
subgraphs: [
{
name: 'upstream',
schema: {
typeDefs: /* GraphQL */ `
type Query {
hello: String
}
`,
resolvers: {
Query: {
hello: () => 'world',
},
},
},
},
],
});

await expect(
gw.execute({
query: /* GraphQL */ `
{
hello
}
`,
}),
).resolves.toMatchInlineSnapshot(`
{
"data": {
"hello": "world",
},
}
`);

await expect(
gw.execute({
query: /* GraphQL */ `
{
hello
}
`,
headers: {
'hive-expose-query-plan': 'true',
},
}),
).resolves.toMatchInlineSnapshot(`
{
"data": {
"hello": "world",
},
"extensions": {
"queryPlan": {
"kind": "QueryPlan",
"node": {
"kind": "Fetch",
"operation": "{hello}",
"operationKind": "query",
"serviceName": "upstream",
},
},
},
}
`);
});
Loading