Skip to content

Commit

Permalink
Take subtypes into account when matching type conditions to extract r…
Browse files Browse the repository at this point in the history
…epresentations (#804)

The `executeSelectionSet` function in `executeQueryPlan.ts`, which is used to extract field values from a result object to build up representations to send to a subgraph, only looked for exact matches of the `__typename` to the type condition on an inline fragment.
  • Loading branch information
martijnwalraven committed Jun 10, 2021
1 parent 70e9050 commit 60b43d6
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 5 deletions.
2 changes: 1 addition & 1 deletion gateway-js/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

> The changes noted within this `vNEXT` section have not been released yet. New PRs and commits which introduce changes should include an entry in this `vNEXT` section as part of their development. When a release is being prepared, a new header will be (manually) created below and the appropriate changes within that release will be moved into the new section.
- _Nothing yet! Stay tuned!_
- Take subtypes into account when matching type conditions to extract representations. [PR #804](https://github.com/apollographql/federation/pull/804)

## v0.28.1

Expand Down
60 changes: 60 additions & 0 deletions gateway-js/src/__tests__/executeQueryPlan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1118,4 +1118,64 @@ describe('executeQueryPlan', () => {
}
`);
});

it(`can execute queries with @include on inline fragment with extension field`, async () => {
const operationString = `#graphql
query {
topProducts(first: 5) {
... on Book @include(if: true) {
price
inStock
}
... on Furniture {
price
inStock
}
}
}
`;

const operationDocument = gql(operationString);

const operationContext = buildOperationContext({
schema,
operationDocument,
});

const queryPlan = queryPlanner.buildQueryPlan(operationContext);

const response = await executeQueryPlan(
queryPlan,
serviceMap,
buildRequestContext(),
operationContext,
);

expect(response.data).toMatchInlineSnapshot(`
Object {
"topProducts": Array [
Object {
"inStock": true,
"price": "899",
},
Object {
"inStock": false,
"price": "1299",
},
Object {
"inStock": true,
"price": "54",
},
Object {
"inStock": true,
"price": "39",
},
Object {
"inStock": false,
"price": "29",
},
],
}
`);
});
});
44 changes: 40 additions & 4 deletions gateway-js/src/executeQueryPlan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
TypeNameMetaFieldDef,
GraphQLFieldResolver,
GraphQLFormattedError,
isAbstractType,
GraphQLSchema,
} from 'graphql';
import { Trace, google } from 'apollo-reporting-protobuf';
import { defaultRootOperationNameLookup } from '@apollo/federation';
Expand Down Expand Up @@ -247,7 +249,11 @@ async function executeFetch<TContext>(
const representationToEntity: number[] = [];

entities.forEach((entity, index) => {
const representation = executeSelectionSet(entity, requires);
const representation = executeSelectionSet(
context.operationContext,
entity,
requires,
);
if (representation && representation[TypeNameMetaFieldDef.name]) {
representations.push(representation);
representationToEntity.push(index);
Expand Down Expand Up @@ -401,6 +407,7 @@ async function executeFetch<TContext>(
* @param selectionSet
*/
function executeSelectionSet(
operationContext: OperationContext,
source: Record<string, any> | null,
selections: QueryPlanSelectionNode[],
): Record<string, any> | null {
Expand All @@ -424,10 +431,13 @@ function executeSelectionSet(
}
if (Array.isArray(source[responseName])) {
result[responseName] = source[responseName].map((value: any) =>
selections ? executeSelectionSet(value, selections) : value,
selections
? executeSelectionSet(operationContext, value, selections)
: value,
);
} else if (selections) {
result[responseName] = executeSelectionSet(
operationContext,
source[responseName],
selections,
);
Expand All @@ -441,10 +451,10 @@ function executeSelectionSet(
const typename = source && source['__typename'];
if (!typename) continue;

if (typename === selection.typeCondition) {
if (doesTypeConditionMatch(operationContext.schema, selection.typeCondition, typename)) {
deepMerge(
result,
executeSelectionSet(source, selection.selections),
executeSelectionSet(operationContext, source, selection.selections),
);
}
break;
Expand All @@ -454,6 +464,32 @@ function executeSelectionSet(
return result;
}

function doesTypeConditionMatch(
schema: GraphQLSchema,
typeCondition: string,
typename: string,
): boolean {
if (typeCondition === typename) {
return true;
}

const type = schema.getType(typename);
if (!type) {
return false;
}

const conditionalType = schema.getType(typeCondition);
if (!conditionalType) {
return false;
}

if (isAbstractType(conditionalType)) {
return schema.isSubType(conditionalType, type);
}

return false;
}

function flattenResultsAtPath(value: any, path: ResponsePath): any {
if (path.length === 0) return value;
if (value === undefined || value === null) return value;
Expand Down

0 comments on commit 60b43d6

Please sign in to comment.