Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix fragment expansion nuking @defer and other minor @defer updates #2093

Merged
merged 5 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions gateway-js/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This CHANGELOG pertains only to Apollo Federation packages in the 2.x range. The

## vNext

- Fix issue where fragment expansion can erase applied directives (most notably `@defer`) [PR #2093](https://github.com/apollographql/federation/pull/2093).
- Fix abnormally high memory usage when extracting subgraphs for some fed1 supergraphs (and small other memory footprint improvements) [PR #2089](https://github.com/apollographql/federation/pull/2089).
- Fix issue with fragment reusing code something mistakenly re-expanding fragments [PR #2098](https://github.com/apollographql/federation/pull/2098).
- Fix issue when type is only reachable through a @provides [PR #2083](https://github.com/apollographql/federation/pull/2083).
Expand Down
17 changes: 13 additions & 4 deletions internals-js/src/__tests__/definitions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -817,17 +817,26 @@ test('correctly convert to a graphQL-js schema', () => {
expect(printGraphQLjsSchema(graphqQLSchema)).toMatchString(sdl);
});

test('Conversion to graphQL-js schema can optionally include @defer definition', () => {
test('Conversion to graphQL-js schema can optionally include @defer and/or @streams definition(s)', () => {
const sdl = `
type Query {
x: Int
}
`;
const schema = parseSchema(sdl);

const graphqQLSchema = schema.toGraphQLJSSchema({ includeDefer: true });
expect(printGraphQLjsSchema(graphqQLSchema)).toMatchString(`
directive @defer(label: String, if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT
expect(printGraphQLjsSchema(schema.toGraphQLJSSchema({ includeDefer: true }))).toMatchString(`
directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT

type Query {
x: Int
}
`);

expect(printGraphQLjsSchema(schema.toGraphQLJSSchema({ includeDefer: true, includeStream: true }))).toMatchString(`
directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT

directive @stream(label: String, initialCount: Int = 0, if: Boolean! = true) on FIELD

type Query {
x: Int
Expand Down
18 changes: 10 additions & 8 deletions internals-js/src/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1128,15 +1128,16 @@ const graphQLBuiltInDirectivesSpecifications: readonly DirectiveSpecification[]
locations: [DirectiveLocation.SCALAR],
argumentFct: (schema) => ({ args: [{ name: 'url', type: new NonNullType(schema.stringType()) }], errors: [] })
}),
// TODO: currently inconditionally adding @defer as the list of built-in. It's probably fine, but double check if we want to not do so when @defer-support is
// not enabled or something (it would probably be hard to handle it at that point anyway but well...).
// Note that @defer and @stream are unconditionally added to `Schema` even if they are technically "optional" built-in. _But_,
// the `Schema#toGraphQLJSSchema` method has an option to decide if @defer/@stream should be included or not in the resulting
// schema, which is how the gateway and router can, at runtime, decide to include or not include them based on actual support.
createDirectiveSpecification({
name: 'defer',
locations: [DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT],
argumentFct: (schema) => ({
args: [
{ name: 'label', type: schema.stringType() },
{ name: 'if', type: schema.booleanType() },
{ name: 'if', type: new NonNullType(schema.booleanType()), defaultValue: true },
],
errors: [],
})
Expand All @@ -1151,17 +1152,14 @@ const graphQLBuiltInDirectivesSpecifications: readonly DirectiveSpecification[]
args: [
{ name: 'label', type: schema.stringType() },
{ name: 'initialCount', type: schema.intType(), defaultValue: 0 },
{ name: 'if', type: schema.booleanType() },
{ name: 'if', type: new NonNullType(schema.booleanType()), defaultValue: true },
],
errors: [],
})
}),
];

export type DeferDirectiveArgs = {
// TODO: we currently do not support variables for the defer label. Passing a label in a variable
// feels like a weird use case in the first place, but we should probably fix this nonetheless (or
// if we decide to have it be a known limitations, we should at least reject it cleanly).
label?: string,
if?: boolean | Variable,
}
Expand Down Expand Up @@ -1299,8 +1297,9 @@ export class Schema {
return nodes;
}

toGraphQLJSSchema(config?: { includeDefer?: boolean }): GraphQLSchema {
toGraphQLJSSchema(config?: { includeDefer?: boolean, includeStream?: boolean }): GraphQLSchema {
const includeDefer = config?.includeDefer ?? false;
const includeStream = config?.includeStream ?? false;

let ast = this.toAST();

Expand All @@ -1312,6 +1311,9 @@ export class Schema {
if (includeDefer) {
additionalNodes.push(this.deferDirective().toAST());
}
if (includeStream) {
additionalNodes.push(this.streamDirective().toAST());
}
if (additionalNodes.length > 0) {
ast = {
kind: Kind.DOCUMENT,
Expand Down
2 changes: 1 addition & 1 deletion internals-js/src/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1894,7 +1894,7 @@ class FragmentSpreadSelection extends FragmentSelection {
}

const expandedSubSelections = this.selectionSet.expandFragments(names, updateSelectionSetFragments);
return sameType(this._element.parentType, this.namedFragment.typeCondition)
return sameType(this._element.parentType, this.namedFragment.typeCondition) && this._element.appliedDirectives.length === 0
? expandedSubSelections.selections()
: new InlineFragmentSelection(this._element, expandedSubSelections);
}
Expand Down
1 change: 1 addition & 0 deletions query-planner-js/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
This CHANGELOG pertains only to Apollo Federation packages in the 2.x range. The Federation v0.x equivalent for this package can be found [here](https://github.com/apollographql/federation/blob/version-0.x/query-planner-js/CHANGELOG.md) on the `version-0.x` branch of this repo.


- Fix issue where fragment expansion can erase applied directives (most notably `@defer`) [PR #2093](https://github.com/apollographql/federation/pull/2093).
- Fix issue with fragment reusing code something mistakenly re-expanding fragments [PR #2098](https://github.com/apollographql/federation/pull/2098).

## 2.1.0-alpha.4
Expand Down
Loading