Skip to content

Commit 038cf0d

Browse files
Gealdariuszkuctrevor-scheer
authored
keep inputRewrites when cloning a FetchGroup (#2898)
if we do not keep them, they may disappear from the final fetch node Co-authored-by: dariuszkuc <[email protected]> Co-authored-by: Trevor Scheer <[email protected]>
1 parent f69a069 commit 038cf0d

File tree

3 files changed

+157
-3
lines changed

3 files changed

+157
-3
lines changed

.changeset/nasty-pillows-buy.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@apollo/query-planner": patch
3+
---
4+
5+
Fix handling of `@interfaceObject` when multiple child query paths are available.
6+
7+
When making copies of `FetchDependencyGraph`s, we were making incomplete copies that were missing `__typename` input rewrite information required for correctly handling `@interfaceObject` resolution.

query-planner-js/src/__tests__/buildPlan.interfaceObject.test.ts

+148
Original file line numberDiff line numberDiff line change
@@ -696,3 +696,151 @@ it('handles @interfaceObject in nested entity', () => {
696696
}
697697
`);
698698
});
699+
700+
it('handles @interfaceObject input rewrites when cloning dependency graph', () => {
701+
const subgraph1 = {
702+
name: 'S1',
703+
typeDefs: gql`
704+
type Query {
705+
i: I!
706+
}
707+
708+
interface I @key(fields: "i1") {
709+
i1: String!
710+
i2: T
711+
}
712+
713+
type T @key(fields: "t1", resolvable: false) {
714+
t1: String!
715+
}
716+
717+
type U implements I @key(fields: "i1") {
718+
id: ID!
719+
i1: String!
720+
i2: T @shareable
721+
}
722+
`,
723+
};
724+
725+
const subgraph2 = {
726+
name: 'S2',
727+
typeDefs: gql`
728+
type I @interfaceObject @key(fields: "i1") {
729+
i1: String!
730+
i2: T @shareable
731+
i3: Int
732+
}
733+
734+
type T @key(fields: "t1", resolvable: false) {
735+
t1: String!
736+
}
737+
`,
738+
};
739+
740+
const subgraph3 = {
741+
name: 'S3',
742+
typeDefs: gql`
743+
type T @key(fields: "t1") {
744+
t1: String!
745+
t2: String! @shareable
746+
t3: Int
747+
}
748+
`,
749+
};
750+
751+
const subgraph4 = {
752+
name: 'S4',
753+
typeDefs: gql`
754+
type T @key(fields: "t1") {
755+
t1: String!
756+
t2: String! @shareable
757+
t4: Int
758+
}
759+
`,
760+
};
761+
762+
const [api, queryPlanner] = composeAndCreatePlanner(
763+
subgraph1,
764+
subgraph2,
765+
subgraph3,
766+
subgraph4,
767+
);
768+
769+
const operation = operationFromDocument(
770+
api,
771+
gql`
772+
query {
773+
i {
774+
__typename
775+
i2 {
776+
__typename
777+
t2
778+
}
779+
i3
780+
}
781+
}
782+
`,
783+
);
784+
785+
const plan = queryPlanner.buildQueryPlan(operation);
786+
expect(plan).toMatchInlineSnapshot(`
787+
QueryPlan {
788+
Sequence {
789+
Fetch(service: "S1") {
790+
{
791+
i {
792+
__typename
793+
i1
794+
i2 {
795+
__typename
796+
t1
797+
}
798+
}
799+
}
800+
},
801+
Parallel {
802+
Flatten(path: "i") {
803+
Fetch(service: "S2") {
804+
{
805+
... on I {
806+
__typename
807+
i1
808+
}
809+
} =>
810+
{
811+
... on I {
812+
i3
813+
}
814+
}
815+
},
816+
},
817+
Flatten(path: "i.i2") {
818+
Fetch(service: "S3") {
819+
{
820+
... on T {
821+
__typename
822+
t1
823+
}
824+
} =>
825+
{
826+
... on T {
827+
__typename
828+
t2
829+
}
830+
}
831+
},
832+
},
833+
},
834+
},
835+
}
836+
`);
837+
838+
assert(isPlanNode(plan.node), 'buildQueryPlan should return QueryPlan');
839+
const rewrites = findFetchNodes('S2', plan.node)[0].inputRewrites;
840+
expect(rewrites).toBeDefined();
841+
expect(rewrites?.length).toBe(1);
842+
const rewrite = rewrites![0];
843+
assert(rewrite.kind === 'ValueSetter', JSON.stringify(rewrite));
844+
expect(rewrite.path).toEqual(['... on I', '__typename']);
845+
expect(rewrite.setValueTo).toBe('I');
846+
});

query-planner-js/src/buildPlan.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -921,9 +921,6 @@ class FetchGroup {
921921
// Set in some code-path to indicate that the selection of the group not be optimized away even if it "looks" useless.
922922
mustPreserveSelection: boolean = false;
923923

924-
private readonly inputRewrites: FetchDataRewrite[] = [];
925-
926-
927924
private constructor(
928925
readonly dependencyGraph: FetchDependencyGraph,
929926
public index: number,
@@ -941,6 +938,7 @@ class FetchGroup {
941938
private cachedCost?: number,
942939
// Cache used to save unecessary recomputation of the `isUseless` method.
943940
private isKnownUseful: boolean = false,
941+
private readonly inputRewrites: FetchDataRewrite[] = [],
944942
) {
945943
if (this._inputs) {
946944
this._inputs.onUpdateCallback = () => {
@@ -1008,6 +1006,7 @@ class FetchGroup {
10081006
this.subgraphAndMergeAtKey,
10091007
this.cachedCost,
10101008
this.isKnownUseful,
1009+
[...this.inputRewrites],
10111010
);
10121011
}
10131012

0 commit comments

Comments
 (0)