Handle response errors in the graphql framework

This commit is contained in:
Chi Vinh Le
2017-05-22 19:47:37 +07:00
parent f32208b35b
commit d1307eb5cd
3 changed files with 66 additions and 8 deletions
+42 -3
View File
@@ -6,7 +6,27 @@ import flatten from 'lodash/flatten';
import isEmpty from 'lodash/isEmpty';
import {getMutationOptions, resolveFragments} from 'coral-framework/services/graphqlRegistry';
import {store} from 'coral-framework/services/store';
import {getDefinitionName} from '../utils';
import {getDefinitionName, getResponseErrors} from '../utils';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-framework/translations.json';
const lang = new I18n(translations);
class ResponseErrors extends Error {
constructor(errors) {
super(`Response Errors ${JSON.stringify(errors)}`);
this.errors = errors.map((e) => new ResponseError(e));
}
}
class ResponseError {
constructor(error) {
Object.assign(this, error);
}
translate(...args) {
return lang.t(`error.${this.translation_key}`, ...args);
}
}
/**
* Exports a HOC with the same signature as `graphql`, that will
@@ -41,6 +61,11 @@ export default (document, config) => (WrappedComponent) => {
.filter((i) => i);
const update = (proxy, result) => {
if (getResponseErrors(result)) {
// Do not run updates when we have mutation errors.
return;
}
updateCallbacks.forEach((cb) => cb(proxy, result));
};
@@ -53,7 +78,14 @@ export default (document, config) => (WrappedComponent) => {
.reduce((res, map) => {
Object.keys(map).forEach((key) => {
if (!(key in res)) {
res[key] = map[key];
res[key] = (prev, result) => {
if (getResponseErrors(result.mutationResult)) {
// Do not run updates when we have mutation errors.
return prev;
}
return map[key](prev, result);
};
} else {
const existing = res[key];
res[key] = (prev, result) => {
@@ -75,7 +107,14 @@ export default (document, config) => (WrappedComponent) => {
if (isEmpty(wrappedConfig.optimisticResponse)) {
delete wrappedConfig.optimisticResponse;
}
return data.mutate(wrappedConfig);
return data.mutate(wrappedConfig)
.then((res) => {
const errors = getResponseErrors(res);
if (errors) {
throw new ResponseErrors(errors);
}
return Promise.resolve(res);
});
};
return config.props({...data, mutate});
};
+13 -5
View File
@@ -1,7 +1,14 @@
import * as React from 'react';
import {graphql} from 'react-apollo';
import {getQueryOptions, resolveFragments} from 'coral-framework/services/graphqlRegistry';
import {getDefinitionName, separateDataAndRoot} from '../utils';
import {getDefinitionName, separateDataAndRoot, getResponseErrors} from '../utils';
const withSkipOnErrors = (reducer) => (prev, action, ...rest) => {
if (action.type === 'APOLLO_MUTATION_RESULT' && getResponseErrors(action.result)) {
return prev;
}
return reducer(prev, action, ...rest);
};
/**
* Exports a HOC with the same signature as `graphql`, that will
@@ -23,10 +30,11 @@ export default (document, config) => (WrappedComponent) => {
.concat(...configs.map((cfg) => cfg.reducer))
.filter((i) => i);
const reducer = reducerCallbacks.reduce(
(a, b) => (prev, ...rest) =>
b(a(prev, ...rest), ...rest),
);
const reducer = withSkipOnErrors(
reducerCallbacks.reduce(
(a, b) => (prev, ...rest) =>
b(a(prev, ...rest), ...rest),
));
return {
...base,
+11
View File
@@ -100,3 +100,14 @@ export function mergeDocuments(documents) {
const literals = [main, ...substitutions.map(() => '\n')];
return gql.apply(null, [literals, ...substitutions]);
}
export function getResponseErrors(mutationResult) {
const result = [];
Object.keys(mutationResult.data).forEach((response) => {
const errors = mutationResult.data[response].errors;
if (errors && errors.length) {
result.push(...errors);
}
});
return result.length ? result : false;
}