Repository: apollographql/apollo-client

Problem statement:
ApolloError not thrown when receiving mutation result with GraphQL errors
ApolloError is not thrown when the result of a mutation contains errors. Haven't tested with queries yet so I don't know if this is only a problem for mutations.

Example:

``` js
client.mutate({
  mutation: gql`
    mutation update($input: ProfileUpdateType) {
      updateUser(input: $input) {
        _id
        email
        name
        picture
      }
    }
  `,
  variables: {
    input: {
      invalidField: 'value'
    }
  }
})
.then(({ errors }) => {
  if (errors) {
    console.log('ApolloError not thrown')
  }
})
.catch(error => console.log('ApolloError: ', error.message));
```

Results in `ApolloError not thrown` being logged to the console.

Example of result with errors:
![image](https://cloud.githubusercontent.com/assets/7143583/17080241/a4070b48-50dc-11e6-8c16-c017a491e4ce.png)



Hints:

Patch:
diff --git a/src/QueryManager.ts b/src/QueryManager.ts
--- a/src/QueryManager.ts
+++ b/src/QueryManager.ts
@@ -237,29 +237,39 @@ export class QueryManager {
       resultBehaviors: [...resultBehaviors, ...updateQueriesResultBehaviors],
     });
 
-    return this.networkInterface.query(request)
-      .then((result) => {
-        this.store.dispatch({
-          type: 'APOLLO_MUTATION_RESULT',
-          result,
-          mutationId,
-          resultBehaviors: [
-            ...resultBehaviors,
-            ...this.collectResultBehaviorsFromUpdateQueries(updateQueries, result),
-          ],
-        });
+    return new Promise((resolve, reject) => {
+      this.networkInterface.query(request)
+        .then((result) => {
+          if (result.errors) {
+            reject(new ApolloError({
+              graphQLErrors: result.errors,
+            }));
+          }
 
-        return result;
-      })
-      .catch((err) => {
-        this.store.dispatch({
-          type: 'APOLLO_MUTATION_ERROR',
-          error: err,
-          mutationId,
-        });
+          this.store.dispatch({
+            type: 'APOLLO_MUTATION_RESULT',
+            result,
+            mutationId,
+            resultBehaviors: [
+                ...resultBehaviors,
+                ...this.collectResultBehaviorsFromUpdateQueries(updateQueries, result),
+            ],
+          });
 
-        return Promise.reject(err);
-      });
+          resolve(result);
+        })
+        .catch((err) => {
+          this.store.dispatch({
+            type: 'APOLLO_MUTATION_ERROR',
+            error: err,
+            mutationId,
+          });
+
+          reject(new ApolloError({
+            networkError: err,
+          }));
+        });
+    });
   }
 
   // Returns a query listener that will update the given observer based on the


Test patch:
diff --git a/test/client.ts b/test/client.ts
--- a/test/client.ts
+++ b/test/client.ts
@@ -1462,4 +1462,71 @@ describe('client', () => {
       assert.equal(query.definitions, initialDefinitions);
     });
   });
+
+  it('should pass a network error correctly on a mutation', (done) => {
+    const mutation = gql`
+      mutation {
+        person {
+          firstName
+          lastName
+        }
+      }`;
+    const data = {
+      person: {
+        firstName: 'John',
+        lastName: 'Smith',
+      },
+    };
+    const networkError = new Error('Some kind of network error.');
+    const client = new ApolloClient({
+      networkInterface: mockNetworkInterface({
+        request: { query: mutation },
+        result: { data },
+        error: networkError,
+      }),
+    });
+
+    client.mutate({ mutation }).then((result) => {
+      done(new Error('Returned a result when it should not have.'));
+    }).catch((error) => {
+      const apolloError = error as ApolloError;
+      assert(apolloError.networkError);
+      assert.equal(apolloError.networkError.message, networkError.message);
+      done();
+    });
+  });
+
+  it('should pass a GraphQL error correctly on a mutation', (done) => {
+    const mutation = gql`
+      mutation {
+        newPerson {
+          person {
+            firstName
+            lastName
+          }
+        }
+      }`;
+    const data = {
+      person: {
+        firstName: 'John',
+        lastName: 'Smith',
+      },
+    };
+    const errors = [ new Error('Some kind of GraphQL error.') ];
+    const client = new ApolloClient({
+      networkInterface: mockNetworkInterface({
+        request: { query: mutation },
+        result: { data, errors },
+      }),
+    });
+    client.mutate({ mutation }).then((result) => {
+      done(new Error('Returned a result when it should not have.'));
+    }).catch((error) => {
+      const apolloError = error as ApolloError;
+      assert(apolloError.graphQLErrors);
+      assert.equal(apolloError.graphQLErrors.length, 1);
+      assert.equal(apolloError.graphQLErrors[0].message, errors[0].message);
+      done();
+    });
+  });
 });
diff --git a/test/optimistic.ts b/test/optimistic.ts
--- a/test/optimistic.ts
+++ b/test/optimistic.ts
@@ -503,7 +503,7 @@ describe('optimistic mutation results', () => {
       })
       .catch((err) => {
         assert.instanceOf(err, Error);
-        assert.equal(err.message, 'forbidden (test error)');
+        assert.equal(err.message, 'Network error: forbidden (test error)');
 
         const dataInStore = client.queryManager.getDataWithOptimisticResults();
         assert.equal((dataInStore['TodoList5'] as any).todos.length, 3);
@@ -539,7 +539,7 @@ describe('optimistic mutation results', () => {
         }).catch((err) => {
           // it is ok to fail here
           assert.instanceOf(err, Error);
-          assert.equal(err.message, 'forbidden (test error)');
+          assert.equal(err.message, 'Network error: forbidden (test error)');
           return null;
         });
 
@@ -814,7 +814,7 @@ describe('optimistic mutation results', () => {
         }).catch((err) => {
           // it is ok to fail here
           assert.instanceOf(err, Error);
-          assert.equal(err.message, 'forbidden (test error)');
+          assert.equal(err.message, 'Network error: forbidden (test error)');
           return null;
         });