From f312f68ec6024d40ccc38b9aa955d1c525ef8783 Mon Sep 17 00:00:00 2001 From: Tristan Cacqueray Date: Sun, 2 Dec 2018 05:48:04 +0000 Subject: [PATCH] web: add error reducer and info toast notification This change adds a new error reducer to manage error from API calls. The info actions retries failed info request after 5 seconds. Change-Id: Ieb2b66a2847650788d9bf68080ab208855941f24 --- web/package.json | 2 ++ web/src/App.jsx | 28 ++++++++++++++++++++++- web/src/actions/errors.js | 42 ++++++++++++++++++++++++++++++++++ web/src/actions/info.js | 5 +++- web/src/reducers/errors.js | 47 ++++++++++++++++++++++++++++++++++++++ web/src/reducers/index.js | 2 ++ web/yarn.lock | 10 ++++++-- 7 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 web/src/actions/errors.js create mode 100644 web/src/reducers/errors.js diff --git a/web/package.json b/web/package.json index 1aa81cb0a3..97d1be970c 100644 --- a/web/package.json +++ b/web/package.json @@ -8,7 +8,9 @@ "private": true, "dependencies": { "axios": "^0.18.0", + "immutability-helper": "^2.8.1", "lodash": "^4.17.10", + "moment": "^2.22.2", "patternfly-react": "^2.13.1", "prop-types": "^15.6.2", "react": "^16.4.2", diff --git a/web/src/App.jsx b/web/src/App.jsx index 5ca9f9bf86..b2488a93d7 100644 --- a/web/src/App.jsx +++ b/web/src/App.jsx @@ -25,16 +25,21 @@ import { Masthead, Notification, NotificationDrawer, + TimedToastNotification, + ToastNotificationList, } from 'patternfly-react' +import * as moment from 'moment' import logo from './images/logo.png' import { routes } from './routes' import { fetchConfigErrorsAction } from './actions/configErrors' import { setTenantAction } from './actions/tenant' +import { clearError } from './actions/errors' class App extends React.Component { static propTypes = { + errors: PropTypes.array, configErrors: PropTypes.array, info: PropTypes.object, tenant: PropTypes.object, @@ -149,6 +154,25 @@ class App extends React.Component { } } + renderErrors = (errors) => { + return ( + + {errors.map(error => ( + {this.props.dispatch(clearError(error.id))}} + > + + {error.text} ({error.status})  + {error.url} + + + ))} + + ) + } + renderConfigErrors = (configErrors) => { const { history } = this.props const errors = [] @@ -207,7 +231,7 @@ class App extends React.Component { render() { const { menuCollapsed, showErrors } = this.state - const { tenant, configErrors } = this.props + const { errors, configErrors, tenant } = this.props return ( @@ -252,6 +276,7 @@ class App extends React.Component { )} + {errors.length > 0 && this.renderErrors(errors)}
{this.renderContent()}
@@ -263,6 +288,7 @@ class App extends React.Component { // This connect the info state from the store to the info property of the App. export default withRouter(connect( state => ({ + errors: state.errors, configErrors: state.configErrors, info: state.info, tenant: state.tenant diff --git a/web/src/actions/errors.js b/web/src/actions/errors.js new file mode 100644 index 0000000000..e113121276 --- /dev/null +++ b/web/src/actions/errors.js @@ -0,0 +1,42 @@ +// Copyright 2018 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +export const ADD_ERROR = 'ADD_ERROR' +export const CLEAR_ERROR = 'CLEAR_ERROR' +export const CLEAR_ERRORS = 'CLEAR_ERRORS' + +let errorId = 0 + +export const addError = error => ({ + type: ADD_ERROR, + id: errorId++, + error +}) + +export const addApiError = error => ( + addError({ + url: error.request.responseURL, + status: error.response.status, + text: error.response.statusText, + }) +) + +export const clearError = id => ({ + type: CLEAR_ERROR, + id +}) + +export const clearErrors = () => ({ + type: CLEAR_ERRORS +}) diff --git a/web/src/actions/info.js b/web/src/actions/info.js index 81e9b4a2a0..a28c62651c 100644 --- a/web/src/actions/info.js +++ b/web/src/actions/info.js @@ -36,7 +36,10 @@ const fetchInfo = () => dispatch => { dispatch(fetchInfoRequest()) return API.fetchInfo() .then(response => dispatch(fetchInfoSuccess(response.data))) - .catch(error => dispatch(fetchInfoFail(error))) + .catch(error => { + dispatch(fetchInfoFail(error)) + setTimeout(() => {dispatch(fetchInfo())}, 5000) + }) } const shouldFetchInfo = state => { diff --git a/web/src/reducers/errors.js b/web/src/reducers/errors.js new file mode 100644 index 0000000000..165bb02d0d --- /dev/null +++ b/web/src/reducers/errors.js @@ -0,0 +1,47 @@ +// Copyright 2018 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +import update from 'immutability-helper' + +import { + ADD_ERROR, + CLEAR_ERROR, + CLEAR_ERRORS, + addApiError, +} from '../actions/errors' + + +export default (state = [], action) => { + // Intercept API failure + if (action.error && action.type.match(/.*_FETCH_FAIL$/)) { + action = addApiError(action.error) + } + switch (action.type) { + case ADD_ERROR: + if (state.filter(error => ( + error.url === action.error.url && + error.status === action.error.status)).length > 0) + return state + action.error.id = action.id + action.error.date = Date.now() + return update(state, {$push: [action.error]}) + case CLEAR_ERROR: + return update(state, {$splice: [[state.indexOf( + state.filter(item => (item.id === action.id))[0]), 1]]}) + case CLEAR_ERRORS: + return [] + default: + return state + } +} diff --git a/web/src/reducers/index.js b/web/src/reducers/index.js index 6f287aed55..ab6aab8ec0 100644 --- a/web/src/reducers/index.js +++ b/web/src/reducers/index.js @@ -15,12 +15,14 @@ import { combineReducers } from 'redux' import configErrors from './configErrors' +import errors from './errors' import info from './info' import tenant from './tenant' const reducers = { info, configErrors, + errors, tenant, } diff --git a/web/yarn.lock b/web/yarn.lock index 2cee873f7e..54e5a3c9da 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -3762,6 +3762,12 @@ ignore@^4.0.2: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" +immutability-helper@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/immutability-helper/-/immutability-helper-2.8.1.tgz#3c5ec05fcd83676bfae7146f319595243ad904f4" + dependencies: + invariant "^2.2.0" + import-lazy@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" @@ -3857,7 +3863,7 @@ interpret@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" -invariant@^2.0.0, invariant@^2.1.0, invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.4: +invariant@^2.0.0, invariant@^2.1.0, invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" dependencies: @@ -5027,7 +5033,7 @@ moment-timezone@^0.4.0, moment-timezone@^0.4.1: dependencies: moment ">= 2.6.0" -"moment@>= 2.6.0", moment@^2.10, moment@^2.19.1: +"moment@>= 2.6.0", moment@^2.10, moment@^2.19.1, moment@^2.22.2: version "2.22.2" resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"