Repository: apollographql/apollo-client

Problem statement:
Several error reporting issues
I think there may be several issues here; this is prompted from [a discussion with @Poincare in Slack](https://apollostack.slack.com/archives/general/p1469029558000617) These may be separate issues; let me know if you'd prefer separate open issues for each.
# Issues
1. `render` error reported as a `networkError`
2. `ApolloError` object does not include stack traces
3. `crashReporter` from `store.ts` not included in `client.middleware()`
# Background

I'm using `react-apollo` and `apollo-client` with an existing Redux store, and had a situation where a Component (hooked up using `connect`) was throwing an error because I was trying to access a property of `undefined` (See line 39 below.) While this is a reduced example, trying to track down the actual error in my app was fairly difficult.
## 1) `render` error

The code below will report the 'cannot read property' error as a `networkError`(!) property of the ApolloError object, even though the error is in a `render` method. I'm not sure why that would be.
## 2) No stack trace on `ApolloError`

The ApolloError object itself doesn't have a stack trace—I have to go into the `networkError` property directly to access it.
## 3) `crashReporter`

While trying to create the reduced test case below, I noticed that if I let Apollo manage the Redux store, I got a separate error message with the proper stack trace, from [this line](https://github.com/apollostack/apollo-client/blob/061180efcb45a8c043af3fc38d71d7642c1300da/src/store.ts#L63). This doesn't happen if you are using an existing Redux store. 
# Code

``` javascript
import React from 'react';
import ReactDOM from 'react-dom';
import ApolloClient from 'apollo-client';
import {ApolloProvider, connect} from 'react-apollo';
import gql from 'graphql-tag';
import { createStore, combineReducers, applyMiddleware } from 'redux';

const client = new ApolloClient({
  networkInterface: {
    query: function() {
      return new Promise(function(resolve, reject) {
        return resolve({
          data: {
            test: {
              id: 1
            }
          }
        });
      });
    }
  }
});
const store = createStore(
  combineReducers({
    apollo: client.reducer(),
  }),
  applyMiddleware(client.middleware())
);

const Example = function(props) {
  if (props.myQuery.loading) {
    return <div>Loading...</div>;
  }
  if (props.myQuery.errors) {
    console.error(props.myQuery.errors.stack); // undefined
    console.error(props.myQuery.errors.networkError.stack); // correct source-mapped stack trace
    return <div>Errors!</div>;
  };
  return <h1>Hello, world!{props.myQuery.doesnt_exist.completely_undefined}</h1>; // throws an error
};

var MyComponent = connect({
  mapQueriesToProps({ownProps, state}) {
    return {
      myQuery: {
        query: gql` {
          test {id}
        }`
      }
    }
  }
})(Example);

ReactDOM.render(
  <ApolloProvider client={client} store={store}>
    <MyComponent />
  </ApolloProvider>,
  document.getElementById('body')
);
```

There is currently an [example at http://apolloerror-example.dahjelle.c9users.io/](http://apolloerror-example.dahjelle.c9users.io/), but I have no idea how long c9.io will let it run. :-) 

Fix error issues
Fixes #434 and #423.

TODO:
- [x] Update CHANGELOG.md with your change
- [x] Make sure all of the significant new logic is covered by tests
- [x] Rebase your changes on master so that they can be merged easily
- [x] Make sure all tests and linter rules pass



Hints:
@dahjelle is this example something we can clone and run locally?

I put it in [this GitHub repo](https://github.com/dahjelle/apolloerror-example). Should be fairly straightforward to get it going (but, then again, it's the stack I'm used to :-) ).

Nice, I dig the mocked network interface! This could be a great way to distribute simple examples :]

I was pretty pumped when I realized you'd set things up to enable mocking a whole GraphQL server. 👍 

That's how we run all of the apollo client tests! https://github.com/apollostack/apollo-client/blob/master/test/mocks/mockNetworkInterface.ts

I think (1) may be tied to one of the current issues with `react-apollo`, (2) is definitely fixable from `apollo-client` (I'll work on it) and I'll have to look more into (3) to figure out what is going on.



Patch:
diff --git a/src/QueryManager.ts b/src/QueryManager.ts
--- a/src/QueryManager.ts
+++ b/src/QueryManager.ts
@@ -699,11 +699,6 @@ export class QueryManager {
             });
 
             this.removeFetchQueryPromise(requestId);
-            if (result.errors) {
-              reject(new ApolloError({
-                graphQLErrors: result.errors,
-              }));
-            }
             return result;
           }).then(() => {
 
@@ -738,9 +733,6 @@ export class QueryManager {
 
             this.removeFetchQueryPromise(requestId);
 
-            reject(new ApolloError({
-              networkError: error,
-            }));
           });
       });
       return retPromise;
diff --git a/src/errors.ts b/src/errors.ts
--- a/src/errors.ts
+++ b/src/errors.ts
@@ -21,6 +21,9 @@ export class ApolloError extends Error {
     this.graphQLErrors = graphQLErrors;
     this.networkError = networkError;
 
+    // set up the stack trace
+    this.stack = new Error().stack;
+
     if (!errorMessage) {
       this.generateErrorMessage();
     } else {


Test patch:
diff --git a/test/QueryManager.ts b/test/QueryManager.ts
--- a/test/QueryManager.ts
+++ b/test/QueryManager.ts
@@ -2611,7 +2611,7 @@ describe('QueryManager', () => {
     });
   });
 
-  it('should reject a fetchQuery promise given a network error', (done) => {
+  it('should reject a query promise given a network error', (done) => {
     const query = gql`
       query {
         author {
@@ -2629,7 +2629,7 @@ describe('QueryManager', () => {
       store: createApolloStore(),
       reduxRootKey: 'apollo',
     });
-    queryManager.fetchQuery('fake-id', { query }).then((result) => {
+    queryManager.query({ query }).then((result) => {
       done(new Error('Returned result on an errored fetchQuery'));
     }).catch((error) => {
       const apolloError = error as ApolloError;
@@ -2685,7 +2685,7 @@ describe('QueryManager', () => {
     });
   });
 
-  it('should reject a fetchQuery promise given a GraphQL error', (done) => {
+  it('should reject a query promise given a GraphQL error', (done) => {
     const query = gql`
       query {
         author {
@@ -2703,7 +2703,7 @@ describe('QueryManager', () => {
       store: createApolloStore(),
       reduxRootKey: 'apollo',
     });
-    queryManager.fetchQuery('fake-id', { query }).then((result) => {
+    queryManager.query({ query }).then((result) => {
       done(new Error('Returned result on an errored fetchQuery'));
     }).catch((error) => {
       const apolloError = error as ApolloError;
diff --git a/test/errors.ts b/test/errors.ts
--- a/test/errors.ts
+++ b/test/errors.ts
@@ -68,4 +68,14 @@ describe('ApolloError', () => {
     assert.include(messages[1], 'Network error');
     assert.include(messages[1], 'network error message');
   });
+
+  it('should contain a stack trace', () => {
+    const graphQLErrors = [ new Error('graphql error message') ];
+    const networkError = new Error('network error message');
+    const apolloError = new ApolloError({
+      graphQLErrors,
+      networkError,
+    });
+    assert(apolloError.stack, 'Does not contain a stack trace.');
+  });
 });