Skip to content

Commit 037979d

Browse files
authored
Don't warn for missing resolvers (#12734)
1 parent 89ac725 commit 037979d

File tree

6 files changed

+77
-38
lines changed

6 files changed

+77
-38
lines changed

.changeset/kind-taxis-thank.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@apollo/client": patch
3+
---
4+
5+
Don't warn about a missing resolver if a `@client` does not have a configured resolver. It is possible the cache contains a `read` function for the field and the warning added confusion.
6+
7+
Note that `read` functions without a defined resolver will receive the `existing` argument as `null` instead of `undefined` even when data hasn't been written to the cache. This is because `LocalState` sets a default value of `null` when a resolver is not defined to ensure that the field contains a value in case a `read` function is not defined rather than omitting the field entirely.

.size-limits.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (CJS)": 43674,
3-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production) (CJS)": 38705,
4-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\"": 33447,
5-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production)": 27639
3+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production) (CJS)": 38653,
4+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\"": 33389,
5+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production)": 27619
66
}

src/__tests__/local-state/general.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,65 @@ describe("Cache manipulation", () => {
577577

578578
await expect(stream).not.toEmitAnything();
579579
});
580+
581+
test("runs read functions for nested @client fields without resolver warnings", async () => {
582+
using _ = spyOnConsole("warn");
583+
const query = gql`
584+
query {
585+
color {
586+
hex
587+
saved @client
588+
}
589+
}
590+
`;
591+
592+
const link = new ApolloLink(() => {
593+
return of({ data: { color: { __typename: "Color", hex: "#000" } } }).pipe(
594+
delay(20)
595+
);
596+
});
597+
598+
const read = jest.fn(() => false);
599+
600+
const cache = new InMemoryCache({
601+
typePolicies: {
602+
Color: {
603+
keyFields: ["hex"],
604+
fields: {
605+
saved: { read },
606+
},
607+
},
608+
},
609+
});
610+
611+
const client = new ApolloClient({
612+
link,
613+
cache,
614+
localState: new LocalState(),
615+
});
616+
617+
const stream = new ObservableStream(client.watchQuery({ query }));
618+
619+
await expect(stream).toEmitTypedValue({
620+
data: undefined,
621+
dataState: "empty",
622+
loading: true,
623+
networkStatus: NetworkStatus.loading,
624+
partial: true,
625+
});
626+
627+
await expect(stream).toEmitTypedValue({
628+
data: { color: { __typename: "Color", hex: "#000", saved: false } },
629+
dataState: "complete",
630+
loading: false,
631+
networkStatus: NetworkStatus.ready,
632+
partial: false,
633+
});
634+
635+
expect(read).toHaveBeenCalledTimes(1);
636+
expect(read).toHaveBeenCalledWith(null, expect.anything());
637+
expect(console.warn).not.toHaveBeenCalled();
638+
});
580639
});
581640

582641
describe("Sample apps", () => {

src/local-state/LocalState.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -615,13 +615,6 @@ export class LocalState<
615615
}
616616

617617
if (!returnPartialData) {
618-
if (__DEV__) {
619-
invariant.warn(
620-
"Could not find a resolver for the '%s' field. The field value has been set to `null`.",
621-
resolverName
622-
);
623-
}
624-
625618
return null;
626619
}
627620
}

src/local-state/__tests__/LocalState/base.test.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ test("throws error when query does not contain client fields", async () => {
573573
);
574574
});
575575

576-
test("warns when a resolver is missing for an `@client` field", async () => {
576+
test("does not warn when a resolver is missing for an `@client` field", async () => {
577577
using _ = spyOnConsole("warn");
578578
const document = gql`
579579
query {
@@ -598,14 +598,10 @@ test("warns when a resolver is missing for an `@client` field", async () => {
598598
})
599599
).resolves.toStrictEqualTyped({ data: { foo: null } });
600600

601-
expect(console.warn).toHaveBeenCalledTimes(1);
602-
expect(console.warn).toHaveBeenCalledWith(
603-
"Could not find a resolver for the '%s' field. The field value has been set to `null`.",
604-
"Query.foo"
605-
);
601+
expect(console.warn).not.toHaveBeenCalled();
606602
});
607603

608-
test("warns for client child fields of a server field", async () => {
604+
test("does not warn for client child fields of a server field", async () => {
609605
using _ = spyOnConsole("warn");
610606
const document = gql`
611607
query {
@@ -634,11 +630,7 @@ test("warns for client child fields of a server field", async () => {
634630
data: { foo: { __typename: "Foo", bar: null } },
635631
});
636632

637-
expect(console.warn).toHaveBeenCalledTimes(1);
638-
expect(console.warn).toHaveBeenCalledWith(
639-
"Could not find a resolver for the '%s' field. The field value has been set to `null`.",
640-
"Foo.bar"
641-
);
633+
expect(console.warn).not.toHaveBeenCalled();
642634
});
643635

644636
test("warns when a resolver returns undefined and sets value to null", async () => {

src/local-state/__tests__/LocalState/cache.test.ts

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ test("handles read functions for root object field from cache if resolver is not
338338
});
339339
});
340340

341-
test("warns if resolver is not defined if cache does not have value", async () => {
341+
test("does not warn if resolver is not defined if cache does not have value", async () => {
342342
using _ = spyOnConsole("warn");
343343
const document = gql`
344344
query {
@@ -363,11 +363,7 @@ test("warns if resolver is not defined if cache does not have value", async () =
363363
})
364364
).resolves.toStrictEqualTyped({ data: { count: null } });
365365

366-
expect(console.warn).toHaveBeenCalledTimes(1);
367-
expect(console.warn).toHaveBeenCalledWith(
368-
"Could not find a resolver for the '%s' field. The field value has been set to `null`.",
369-
"Query.count"
370-
);
366+
expect(console.warn).not.toHaveBeenCalled();
371367
});
372368

373369
test("reads from the cache on a nested scalar field by default if a resolver is not defined", async () => {
@@ -713,11 +709,7 @@ test("does not confuse field missing resolver with root field of same name on a
713709
},
714710
});
715711

716-
expect(console.warn).toHaveBeenCalledTimes(1);
717-
expect(console.warn).toHaveBeenCalledWith(
718-
"Could not find a resolver for the '%s' field. The field value has been set to `null`.",
719-
"User.count"
720-
);
712+
expect(console.warn).not.toHaveBeenCalled();
721713
});
722714

723715
test("does not confuse field missing resolver with root field of same name on a non-normalized record", async () => {
@@ -769,11 +761,7 @@ test("does not confuse field missing resolver with root field of same name on a
769761
},
770762
});
771763

772-
expect(console.warn).toHaveBeenCalledTimes(1);
773-
expect(console.warn).toHaveBeenCalledWith(
774-
"Could not find a resolver for the '%s' field. The field value has been set to `null`.",
775-
"User.count"
776-
);
764+
expect(console.warn).not.toHaveBeenCalled();
777765
});
778766

779767
test("warns on undefined value if partial data is written to the cache for an object client field", async () => {

0 commit comments

Comments
 (0)