Skip to content

Commit

Permalink
Fix regression that causes partial data to be reported unexpectedly i…
Browse files Browse the repository at this point in the history
…n some circumstances (#11579)
  • Loading branch information
jerelmiller committed Feb 15, 2024
1 parent 8c20955 commit 1ba2fd9
Show file tree
Hide file tree
Showing 6 changed files with 416 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/neat-toes-leave.md
@@ -0,0 +1,5 @@
---
"@apollo/client": patch
---

Fix issue where partial data is reported to `useQuery` when using `notifyOnNetworkStatusChange` after it errors while another overlapping query succeeds.
5 changes: 5 additions & 0 deletions .changeset/seven-shoes-invite.md
@@ -0,0 +1,5 @@
---
"@apollo/client": patch
---

Fix an issue where a partial cache write for an errored query would result in automatically refetching that query.
4 changes: 2 additions & 2 deletions .size-limits.json
@@ -1,4 +1,4 @@
{
"dist/apollo-client.min.cjs": 39042,
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32550
"dist/apollo-client.min.cjs": 39075,
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32584
}
21 changes: 21 additions & 0 deletions src/core/QueryInfo.ts
Expand Up @@ -210,7 +210,28 @@ export class QueryInfo {

setDiff(diff: Cache.DiffResult<any> | null) {
const oldDiff = this.lastDiff && this.lastDiff.diff;

// If we do not tolerate partial results, skip this update to prevent it
// from being reported. This prevents a situtuation where a query that
// errors and another succeeds with overlapping data does not report the
// partial data result to the errored query.
//
// See https://github.com/apollographql/apollo-client/issues/11400 for more
// information on this issue.
if (
diff &&
!diff.complete &&
!this.observableQuery?.options.returnPartialData &&
// In the case of a cache eviction, the diff will become partial so we
// schedule a notification to send a network request (this.oqListener) to
// go and fetch the missing data.
!(oldDiff && oldDiff.complete)
) {
return;
}

this.updateLastDiff(diff);

if (!this.dirty && !equal(oldDiff && oldDiff.result, diff && diff.result)) {
this.dirty = true;
if (!this.notifyTimeout) {
Expand Down
119 changes: 119 additions & 0 deletions src/core/__tests__/ObservableQuery.ts
Expand Up @@ -24,6 +24,7 @@ import {
itAsync,
MockLink,
mockSingleLink,
MockSubscriptionLink,
subscribeAndCount,
wait,
} from "../../testing";
Expand Down Expand Up @@ -2389,6 +2390,124 @@ describe("ObservableQuery", () => {
}
);

it("handles multiple calls to getCurrentResult without losing data", async () => {
const query = gql`
{
greeting {
message
... on Greeting @defer {
recipient {
name
}
}
}
}
`;

const link = new MockSubscriptionLink();

const client = new ApolloClient({
link,
cache: new InMemoryCache(),
});

const obs = client.watchQuery({ query });
const stream = new ObservableStream(obs);

link.simulateResult({
result: {
data: {
greeting: {
message: "Hello world",
__typename: "Greeting",
},
},
hasNext: true,
},
});

{
const result = await stream.takeNext();
expect(result.data).toEqual({
greeting: {
message: "Hello world",
__typename: "Greeting",
},
});
}

expect(obs.getCurrentResult().data).toEqual({
greeting: {
message: "Hello world",
__typename: "Greeting",
},
});

expect(obs.getCurrentResult().data).toEqual({
greeting: {
message: "Hello world",
__typename: "Greeting",
},
});

link.simulateResult(
{
result: {
incremental: [
{
data: {
recipient: {
name: "Alice",
__typename: "Person",
},
__typename: "Greeting",
},
path: ["greeting"],
},
],
hasNext: false,
},
},
true
);

{
const result = await stream.takeNext();
expect(result.data).toEqual({
greeting: {
message: "Hello world",
recipient: {
name: "Alice",
__typename: "Person",
},
__typename: "Greeting",
},
});
}

expect(obs.getCurrentResult().data).toEqual({
greeting: {
message: "Hello world",
recipient: {
name: "Alice",
__typename: "Person",
},
__typename: "Greeting",
},
});

expect(obs.getCurrentResult().data).toEqual({
greeting: {
message: "Hello world",
recipient: {
name: "Alice",
__typename: "Person",
},
__typename: "Greeting",
},
});
});

{
type Result = Partial<ApolloQueryResult<{ hello: string }>>;

Expand Down

0 comments on commit 1ba2fd9

Please sign in to comment.