[IM] improve ux for permissions error (#38916) (#38984)

This commit is contained in:
Alison Goryachev 2019-06-14 13:32:57 -04:00 committed by GitHub
parent 1fe4812ccd
commit c8b66a7da3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 132 additions and 15 deletions

View file

@ -16,6 +16,7 @@ import {
isSortAscending,
getIndicesAsArray,
indicesLoading,
indicesError,
getTableState
} from '../../../../store/selectors';
import {
@ -45,6 +46,7 @@ const mapStateToProps = (state) => {
sortField: getSortField(state),
isSortAscending: isSortAscending(state),
indicesLoading: indicesLoading(state),
indicesError: indicesError(state),
toggleNameToVisibleMap: getTableState(state).toggleNameToVisibleMap
};
};

View file

@ -45,6 +45,7 @@ import {
} from '../../../../index_management_extensions';
import { renderBadges } from '../../../../lib/render_badges';
import { NoMatch } from '../../../no_match';
import { PageErrorForbidden } from '../../../page_error';
import { IndexActionsContextMenu } from '../../components';
const HEADERS = {
@ -251,6 +252,44 @@ export class IndexTable extends Component {
);
});
}
renderError() {
const { indicesError } = this.props;
const data = indicesError.data ? indicesError.data : indicesError;
const {
error: errorString,
cause,
message,
} = data;
return (
<Fragment>
<EuiCallOut
title={(
<FormattedMessage
id="xpack.idxMgmt.indexTable.serverErrorTitle"
defaultMessage="Error loading indices"
/>
)}
color="danger"
iconType="alert"
>
<div>{message || errorString}</div>
{cause && (
<Fragment>
<EuiSpacer size="m" />
<ul>
{cause.map((message, i) => <li key={i}>{message}</li>)}
</ul>
</Fragment>
)}
</EuiCallOut>
<EuiSpacer size="xl" />
</Fragment>
);
}
renderBanners() {
const { allIndices = [], filterChanged } = this.props;
return getBannerExtensions().map((bannerExtension, i) => {
@ -341,19 +380,35 @@ export class IndexTable extends Component {
indices,
loadIndices,
indicesLoading,
indicesError,
allIndices,
} = this.props;
const emptyState = indicesLoading ? (
<EuiFlexGroup justifyContent="spaceAround">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="xl" />
</EuiFlexItem>
</EuiFlexGroup>
) : (
<NoMatch />
);
let emptyState;
if (indicesLoading) {
emptyState = (
<EuiFlexGroup justifyContent="spaceAround">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="xl" />
</EuiFlexItem>
</EuiFlexGroup>
);
}
if (!indicesLoading && !indicesError) {
emptyState = (
<NoMatch />
);
}
const { selectedIndicesMap } = this.state;
const atLeastOneItemSelected = Object.keys(selectedIndicesMap).length > 0;
if (indicesError && indicesError.status === 403) {
return <PageErrorForbidden />;
}
return (
<EuiPageContent>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="flexEnd">
@ -377,7 +432,7 @@ export class IndexTable extends Component {
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{indicesLoading && allIndices.length === 0 ? null : (
{((indicesLoading && allIndices.length === 0) || indicesError) ? null : (
<EuiFlexGroup>
{getToggleExtensions().map((toggle) => {
return this.renderToggleControl(toggle);
@ -401,6 +456,7 @@ export class IndexTable extends Component {
</EuiFlexGroup>
<EuiSpacer />
{this.renderBanners()}
{indicesError && this.renderError()}
<EuiFlexGroup gutterSize="l" alignItems="center">
{atLeastOneItemSelected ? (
<EuiFlexItem grow={false}>
@ -417,7 +473,7 @@ export class IndexTable extends Component {
/>
</EuiFlexItem>
) : null}
{indicesLoading && allIndices.length === 0 ? null : (
{((indicesLoading && allIndices.length === 0) || indicesError) ? null : (
<Fragment>
<EuiFlexItem>
<EuiSearchBar

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { PageErrorForbidden } from './page_error_forbidden';

View file

@ -0,0 +1,29 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { EuiEmptyPrompt, EuiPageContent } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
export function PageErrorForbidden() {
return (
<EuiPageContent>
<EuiEmptyPrompt
iconType="securityApp"
iconColor={undefined}
title={
<h1>
<FormattedMessage
id="xpack.idxMgmt.pageErrorForbidden.title"
defaultMessage="You do not have permissions to use Index Management"
/>
</h1>
}
/>
</EuiPageContent>
);
}

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { PageErrorForbidden } from './components';

View file

@ -6,18 +6,18 @@
import { createAction } from 'redux-actions';
import { loadIndices as request } from '../../services';
import { toastNotifications } from 'ui/notify';
export const loadIndicesStart = createAction('INDEX_MANAGEMENT_LOAD_INDICES_START');
export const loadIndicesSuccess = createAction('INDEX_MANAGEMENT_LOAD_INDICES_SUCCESS');
export const loadIndicesError = createAction('INDEX_MANAGEMENT_LOAD_INDICES_ERROR');
export const loadIndices = () => async (dispatch) => {
dispatch(loadIndicesStart());
let indices;
try {
indices = await request();
} catch (error) {
dispatch(loadIndicesSuccess({ indices: [] }));
return toastNotifications.addDanger(error.data.message);
return dispatch(loadIndicesError(error));
}
dispatch(loadIndicesSuccess({ indices }));
};

View file

@ -10,7 +10,8 @@ import {
deleteIndicesSuccess,
loadIndicesSuccess,
reloadIndicesSuccess,
loadIndicesStart
loadIndicesStart,
loadIndicesError,
} from '../actions';
const byId = handleActions({
@ -70,16 +71,30 @@ const allIds = handleActions({
return state;
}
}, []);
const loading = handleActions({
[loadIndicesStart]() {
return true;
},
[loadIndicesSuccess]() {
return false;
},
[loadIndicesError]() {
return false;
}
}, true);
const error = handleActions({
[loadIndicesError](state, action) {
const error = action.payload;
const newState = { ...error };
return newState;
}
}, false);
export const indices = combineReducers({
loading,
error,
byId,
allIds
});

View file

@ -17,6 +17,7 @@ export const isDetailPanelOpen = (state) => !!getDetailPanelType(state);
export const getDetailPanelIndexName = (state) => state.detailPanel.indexName;
export const getIndices = (state) => state.indices.byId;
export const indicesLoading = (state) => state.indices.loading;
export const indicesError = (state) => state.indices.error;
export const getIndicesAsArray = (state) => Object.values(state.indices.byId);
export const getIndicesByName = (state, indexNames) => {
const indices = getIndices(state);