aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/src/main/js/apps/code/actions/index.js30
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/Code.js16
-rw-r--r--server/sonar-web/src/main/js/apps/code/reducers/index.js28
-rw-r--r--server/sonar-web/tests/apps/code/store-test.js17
-rw-r--r--server/sonar-web/tests/jsdom-setup.js1
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties1
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