Repository: apollographql/apollo-client

Problem statement:
returnPartialData usage leads to "Missing cache result fields" warning
We are upgrading from 2.x to 3.2.0. Everything is upgraded and working as expected but in a few situations we have query pattern where we...

1. Load a list of a given type in a list view
2. have a detail view that loads single records from the list with additional fields
3. uses `returnPartialData: true` and handles missing data gracefully in the UI

These still _appear_ to work as expected but now report `Missing cache result fields` and list the fields that are not in the list view query. I _think_ this is a mis-reported warning as everything appears to work and if I set `returnPartialData: false` the errors go away.

Note also this is a readonly API with no mutations and all the objects/queries in question include an `id` field so we aren't really doing anything crazy cache wise I don't think.

**Intended outcome:**

Running a query that is a partial cache hit with `returnPartialData: true` should not result in warnings for the fields that are missing as you explicitly requested partial data.

**Actual outcome:**

Warnings are shown for missing fields when partial data is returned.

**How to reproduce the issue:**

List Query...

```javascript
const LIST_SCENES = gql`
  query ListScenes {
    scenes { id, title }
  }
`;

useQuery(LIST_SCENES);
```

Detail Query....

```javascript
const GET_SCENE = gql`
  query GetScene($sceneId: String!) {
    scene(id: $sceneId) { id, title, content }
  }
`;

useQuery(GET_SCENE, { returnPartialData: true, variables: { sceneId: "my-scene-id" } });

// => WARN: Missing cache result fields: scene.content [{"clientOnly": false, "message": "Can't find field 'content' on Scene:my-scene-id object", "path": ["scene", "content"] ...
```

**Versions**

```
  System:
    OS: macOS 10.15.6
  Binaries:
    Node: 13.9.0 - ~/.asdf/installs/nodejs/13.9.0/bin/node
    Yarn: 1.22.5 - ~/dev/portal/app/node_modules/.bin/yarn
    npm: 6.13.7 - ~/.asdf/installs/nodejs/13.9.0/bin/npm
  Browsers:
    Chrome: 85.0.4183.102
    Safari: 14.0
  npmPackages:
    @apollo/client: ^3.2.0 => 3.2.0 
    apollo: ^2.30.3 => 2.30.3 
    apollo-link-queue: ^3.0.0 => 3.0.0 
    apollo-link-timeout: ^2.0.1 => 2.0.1 
    apollo-utilities: ^1.3.4 => 1.3.4 
```


Hints:
@andykent Totally agree with this! We'll get this fixed soon, but for now I can assure you these are indeed just warnings, so you don't need to do anything about them. Sorry for the annoyance.
@benjamn Great news, thanks for the confirmation.

Patch:
diff --git a/src/core/QueryManager.ts b/src/core/QueryManager.ts
--- a/src/core/QueryManager.ts
+++ b/src/core/QueryManager.ts
@@ -972,7 +972,8 @@ export class QueryManager<TStore> {
 
       if (process.env.NODE_ENV !== 'production' &&
           isNonEmptyArray(diff.missing) &&
-          !equal(data, {})) {
+          !equal(data, {}) &&
+          !returnPartialData) {
         invariant.warn(`Missing cache result fields: ${
           diff.missing.map(m => m.path.join('.')).join(', ')
         }`, diff.missing);


Test patch:
diff --git a/src/core/__tests__/QueryManager/index.ts b/src/core/__tests__/QueryManager/index.ts
--- a/src/core/__tests__/QueryManager/index.ts
+++ b/src/core/__tests__/QueryManager/index.ts
@@ -5228,5 +5228,107 @@ describe('QueryManager', () => {
 
       expect(queryManager['inFlightLinkObservables'].size).toBe(0)
     });
-  })
+  });
+
+  describe('missing cache field warnings', () => {
+    const originalWarn = console.warn;
+    let warnCount = 0;
+
+    beforeEach(() => {
+      warnCount = 0;
+      console.warn = (...args: any[]) => {
+        warnCount += 1;
+      };
+    });
+
+    afterEach(() => {
+      console.warn = originalWarn;
+    });
+
+    function validateWarnings(
+      resolve: (result?: any) => void,
+      reject: (reason?: any) => void,
+      returnPartialData = false,
+      expectedWarnCount = 1
+    ) {
+      const query1 = gql`
+        query {
+          car {
+            make
+            model
+            id
+            __typename
+          }
+        }
+      `;
+
+      const query2 = gql`
+        query {
+          car {
+            make
+            model
+            vin
+            id
+            __typename
+          }
+        }
+      `;
+
+      const data1 = {
+        car: {
+          make: 'Ford',
+          model: 'Pinto',
+          id: 123,
+          __typename: 'Car'
+        },
+      };
+
+      const queryManager = mockQueryManager(
+        reject,
+        {
+          request: { query: query1 },
+          result: { data: data1 },
+        },
+      );
+
+      const observable1 = queryManager.watchQuery<any>({ query: query1 });
+      const observable2 = queryManager.watchQuery<any>({
+        query: query2,
+        fetchPolicy: 'cache-only',
+        returnPartialData,
+      });
+
+      return observableToPromise(
+        { observable: observable1 },
+        result => {
+          expect(result).toEqual({
+            loading: false,
+            data: data1,
+            networkStatus: NetworkStatus.ready,
+          });
+        },
+      ).then(() => {
+        observableToPromise(
+          { observable: observable2 },
+          result => {
+            expect(result).toEqual({
+              data: data1,
+              loading: false,
+              networkStatus: NetworkStatus.ready,
+              partial: true,
+            });
+            expect(warnCount).toBe(expectedWarnCount);
+          },
+        ).then(resolve, reject)
+      });
+    }
+
+    itAsync('should show missing cache result fields warning when returnPartialData is false', (resolve, reject) => {
+      validateWarnings(resolve, reject, false, 1);
+    });
+
+    itAsync('should not show missing cache result fields warning when returnPartialData is true', (resolve, reject) => {
+      validateWarnings(resolve, reject, true, 0);
+    });
+  });
 });