diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/auto_follow_pattern_edit/auto_follow_pattern_edit.container.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/auto_follow_pattern_edit/auto_follow_pattern_edit.container.js index 26a312692e5ef..3f8c8331f4909 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/sections/auto_follow_pattern_edit/auto_follow_pattern_edit.container.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/auto_follow_pattern_edit/auto_follow_pattern_edit.container.js @@ -7,8 +7,13 @@ import { connect } from 'react-redux'; import { SECTIONS } from '../../constants'; -import { getApiStatus, getApiError, getSelectedAutoFollowPattern } from '../../store/selectors'; -import { getAutoFollowPattern, saveAutoFollowPattern, clearApiError } from '../../store/actions'; +import { + getApiStatus, + getApiError, + getSelectedAutoFollowPatternId, + getSelectedAutoFollowPattern, +} from '../../store/selectors'; +import { getAutoFollowPattern, saveAutoFollowPattern, selectEditAutoFollowPattern, clearApiError } from '../../store/actions'; import { AutoFollowPatternEdit as AutoFollowPatternEditView } from './auto_follow_pattern_edit'; const scope = SECTIONS.AUTO_FOLLOW_PATTERN; @@ -16,11 +21,13 @@ const scope = SECTIONS.AUTO_FOLLOW_PATTERN; const mapStateToProps = (state) => ({ apiStatus: getApiStatus(scope)(state), apiError: getApiError(scope)(state), - autoFollowPattern: getSelectedAutoFollowPattern(state), + autoFollowPatternId: getSelectedAutoFollowPatternId('edit')(state), + autoFollowPattern: getSelectedAutoFollowPattern('edit')(state), }); const mapDispatchToProps = dispatch => ({ getAutoFollowPattern: (id) => dispatch(getAutoFollowPattern(id)), + selectAutoFollowPattern: (id) => dispatch(selectEditAutoFollowPattern(id)), saveAutoFollowPattern: (id, autoFollowPattern) => dispatch(saveAutoFollowPattern(id, autoFollowPattern, true)), clearApiError: () => dispatch(clearApiError(scope)), }); diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/auto_follow_pattern_edit/auto_follow_pattern_edit.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/auto_follow_pattern_edit/auto_follow_pattern_edit.js index 46a802e9c228f..2e7f1cf89909e 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/sections/auto_follow_pattern_edit/auto_follow_pattern_edit.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/auto_follow_pattern_edit/auto_follow_pattern_edit.js @@ -37,22 +37,40 @@ export const AutoFollowPatternEdit = injectI18n( class extends PureComponent { static propTypes = { getAutoFollowPattern: PropTypes.func.isRequired, + selectAutoFollowPattern: PropTypes.func.isRequired, saveAutoFollowPattern: PropTypes.func.isRequired, clearApiError: PropTypes.func.isRequired, apiError: PropTypes.object, apiStatus: PropTypes.string.isRequired, + autoFollowPattern: PropTypes.object, + autoFollowPatternId: PropTypes.string, } - componentDidMount() { - const { autoFollowPattern, match: { params: { id } } } = this.props; - if (!autoFollowPattern) { - const decodedId = decodeURIComponent(id); - this.props.getAutoFollowPattern(decodedId); + static getDerivedStateFromProps({ autoFollowPatternId }, { lastAutoFollowPatternId }) { + if (lastAutoFollowPatternId !== autoFollowPatternId) { + return { lastAutoFollowPatternId: autoFollowPatternId }; } + } + + state = { lastAutoFollowPatternId: undefined } + + componentDidMount() { + const { match: { params: { id } }, selectAutoFollowPattern } = this.props; + const decodedId = decodeURIComponent(id); + + selectAutoFollowPattern(decodedId); chrome.breadcrumbs.set([ MANAGEMENT_BREADCRUMB, listBreadcrumb, editBreadcrumb ]); } + componentDidUpdate(prevProps, prevState) { + const { autoFollowPattern, getAutoFollowPattern } = this.props; + if (!autoFollowPattern && prevState.lastAutoFollowPatternId !== this.state.lastAutoFollowPatternId) { + // Fetch the auto-follow pattern on the server + getAutoFollowPattern(this.state.lastAutoFollowPatternId); + } + } + componentWillUnmount() { this.props.clearApiError(); } diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.container.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.container.js index efed7e2593c97..7e1b650f521d8 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.container.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.container.js @@ -9,15 +9,14 @@ import { connect } from 'react-redux'; import { SECTIONS } from '../../../constants'; import { getListAutoFollowPatterns, + getSelectedAutoFollowPatternId, getApiStatus, getApiError, isApiAuthorized, - isAutoFollowPatternDetailPanelOpen as isDetailPanelOpen, } from '../../../store/selectors'; import { loadAutoFollowPatterns, - openAutoFollowPatternDetailPanel as openDetailPanel, - closeAutoFollowPatternDetailPanel as closeDetailPanel, + selectDetailAutoFollowPattern, } from '../../../store/actions'; import { AutoFollowPatternList as AutoFollowPatternListView } from './auto_follow_pattern_list'; @@ -25,20 +24,15 @@ const scope = SECTIONS.AUTO_FOLLOW_PATTERN; const mapStateToProps = (state) => ({ autoFollowPatterns: getListAutoFollowPatterns(state), + autoFollowPatternId: getSelectedAutoFollowPatternId('detail')(state), apiStatus: getApiStatus(scope)(state), apiError: getApiError(scope)(state), isAuthorized: isApiAuthorized(scope)(state), - isDetailPanelOpen: isDetailPanelOpen(state), }); const mapDispatchToProps = dispatch => ({ loadAutoFollowPatterns: (inBackground) => dispatch(loadAutoFollowPatterns(inBackground)), - openDetailPanel: (name) => { - dispatch(openDetailPanel(name)); - }, - closeDetailPanel: () => { - dispatch(closeDetailPanel()); - }, + selectAutoFollowPattern: (id) => dispatch(selectDetailAutoFollowPattern(id)), }); export const AutoFollowPatternList = connect( diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.js index 34768f655be69..0bc8231c5cbc5 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.js @@ -17,49 +17,71 @@ import { AutoFollowPatternTable, DetailPanel } from './components'; const REFRESH_RATE_MS = 30000; +const getQueryParamPattern = ({ location: { search } }) => { + const { pattern } = extractQueryParams(search); + return pattern ? decodeURIComponent(pattern) : null; +}; + export const AutoFollowPatternList = injectI18n( class extends PureComponent { static propTypes = { loadAutoFollowPatterns: PropTypes.func, + selectAutoFollowPattern: PropTypes.func, autoFollowPatterns: PropTypes.array, apiStatus: PropTypes.string, apiError: PropTypes.object, - openDetailPanel: PropTypes.func.isRequired, - closeDetailPanel: PropTypes.func.isRequired, - isDetailPanelOpen: PropTypes.bool, } + static getDerivedStateFromProps({ autoFollowPatternId }, { lastAutoFollowPatternId }) { + if (autoFollowPatternId !== lastAutoFollowPatternId) { + return { + lastAutoFollowPatternId: autoFollowPatternId, + isDetailPanelOpen: !!autoFollowPatternId, + }; + } + return null; + } + + state = { + lastAutoFollowPatternId: null, + isDetailPanelOpen: false, + }; + componentDidMount() { - this.props.loadAutoFollowPatterns(); + const { loadAutoFollowPatterns, selectAutoFollowPattern, history } = this.props; + + loadAutoFollowPatterns(); + + // Select the pattern in the URL query params + selectAutoFollowPattern(getQueryParamPattern(history)); // Interval to load auto-follow patterns in the background passing "true" to the fetch method - this.interval = setInterval(() => this.props.loadAutoFollowPatterns(true), REFRESH_RATE_MS); + this.interval = setInterval(() => loadAutoFollowPatterns(true), REFRESH_RATE_MS); } - componentWillUnmount() { - clearInterval(this.interval); + componentDidUpdate(prevProps, prevState) { + const { history } = this.props; + const { lastAutoFollowPatternId } = this.state; + + /** + * Each time our state is updated (through getDerivedStateFromProps()) + * we persist the auto-follow pattern id to query params for deep linking + */ + if (lastAutoFollowPatternId !== prevState.lastAutoFollowPatternId) { + if(!lastAutoFollowPatternId) { + history.replace({ + search: '', + }); + } else { + history.replace({ + search: `?pattern=${encodeURIComponent(lastAutoFollowPatternId)}`, + }); + } + } } - componentDidUpdate() { - const { - openDetailPanel, - closeDetailPanel, - isDetailPanelOpen, - history: { - location: { - search, - }, - }, - } = this.props; - - const { pattern: patternName } = extractQueryParams(search); - - // Show deeplinked auto follow pattern whenever patterns get loaded or the URL changes. - if (patternName != null) { - openDetailPanel(patternName); - } else if (isDetailPanelOpen) { - closeDetailPanel(); - } + componentWillUnmount() { + clearInterval(this.interval); } renderEmpty() { @@ -102,7 +124,13 @@ export const AutoFollowPatternList = injectI18n( } renderList() { - const { autoFollowPatterns, apiStatus } = this.props; + const { + selectAutoFollowPattern, + autoFollowPatterns, + apiStatus, + } = this.props; + + const { isDetailPanelOpen } = this.state; if (apiStatus === API_STATUS.LOADING) { return ( @@ -118,7 +146,7 @@ export const AutoFollowPatternList = injectI18n( return ( - + {isDetailPanelOpen && selectAutoFollowPattern(null)} />} ); } diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.container.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.container.js index f631533f68385..d80bf65bd0013 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.container.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.container.js @@ -7,10 +7,7 @@ import { connect } from 'react-redux'; import { SECTIONS } from '../../../../../constants'; -import { - editAutoFollowPattern, - openAutoFollowPatternDetailPanel as openDetailPanel, -} from '../../../../../store/actions'; +import { selectDetailAutoFollowPattern } from '../../../../../store/actions'; import { getApiStatus } from '../../../../../store/selectors'; import { AutoFollowPatternTable as AutoFollowPatternTableComponent } from './auto_follow_pattern_table'; @@ -21,10 +18,7 @@ const mapStateToProps = (state) => ({ }); const mapDispatchToProps = (dispatch) => ({ - editAutoFollowPattern: (name) => dispatch(editAutoFollowPattern(name)), - openDetailPanel: (name) => { - dispatch(openDetailPanel(name)); - }, + selectAutoFollowPattern: (name) => dispatch(selectDetailAutoFollowPattern(name)), }); export const AutoFollowPatternTable = connect( diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js index fa42a80b4ecd4..1a9966896db5c 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js @@ -25,7 +25,7 @@ export const AutoFollowPatternTable = injectI18n( class extends PureComponent { static propTypes = { autoFollowPatterns: PropTypes.array, - openDetailPanel: PropTypes.func.isRequired, + selectAutoFollowPattern: PropTypes.func.isRequired, } state = { @@ -61,7 +61,7 @@ export const AutoFollowPatternTable = injectI18n( }; getTableColumns() { - const { intl, editAutoFollowPattern, openDetailPanel } = this.props; + const { intl, selectAutoFollowPattern } = this.props; return [{ field: 'name', @@ -73,7 +73,7 @@ export const AutoFollowPatternTable = injectI18n( truncateText: false, render: (name) => { return ( - openDetailPanel(name)}> + selectAutoFollowPattern(name)}> {name} ); @@ -142,20 +142,25 @@ export const AutoFollowPatternTable = injectI18n( }, }, { - name: intl.formatMessage({ - id: 'xpack.crossClusterReplication.editIndexPattern.fields.table.actionEditLabel', - defaultMessage: 'Edit', - }), - description: intl.formatMessage({ - id: 'xpack.crossClusterReplication.editIndexPattern.fields.table.actionEditDescription', - defaultMessage: 'Edit', - }), - icon: 'pencil', - onClick: ({ name }) => { - editAutoFollowPattern(name); - routing.navigate(encodeURI(`/auto_follow_patterns/edit/${encodeURIComponent(name)}`)); + render: ({ name }) => { + const label = i18n.translate('xpack.crossClusterReplication.autoFollowPatternList.table.actionEditDescription', { + defaultMessage: 'Edit auto-follow pattern', + }); + + return ( + + + + ); }, - type: 'icon', }, ], width: '100px', diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.container.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.container.js index 3f8183eb5158e..6446800388729 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.container.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.container.js @@ -7,45 +7,17 @@ import { connect } from 'react-redux'; import { DetailPanel as DetailPanelView } from './detail_panel'; -import { - getDetailPanelAutoFollowPattern, - getDetailPanelAutoFollowPatternName, - getApiStatus, - isAutoFollowPatternDetailPanelOpen as isDetailPanelOpen, -} from '../../../../../store/selectors'; - -import { - closeAutoFollowPatternDetailPanel as closeDetailPanel, - editAutoFollowPattern, -} from '../../../../../store/actions'; - -import { - SECTIONS -} from '../../../../../constants'; +import { getSelectedAutoFollowPattern, getSelectedAutoFollowPatternId, getApiStatus, } from '../../../../../store/selectors'; +import { SECTIONS } from '../../../../../constants'; const scope = SECTIONS.AUTO_FOLLOW_PATTERN; -const mapStateToProps = (state) => { - return { - isDetailPanelOpen: isDetailPanelOpen(state), - autoFollowPattern: getDetailPanelAutoFollowPattern(state), - autoFollowPatternName: getDetailPanelAutoFollowPatternName(state), - apiStatus: getApiStatus(scope)(state), - }; -}; - -const mapDispatchToProps = (dispatch) => { - return { - closeDetailPanel: () => { - dispatch(closeDetailPanel()); - }, - editAutoFollowPattern: (name) => { - dispatch(editAutoFollowPattern(name)); - } - }; -}; +const mapStateToProps = (state) => ({ + autoFollowPatternId: getSelectedAutoFollowPatternId('detail')(state), + autoFollowPattern: getSelectedAutoFollowPattern('detail')(state), + apiStatus: getApiStatus(scope)(state), +}); export const DetailPanel = connect( mapStateToProps, - mapDispatchToProps )(DetailPanelView); diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js index c2c239960743d..0be3de11d6f9e 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js @@ -41,12 +41,10 @@ import routing from '../../../../../services/routing'; export class DetailPanelUi extends Component { static propTypes = { - isDetailPanelOpen: PropTypes.bool.isRequired, apiStatus: PropTypes.string, + autoFollowPatternId: PropTypes.string, autoFollowPattern: PropTypes.object, - autoFollowPatternName: PropTypes.string, closeDetailPanel: PropTypes.func.isRequired, - editAutoFollowPattern: PropTypes.func.isRequired, } renderAutoFollowPattern() { @@ -192,7 +190,7 @@ export class DetailPanelUi extends Component { autoFollowPattern, } = this.props; - if(apiStatus === API_STATUS.LOADING) { + if (apiStatus === API_STATUS.LOADING) { return ( @@ -277,56 +269,49 @@ export class DetailPanelUi extends Component { - - - - - {(deleteAutoFollowPattern) => ( - deleteAutoFollowPattern(autoFollowPatternName)} - > - - - )} - - - - - { - editAutoFollowPattern(autoFollowPatternName); - routing.navigate(encodeURI(`/auto_follow_patterns/edit/${encodeURIComponent(autoFollowPatternName)}`)); - }} - > - - - - - + {autoFollowPattern && ( + + + + + {(deleteAutoFollowPattern) => ( + deleteAutoFollowPattern(autoFollowPattern.name)} + > + + + )} + + + + + { + routing.navigate(encodeURI(`/auto_follow_patterns/edit/${encodeURIComponent(autoFollowPattern.name)}`)); + }} + > + + + + + + )} ); } render() { - const { - isDetailPanelOpen, - closeDetailPanel, - autoFollowPatternName, - } = this.props; - - if (!isDetailPanelOpen) { - return null; - } + const { autoFollowPatternId, closeDetailPanel } = this.props; return ( + -

{autoFollowPatternName}

+

{autoFollowPatternId}

{this.renderContent()} - {this.renderFooter()}
); diff --git a/x-pack/plugins/cross_cluster_replication/public/app/services/routing.js b/x-pack/plugins/cross_cluster_replication/public/app/services/routing.js index 988201fd240f7..fd24a8932f532 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/services/routing.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/services/routing.js @@ -80,6 +80,10 @@ class Routing { }); } + getAutoFollowPatternPath = (name, section = '/edit') => { + return encodeURI(`#${BASE_PATH}/auto_follow_patterns${section}/${encodeURIComponent(name)}`); + }; + get reactRouter() { return this._reactRouter; } diff --git a/x-pack/plugins/cross_cluster_replication/public/app/store/action_types.js b/x-pack/plugins/cross_cluster_replication/public/app/store/action_types.js index dd618161aefce..c018e7b44ebcc 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/store/action_types.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/store/action_types.js @@ -11,10 +11,10 @@ export const API_REQUEST_END = 'API_REQUEST_END'; export const API_ERROR_SET = 'API_ERROR_SET'; // Auto Follow Pattern -export const AUTO_FOLLOW_PATTERN_EDIT = 'AUTO_FOLLOW_PATTERN_EDIT'; +export const AUTO_FOLLOW_PATTERN_SELECT_DETAIL = 'AUTO_FOLLOW_PATTERN_SELECT_DETAIL'; +export const AUTO_FOLLOW_PATTERN_SELECT_EDIT = 'AUTO_FOLLOW_PATTERN_SELECT_EDIT'; export const AUTO_FOLLOW_PATTERN_LOAD = 'AUTO_FOLLOW_PATTERN_LOAD'; export const AUTO_FOLLOW_PATTERN_GET = 'AUTO_FOLLOW_PATTERN_GET'; export const AUTO_FOLLOW_PATTERN_CREATE = 'AUTO_FOLLOW_PATTERN_CREATE'; export const AUTO_FOLLOW_PATTERN_UPDATE = 'AUTO_FOLLOW_PATTERN_UPDATE'; export const AUTO_FOLLOW_PATTERN_DELETE = 'AUTO_FOLLOW_PATTERN_DELETE'; -export const AUTO_FOLLOW_PATTERN_DETAIL_PANEL = 'AUTO_FOLLOW_PATTERN_DETAIL_PANEL'; diff --git a/x-pack/plugins/cross_cluster_replication/public/app/store/actions/api.js b/x-pack/plugins/cross_cluster_replication/public/app/store/actions/api.js index 7b5fa621dddea..0bc4be5c7be04 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/store/actions/api.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/store/actions/api.js @@ -7,15 +7,6 @@ import * as t from '../action_types'; import { API_STATUS } from '../../constants'; -export const sendApiRequest = ({ - label, - scope, - status, - handler, - onSuccess = () => undefined, - onError = () => undefined, -}) => ({ type: t.API, payload: { label, scope, status, handler, onSuccess, onError } }); - export const apiRequestStart = ({ label, scope, status = API_STATUS.LOADING }) => ({ type: t.API_REQUEST_START, payload: { label, scope, status }, @@ -29,3 +20,32 @@ export const setApiError = ({ error, scope }) => ({ }); export const clearApiError = scope => ({ type: t.API_ERROR_SET, payload: { error: null, scope } }); + +export const sendApiRequest = ({ + label, + scope, + status, + handler, + onSuccess = () => undefined, + onError = () => undefined, +}) => async (dispatch, getState) => { + + dispatch(clearApiError(scope)); + dispatch(apiRequestStart({ label, scope, status })); + + try { + const response = await handler(dispatch); + + dispatch(apiRequestEnd({ label, scope })); + dispatch({ type: `${label}_SUCCESS`, payload: response }); + + onSuccess(response, dispatch, getState); + + } catch (error) { + dispatch(apiRequestEnd({ label, scope })); + dispatch(setApiError({ error, scope })); + dispatch({ type: `${label}_FAILURE`, payload: error }); + + onError(error, dispatch, getState); + } +}; diff --git a/x-pack/plugins/cross_cluster_replication/public/app/store/actions/auto_follow_pattern.js b/x-pack/plugins/cross_cluster_replication/public/app/store/actions/auto_follow_pattern.js index e02085ceb0290..b041be4e4bea5 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/store/actions/auto_follow_pattern.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/store/actions/auto_follow_pattern.js @@ -16,25 +16,18 @@ import { import routing from '../../services/routing'; import * as t from '../action_types'; import { sendApiRequest } from './api'; -import { getDetailPanelAutoFollowPatternName } from '../selectors'; +import { getSelectedAutoFollowPatternId } from '../selectors'; const { AUTO_FOLLOW_PATTERN: scope } = SECTIONS; -export const editAutoFollowPattern = (name) => ({ - type: t.AUTO_FOLLOW_PATTERN_EDIT, - payload: name +export const selectDetailAutoFollowPattern = (id) => ({ + type: t.AUTO_FOLLOW_PATTERN_SELECT_DETAIL, + payload: id }); -export const openAutoFollowPatternDetailPanel = (name) => { - return { - type: t.AUTO_FOLLOW_PATTERN_DETAIL_PANEL, - payload: name - }; -}; - -export const closeAutoFollowPatternDetailPanel = () => ({ - type: t.AUTO_FOLLOW_PATTERN_DETAIL_PANEL, - payload: null +export const selectEditAutoFollowPattern = (id) => ({ + type: t.AUTO_FOLLOW_PATTERN_SELECT_EDIT, + payload: id }); export const loadAutoFollowPatterns = (isUpdating = false) => @@ -42,21 +35,17 @@ export const loadAutoFollowPatterns = (isUpdating = false) => label: t.AUTO_FOLLOW_PATTERN_LOAD, scope, status: isUpdating ? API_STATUS.UPDATING : API_STATUS.LOADING, - handler: async () => { - return await loadAutoFollowPatternsRequest(); - }, + handler: async () => ( + await loadAutoFollowPatternsRequest() + ), }); export const getAutoFollowPattern = (id) => sendApiRequest({ label: t.AUTO_FOLLOW_PATTERN_GET, scope, - handler: async (dispatch) => ( - getAutoFollowPatternRequest(id) - .then((response) => { - dispatch(editAutoFollowPattern(id)); - return response; - }) + handler: async () => ( + await getAutoFollowPatternRequest(id) ) }); @@ -132,12 +121,12 @@ export const deleteAutoFollowPattern = (id) => ( }); toastNotifications.addSuccess(successMessage); - } - // If we've just deleted a pattern we were looking at, we need to close the panel. - const detailPanelAutoFollowPatternName = getDetailPanelAutoFollowPatternName(getState()); - if (detailPanelAutoFollowPatternName && response.itemsDeleted.includes(detailPanelAutoFollowPatternName)) { - dispatch(closeAutoFollowPatternDetailPanel()); + // If we've just deleted a pattern we were looking at, we need to close the panel. + const autoFollowPatternId = getSelectedAutoFollowPatternId('detail')(getState()); + if (response.itemsDeleted.includes(autoFollowPatternId)) { + dispatch(selectDetailAutoFollowPattern(null)); + } } } }) diff --git a/x-pack/plugins/cross_cluster_replication/public/app/store/middleware/api.js b/x-pack/plugins/cross_cluster_replication/public/app/store/middleware/api.js deleted file mode 100644 index 234aa24c07748..0000000000000 --- a/x-pack/plugins/cross_cluster_replication/public/app/store/middleware/api.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 * as t from '../action_types'; -import { apiRequestStart, apiRequestEnd, setApiError, clearApiError } from '../actions/api'; - -export const apiMiddleware = ({ dispatch, getState }) => next => async (action) => { - next(action); - - if (action.type !== t.API) { - return; - } - - const { label, scope, status, handler, onSuccess, onError } = action.payload; - - dispatch(clearApiError(scope)); - dispatch(apiRequestStart({ label, scope, status })); - - try { - const response = await handler(dispatch); - - dispatch(apiRequestEnd({ label, scope })); - dispatch({ type: `${label}_SUCCESS`, payload: response }); - - onSuccess(response, dispatch, getState); - - } catch (error) { - dispatch(apiRequestEnd({ label, scope })); - dispatch(setApiError({ error, scope })); - dispatch({ type: `${label}_FAILURE`, payload: error }); - - onError(error, dispatch, getState); - } -}; diff --git a/x-pack/plugins/cross_cluster_replication/public/app/store/middleware/auto_follow_pattern.js b/x-pack/plugins/cross_cluster_replication/public/app/store/middleware/auto_follow_pattern.js deleted file mode 100644 index 214f4768be7a7..0000000000000 --- a/x-pack/plugins/cross_cluster_replication/public/app/store/middleware/auto_follow_pattern.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 routing from '../../services/routing'; -import * as t from '../action_types'; -import { extractQueryParams } from '../../services/query_params'; - -export const autoFollowPatternMiddleware = () => next => action => { - const { type, payload: name } = action; - const { history } = routing.reactRouter; - const search = history.location.search; - const { pattern: patternName } = extractQueryParams(search); - - switch (type) { - case t.AUTO_FOLLOW_PATTERN_DETAIL_PANEL: - if (!routing.userHasLeftApp) { - // Persist state to query params by removing deep link. - if(!name) { - history.replace({ - search: '', - }); - } - // Allow the user to share a deep link to this job. - else if (patternName !== name) { - history.replace({ - search: `?pattern=${encodeURIComponent(name)}`, - }); - } - } - - break; - } - - return next(action); -}; diff --git a/x-pack/plugins/cross_cluster_replication/public/app/store/middleware/index.js b/x-pack/plugins/cross_cluster_replication/public/app/store/middleware/index.js deleted file mode 100644 index 5097ec64e8284..0000000000000 --- a/x-pack/plugins/cross_cluster_replication/public/app/store/middleware/index.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - * 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 { apiMiddleware } from './api'; -export { autoFollowPatternMiddleware } from './auto_follow_pattern'; diff --git a/x-pack/plugins/cross_cluster_replication/public/app/store/reducers/auto_follow_pattern.js b/x-pack/plugins/cross_cluster_replication/public/app/store/reducers/auto_follow_pattern.js index 398f949db9425..2170bc539d033 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/store/reducers/auto_follow_pattern.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/store/reducers/auto_follow_pattern.js @@ -10,8 +10,8 @@ import { getPrefixSuffixFromFollowPattern } from '../../services/auto_follow_pat const initialState = { byId: {}, - selectedId: null, - detailPanelId: null, + selectedDetailId: null, + selectedEditId: null, }; const success = action => `${action}_SUCCESS`; @@ -31,11 +31,11 @@ export const reducer = (state = initialState, action) => { case success(t.AUTO_FOLLOW_PATTERN_GET): { return { ...state, byId: { ...state.byId, [action.payload.name]: parseAutoFollowPattern(action.payload) } }; } - case t.AUTO_FOLLOW_PATTERN_EDIT: { - return { ...state, selectedId: action.payload }; + case t.AUTO_FOLLOW_PATTERN_SELECT_DETAIL: { + return { ...state, selectedDetailId: action.payload }; } - case t.AUTO_FOLLOW_PATTERN_DETAIL_PANEL: { - return { ...state, detailPanelId: action.payload }; + case t.AUTO_FOLLOW_PATTERN_SELECT_EDIT: { + return { ...state, selectedEditId: action.payload }; } case success(t.AUTO_FOLLOW_PATTERN_DELETE): { const byId = { ...state.byId }; diff --git a/x-pack/plugins/cross_cluster_replication/public/app/store/selectors/index.js b/x-pack/plugins/cross_cluster_replication/public/app/store/selectors/index.js index bf680fb157879..52e638cc0c513 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/store/selectors/index.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/store/selectors/index.js @@ -21,21 +21,15 @@ export const isApiAuthorized = (scope) => createSelector(getApiError(scope), (er // Auto-follow pattern export const getAutoFollowPatternState = (state) => state.autoFollowPattern; export const getAutoFollowPatterns = createSelector(getAutoFollowPatternState, (autoFollowPatternsState) => autoFollowPatternsState.byId); -export const getDetailPanelAutoFollowPatternName = createSelector(getAutoFollowPatternState, - (autoFollowPatternsState) => autoFollowPatternsState.detailPanelId); -export const getSelectedAutoFollowPattern = createSelector(getAutoFollowPatternState, (autoFollowPatternsState) => { - if(!autoFollowPatternsState.selectedId) { - return null; - } - return autoFollowPatternsState.byId[autoFollowPatternsState.selectedId]; -}); -export const isAutoFollowPatternDetailPanelOpen = createSelector(getAutoFollowPatternState, (autoFollowPatternsState) => { - return !!autoFollowPatternsState.detailPanelId; -}); -export const getDetailPanelAutoFollowPattern = createSelector(getAutoFollowPatternState, (autoFollowPatternsState) => { - if(!autoFollowPatternsState.detailPanelId) { +export const getSelectedAutoFollowPatternId = (view = 'detail') => createSelector(getAutoFollowPatternState, (autoFollowPatternsState) => ( + view === 'detail' ? autoFollowPatternsState.selectedDetailId : autoFollowPatternsState.selectedEditId +)); +export const getSelectedAutoFollowPattern = (view = 'detail') => createSelector(getAutoFollowPatternState, (autoFollowPatternsState) => { + const propId = view === 'detail' ? 'selectedDetailId' : 'selectedEditId'; + + if(!autoFollowPatternsState[propId]) { return null; } - return autoFollowPatternsState.byId[autoFollowPatternsState.detailPanelId]; + return autoFollowPatternsState.byId[autoFollowPatternsState[propId]]; }); export const getListAutoFollowPatterns = createSelector(getAutoFollowPatterns, (autoFollowPatterns) => objectToArray(autoFollowPatterns)); diff --git a/x-pack/plugins/cross_cluster_replication/public/app/store/store.js b/x-pack/plugins/cross_cluster_replication/public/app/store/store.js index 6d2a700deab41..b6674d89f6278 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/store/store.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/store/store.js @@ -5,12 +5,12 @@ */ import { applyMiddleware, compose, createStore } from 'redux'; +import thunk from 'redux-thunk'; -import { apiMiddleware, autoFollowPatternMiddleware } from './middleware'; import { ccr } from './reducers'; function createCrossClusterReplicationStore(initialState = {}) { - const enhancers = [applyMiddleware(apiMiddleware, autoFollowPatternMiddleware)]; + const enhancers = [applyMiddleware(thunk)]; if (window.__REDUX_DEVTOOLS_EXTENSION__) { enhancers.push(window.__REDUX_DEVTOOLS_EXTENSION__()); diff --git a/x-pack/plugins/remote_clusters/public/sections/remote_cluster_list/detail_panel/detail_panel.js b/x-pack/plugins/remote_clusters/public/sections/remote_cluster_list/detail_panel/detail_panel.js index b90ad0148709f..130e92aba0332 100644 --- a/x-pack/plugins/remote_clusters/public/sections/remote_cluster_list/detail_panel/detail_panel.js +++ b/x-pack/plugins/remote_clusters/public/sections/remote_cluster_list/detail_panel/detail_panel.js @@ -287,11 +287,6 @@ export const DetailPanel = injectI18n( closeDetailPanel, } = this.props; - // Remote clusters configured by a node's elasticsearch.yml file can't be edited or removed. - if (!cluster || cluster.isConfiguredByNode) { - return null; - } - return ( @@ -308,38 +303,40 @@ export const DetailPanel = injectI18n( - - - - - {(removeCluster) => ( - - - - )} - - - - - - - - - - + {cluster && !cluster.isConfiguredByNode && ( + + + + + {(removeCluster) => ( + + + + )} + + + + + + + + + + + )} );