diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index ccf6195f6..548834a94 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -124,36 +124,28 @@ const extension = { mutations: { IgnoreUser: ({variables}) => ({ updateQueries: { - EmbedQuery: (previousData, {mutationResult}) => { + EmbedQuery: (previousData) => { const ignoredUserId = variables.id; - const response = mutationResult.data.ignoreUser; - if (ignoredUserId && !response.errors) { - const updated = update(previousData, {me: {ignoredUsers: {$push: [{ - id: ignoredUserId, - __typename: 'User', - }]}}}); - return updated; - } - return previousData; + const updated = update(previousData, {me: {ignoredUsers: {$push: [{ + id: ignoredUserId, + __typename: 'User', + }]}}}); + return updated; } } }), StopIgnoringUser: ({variables}) => ({ updateQueries: { - EmbedStreamProfileQuery: (previousData, {mutationResult}) => { + EmbedStreamProfileQuery: (previousData) => { const noLongerIgnoredUserId = variables.id; - const response = mutationResult.data.stopIgnoringUser; - if (noLongerIgnoredUserId && !response.errors) { - // remove noLongerIgnoredUserId from ignoredUsers - const updated = update(previousData, {me: {ignoredUsers: { - $apply: (ignoredUsers) => { - return ignoredUsers.filter((u) => u.id !== noLongerIgnoredUserId); - } - }}}); - return updated; - } - return previousData; + // remove noLongerIgnoredUserId from ignoredUsers + const updated = update(previousData, {me: {ignoredUsers: { + $apply: (ignoredUsers) => { + return ignoredUsers.filter((u) => u.id !== noLongerIgnoredUserId); + } + }}}); + return updated; } } }), @@ -221,12 +213,7 @@ const extension = { variables: {id, edit}, }) => ({ updateQueries: { - EmbedQuery: (previousData, {mutationResult: {data: {editComment: {comment, errors}}}}) => { - - // @TODO (kiwi) revisit after streamlining error handling - if (errors && errors.length) { - return previousData; - } + EmbedQuery: (previousData, {mutationResult: {data: {editComment: {comment}}}}) => { const {status} = comment; const updateCommentWithEdit = (comment, edit) => { const {body} = edit; diff --git a/client/coral-framework/hocs/withMutation.js b/client/coral-framework/hocs/withMutation.js index 044bcdfad..285932372 100644 --- a/client/coral-framework/hocs/withMutation.js +++ b/client/coral-framework/hocs/withMutation.js @@ -6,7 +6,25 @@ 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 t from 'coral-framework/services/i18n'; + +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 t(`error.${this.translation_key}`, ...args); + } +} /** * Exports a HOC with the same signature as `graphql`, that will @@ -41,6 +59,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 +76,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 +105,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}); }; diff --git a/client/coral-framework/hocs/withQuery.js b/client/coral-framework/hocs/withQuery.js index 036f9c149..84182f7dd 100644 --- a/client/coral-framework/hocs/withQuery.js +++ b/client/coral-framework/hocs/withQuery.js @@ -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, diff --git a/client/coral-framework/utils/index.js b/client/coral-framework/utils/index.js index 8fb9cbf81..c8e68517a 100644 --- a/client/coral-framework/utils/index.js +++ b/client/coral-framework/utils/index.js @@ -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; +}