diff --git a/.changeset/lazy-suns-end.md b/.changeset/lazy-suns-end.md new file mode 100644 index 0000000000..f3dd93c720 --- /dev/null +++ b/.changeset/lazy-suns-end.md @@ -0,0 +1,5 @@ +--- +'@urql/exchange-populate': patch +--- + +Fix the scenario where we traverse a query but end up in a nested fragment, this maks it so that we can't derive the type for the sub-entity diff --git a/exchanges/populate/src/populateExchange.test.ts b/exchanges/populate/src/populateExchange.test.ts index 30321c387a..0a631188ec 100644 --- a/exchanges/populate/src/populateExchange.test.ts +++ b/exchanges/populate/src/populateExchange.test.ts @@ -648,6 +648,75 @@ describe('interface returned in mutation', () => { }); }); +describe('nested fragment', () => { + const fragment = gql` + fragment TodoFragment on Todo { + id + author { + id + } + } + `; + + const queryOp = makeOperation( + 'query', + { + key: 1234, + query: gql` + query { + todos { + ...TodoFragment + } + } + + ${fragment} + `, + }, + context + ); + + const mutationOp = makeOperation( + 'mutation', + { + key: 5678, + query: gql` + mutation MyMutation { + updateTodo @populate + } + `, + }, + context + ); + + it('should work with nested fragments', () => { + const response = pipe( + fromArray([queryOp, mutationOp]), + populateExchange({ schema })(exchangeArgs), + toArray + ); + + expect(print(response[1].query)).toMatchInlineSnapshot(` + "mutation MyMutation { + updateTodo { + ...Todo_PopulateFragment_0 + } + } + + fragment Todo_PopulateFragment_0 on Todo { + ...TodoFragment + } + + fragment TodoFragment on Todo { + id + author { + id + } + } + " + `); + }); +}); + describe('nested interfaces', () => { const queryOp = makeOperation( 'query', diff --git a/exchanges/populate/src/populateExchange.ts b/exchanges/populate/src/populateExchange.ts index 26009717ea..32ccad60db 100644 --- a/exchanges/populate/src/populateExchange.ts +++ b/exchanges/populate/src/populateExchange.ts @@ -176,13 +176,19 @@ export const extractSelectionsFromQuery = ( }; const visits: string[] = []; + let insideFragment = false; traverse( query, node => { if (node.kind === Kind.FRAGMENT_DEFINITION) { + insideFragment = true; extractedFragments.push(node); - } else if (node.kind === Kind.FIELD && node.selectionSet) { + } else if ( + node.kind === Kind.FIELD && + node.selectionSet && + !insideFragment + ) { const type = unwrapType( resolveFields(schema, visits)[node.name.value].type ); @@ -219,7 +225,13 @@ export const extractSelectionsFromQuery = ( } }, node => { - if (node.kind === Kind.FIELD && node.selectionSet) visits.pop(); + if (node.kind === Kind.FIELD && node.selectionSet && !insideFragment) { + visits.pop(); + } + + if (node.kind === Kind.FRAGMENT_DEFINITION) { + insideFragment = false; + } } );