diff options
6 files changed, 86 insertions, 7 deletions
diff --git a/server/sonar-web/src/main/js/apps/code/actions/index.js b/server/sonar-web/src/main/js/apps/code/actions/index.js index a68df6429b7..53437e244c8 100644 --- a/server/sonar-web/src/main/js/apps/code/actions/index.js +++ b/server/sonar-web/src/main/js/apps/code/actions/index.js @@ -26,6 +26,7 @@ export const SEARCH = 'SEARCH'; export const UPDATE_QUERY = 'UPDATE_QUERY'; export const START_FETCHING = 'START_FETCHING'; export const STOP_FETCHING = 'STOP_FETCHING'; +export const RAISE_ERROR = 'RAISE_ERROR'; export function initComponentAction (component, breadcrumbs = []) { @@ -67,6 +68,13 @@ export function stopFetching () { return { type: STOP_FETCHING }; } +export function raiseError (message) { + return { + type: RAISE_ERROR, + message + }; +} + function getPath (componentKey) { return '/' + encodeURIComponent(componentKey); @@ -107,6 +115,22 @@ let requestTree = (query, baseComponent, dispatch) => { }; requestTree = _.debounce(requestTree, 250); +async function getErrorMessage (response) { + switch (response.status) { + case 401: + return window.t('not_authorized'); + default: + try { + let json = await response.json(); + return json['err_msg'] || + (json.errors && _.pluck(json.errors, 'msg').join('. ')) || + window.t('default_error_message'); + } catch (e) { + return window.t('default_error_message'); + } + } +} + export function initComponent (componentKey, breadcrumbs) { return dispatch => { dispatch(startFetching()); @@ -125,7 +149,11 @@ export function browse (componentKey) { dispatch(browseAction(component, children, breadcrumbs)); }) .then(() => dispatch(pushPath(getPath(componentKey)))) - .then(() => dispatch(stopFetching())); + .then(() => dispatch(stopFetching())) + .catch(e => { + getErrorMessage(e.response) + .then(message => dispatch(raiseError(message))); + }); }; } diff --git a/server/sonar-web/src/main/js/apps/code/components/Code.js b/server/sonar-web/src/main/js/apps/code/components/Code.js index 2e54f46c955..53365d97d46 100644 --- a/server/sonar-web/src/main/js/apps/code/components/Code.js +++ b/server/sonar-web/src/main/js/apps/code/components/Code.js @@ -33,7 +33,15 @@ class Code extends Component { } render () { - const { fetching, baseComponent, components, breadcrumbs, sourceViewer, coverageMetric, searchResults } = this.props; + const { + fetching, + baseComponent, + components, + breadcrumbs, + sourceViewer, + coverageMetric, + searchResults, + errorMessage } = this.props; const shouldShowBreadcrumbs = Array.isArray(breadcrumbs) && breadcrumbs.length > 1; const shouldShowSearchResults = !!searchResults; const shouldShowSourceViewer = !!sourceViewer; @@ -55,6 +63,12 @@ class Code extends Component { <Search component={this.props.component}/> </header> + {errorMessage && ( + <div className="alert alert-danger"> + {errorMessage} + </div> + )} + {shouldShowBreadcrumbs && ( <Breadcrumbs breadcrumbs={breadcrumbs} diff --git a/server/sonar-web/src/main/js/apps/code/reducers/index.js b/server/sonar-web/src/main/js/apps/code/reducers/index.js index c177efd55fc..7ecd8fe27e9 100644 --- a/server/sonar-web/src/main/js/apps/code/reducers/index.js +++ b/server/sonar-web/src/main/js/apps/code/reducers/index.js @@ -1,6 +1,6 @@ import _ from 'underscore'; -import { INIT, BROWSE, SEARCH, UPDATE_QUERY, START_FETCHING, STOP_FETCHING } from '../actions'; +import { INIT, BROWSE, SEARCH, UPDATE_QUERY, START_FETCHING, STOP_FETCHING, RAISE_ERROR } from '../actions'; function hasSourceCode (component) { @@ -37,7 +37,8 @@ export const initialState = { searchResults: null, searchQuery: '', coverageMetric: null, - baseBreadcrumbs: [] + baseBreadcrumbs: [], + errorMessage: null }; @@ -55,15 +56,34 @@ export function current (state = initialState, action) { const breadcrumbs = action.breadcrumbs.slice(baseBreadcrumbsLength); const sourceViewer = hasSourceCode(action.component) ? action.component : null; - return { ...state, baseComponent, components, breadcrumbs, sourceViewer, searchResults: null, searchQuery: '' }; + return { + ...state, + baseComponent, + components, + breadcrumbs, + sourceViewer, + searchResults: null, + searchQuery: '', + errorMessage: null + }; case SEARCH: - return { ...state, searchResults: action.components }; + return { + ...state, + searchResults: action.components, + errorMessage: null + }; case UPDATE_QUERY: return { ...state, searchQuery: action.query }; case START_FETCHING: return { ...state, fetching: true }; case STOP_FETCHING: return { ...state, fetching: false }; + case RAISE_ERROR: + return { + ...state, + errorMessage: action.message, + fetching: false + }; default: return state; } diff --git a/server/sonar-web/tests/apps/code/store-test.js b/server/sonar-web/tests/apps/code/store-test.js index 9f883e7f8c6..dbff7efd961 100644 --- a/server/sonar-web/tests/apps/code/store-test.js +++ b/server/sonar-web/tests/apps/code/store-test.js @@ -7,7 +7,8 @@ import { searchAction, updateQueryAction, startFetching, - stopFetching + stopFetching, + raiseError } from '../../../src/main/js/apps/code/actions'; @@ -207,6 +208,20 @@ describe('Code :: Store', () => { .to.equal(''); }); }); + describe('errorMessage', () => { + it('should be set', () => { + expect(current(initialState, raiseError('error!')).errorMessage) + .to.equal('error!'); + }); + + it('should be reset', () => { + const stateBefore = Object.assign({}, initialState, { errorMessage: 'error!' }); + expect(current(stateBefore, browseAction(exampleComponent)).errorMessage) + .to.be.null; + expect(current(stateBefore, searchAction(exampleComponents)).errorMessage) + .to.be.null; + }); + }); }); describe('bucket', () => { it('should add initial component', () => { diff --git a/server/sonar-web/tests/jsdom-setup.js b/server/sonar-web/tests/jsdom-setup.js index e911cbeb95b..f1d92d7d818 100644 --- a/server/sonar-web/tests/jsdom-setup.js +++ b/server/sonar-web/tests/jsdom-setup.js @@ -1,4 +1,5 @@ /* globals global: false */ +require("babel-polyfill"); var jsdom = require('jsdom'); diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 8f40801694f..86e4eac69e7 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -236,6 +236,7 @@ new_window=New window no_data=No data no_lines_match_your_filter_criteria=No lines match your filter criteria. no_results=No results +not_authorized=You are not authorized. not_authorized_to_access_project=You are not authorized to access to this '{0}' project over_x_days=over {0} days over_x_days.short={0} days |