diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d9e081b45a..16a88f33cd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Expect active development and potentially significant breaking changes in the `0.x` track. We'll try to be diligent about releasing a `1.0` version in a timely fashion (ideally within 1 or 2 months), so that we can take advantage of SemVer to signify breaking changes from that point on. +### vNEXT + +- Made client more robust in the case where the server returns an empty error array, even though that's not in the GraphQL spec. [Issue #156](https://github.com/apollostack/apollo-client/issues/155) [PR #173](https://github.com/apollostack/apollo-client/pull/173) + ### v0.3.0 - **Breaking change:** Require all queries to be wrapped with a `gql` template literal tag, and throw an error when they aren't. [Issue #155](https://github.com/apollostack/apollo-client/issues/155) [PR #168](https://github.com/apollostack/apollo-client/pull/168) diff --git a/src/data/resultUtils.ts b/src/data/resultUtils.ts new file mode 100644 index 00000000000..c47c1043c23 --- /dev/null +++ b/src/data/resultUtils.ts @@ -0,0 +1,7 @@ +import { + GraphQLResult, +} from 'graphql'; + +export function graphQLResultHasError(result: GraphQLResult) { + return result.errors && result.errors.length; +} diff --git a/src/data/store.ts b/src/data/store.ts index f65f1edba41..df86038f439 100644 --- a/src/data/store.ts +++ b/src/data/store.ts @@ -22,6 +22,10 @@ import { ApolloReducerConfig, } from '../store'; +import { + graphQLResultHasError, +} from './resultUtils'; + export interface NormalizedCache { [dataId: string]: StoreObject; } @@ -53,7 +57,7 @@ export function data( } // XXX handle partial result due to errors - if (!action.result.errors) { + if (! graphQLResultHasError(action.result)) { const queryStoreValue = queries[action.queryId]; // XXX use immutablejs instead of cloning diff --git a/src/queries/store.ts b/src/queries/store.ts index 03506675888..b865a47c05e 100644 --- a/src/queries/store.ts +++ b/src/queries/store.ts @@ -7,6 +7,10 @@ import { isQueryStopAction, } from '../actions'; +import { + graphQLResultHasError, +} from '../data/resultUtils'; + import { SelectionSet, GraphQLError, @@ -74,7 +78,7 @@ export function queries( } const newState = assign({}, previousState) as QueryStore; - const resultHasGraphQLErrors = action.result.errors && action.result.errors.length; + const resultHasGraphQLErrors = graphQLResultHasError(action.result); newState[action.queryId] = assign({}, previousState[action.queryId], { loading: false, diff --git a/test/QueryManager.ts b/test/QueryManager.ts index 95ed8f419d6..0c163106a74 100644 --- a/test/QueryManager.ts +++ b/test/QueryManager.ts @@ -168,6 +168,102 @@ describe('QueryManager', () => { }); }); + it('handles GraphQL errors with data returned', (done) => { + const query = gql` + query people { + allPeople(first: 1) { + people { + name + } + } + } + `; + + const networkInterface = mockNetworkInterface( + { + request: {query }, + result: { + data: { + allPeople: { + people: { + name: 'Ada Lovelace', + }, + }, + }, + errors: [ + { + name: 'Name', + message: 'This is an error message.', + }, + ], + }, + } + ); + + const queryManager = new QueryManager({ + networkInterface, + store: createApolloStore(), + reduxRootKey: 'apollo', + }); + + const handle = queryManager.watchQuery({ + query, + }); + + handle.subscribe({ + next(result) { + assert.equal(result.errors[0].message, 'This is an error message.'); + done(); + }, + }); + }); + + it('empty error array (handle non-spec-compliant server) #156', (done) => { + const query = gql` + query people { + allPeople(first: 1) { + people { + name + } + } + } + `; + + const networkInterface = mockNetworkInterface( + { + request: {query }, + result: { + data: { + allPeople: { + people: { + name: 'Ada Lovelace', + }, + }, + }, + errors: [], + }, + } + ); + + const queryManager = new QueryManager({ + networkInterface, + store: createApolloStore(), + reduxRootKey: 'apollo', + }); + + const handle = queryManager.watchQuery({ + query, + }); + + handle.subscribe({ + next(result) { + assert.equal(result.data['allPeople'].people.name, 'Ada Lovelace'); + assert.notProperty(result, 'errors'); + done(); + }, + }); + }); + it('handles network errors', (done) => { const query = gql` query people {