From 13d42010a9d8f9525ef64d484f0d3f5b54d992ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yulia=20=C4=8Cech?= <6585477+yuliacech@users.noreply.github.com> Date: Wed, 28 Oct 2020 14:04:32 +0100 Subject: [PATCH] Add uri decode to es_ui_shared and fix navigation issues with special characters (#80835) * Add uri decode to es_ui_shared and fix data stream issue with % name * Add a test for data streams tab opened for name with a dollar sign * Import uri decode function from es_ui_shared and fix navigation issues for filters * Add tests for data streams with special characters in name * Revert react router navigate changes (is done in a separate PR) * Reverting changes to dataManagement es client and get data stream api route * Fix data stream name filter when activated from a url parameter * Clean up for better consistency and fixes after https://github.com/elastic/kibana/pull/81664 Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/plugins/es_ui_shared/public/index.ts | 2 +- .../public/url/attempt_to_uri_decode.test.ts | 32 ++++++++ .../public/url/attempt_to_uri_decode.ts | 32 ++++++++ src/plugins/es_ui_shared/public/url/index.ts | 1 + .../policy_table/components/table_content.tsx | 2 +- .../home/data_streams_tab.helpers.ts | 25 +++++++ .../home/data_streams_tab.test.ts | 75 +++++++++++++------ .../component_template_details.tsx | 11 ++- .../component_template_list.tsx | 8 +- .../component_template_clone.tsx | 5 +- .../component_template_edit.tsx | 13 +++- .../component_templates/lib/index.ts | 2 - .../component_templates/lib/utils.ts | 18 ----- .../component_templates/shared_imports.ts | 1 + .../data_stream_list/data_stream_list.tsx | 24 +++--- .../data_stream_table/data_stream_table.tsx | 16 +--- .../index_list/index_table/index_table.js | 8 +- .../template_table/template_table.tsx | 7 +- .../template_details_content.tsx | 12 +-- .../home/template_list/template_list.tsx | 3 +- .../template_table/template_table.tsx | 10 +-- .../template_clone/template_clone.tsx | 5 +- .../sections/template_edit/template_edit.tsx | 7 +- .../public/application/services/routing.ts | 29 ++----- .../index_management/public/shared_imports.ts | 1 + .../api/data_streams/register_get_route.ts | 7 +- .../pipelines_clone/pipelines_clone.tsx | 3 +- .../pipelines_edit/pipelines_edit.tsx | 3 +- .../sections/shared/attempt_to_uri_decode.ts | 15 ---- .../application/sections/shared/index.ts | 7 -- .../ingest_pipelines/public/shared_imports.ts | 1 + 31 files changed, 220 insertions(+), 165 deletions(-) create mode 100644 src/plugins/es_ui_shared/public/url/attempt_to_uri_decode.test.ts create mode 100644 src/plugins/es_ui_shared/public/url/attempt_to_uri_decode.ts delete mode 100644 x-pack/plugins/index_management/public/application/components/component_templates/lib/utils.ts delete mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/shared/attempt_to_uri_decode.ts delete mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/shared/index.ts diff --git a/src/plugins/es_ui_shared/public/index.ts b/src/plugins/es_ui_shared/public/index.ts index 94b084e7d3f20f..f48198459d48d4 100644 --- a/src/plugins/es_ui_shared/public/index.ts +++ b/src/plugins/es_ui_shared/public/index.ts @@ -57,7 +57,7 @@ export { export { Forms, ace, GlobalFlyout, XJson }; -export { extractQueryParams } from './url'; +export { extractQueryParams, attemptToURIDecode } from './url'; /** dummy plugin, we just want esUiShared to have its own bundle */ export function plugin() { diff --git a/src/plugins/es_ui_shared/public/url/attempt_to_uri_decode.test.ts b/src/plugins/es_ui_shared/public/url/attempt_to_uri_decode.test.ts new file mode 100644 index 00000000000000..15750c76678004 --- /dev/null +++ b/src/plugins/es_ui_shared/public/url/attempt_to_uri_decode.test.ts @@ -0,0 +1,32 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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 { attemptToURIDecode } from './attempt_to_uri_decode'; + +test('decodes an encoded string', () => { + const encodedString = 'test%3F'; + expect(attemptToURIDecode(encodedString)).toBe('test?'); +}); + +// react router partially decodes %25 sequence to % in match params +// https://github.com/elastic/kibana/pull/81664 +test('ignores the error if a string is already decoded', () => { + const decodedString = 'test%'; + expect(attemptToURIDecode(decodedString)).toBe(decodedString); +}); diff --git a/src/plugins/es_ui_shared/public/url/attempt_to_uri_decode.ts b/src/plugins/es_ui_shared/public/url/attempt_to_uri_decode.ts new file mode 100644 index 00000000000000..65444b83f77bb2 --- /dev/null +++ b/src/plugins/es_ui_shared/public/url/attempt_to_uri_decode.ts @@ -0,0 +1,32 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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. + */ + +/* + * Use this function with any match params coming from react router to safely decode values. + * https://github.com/elastic/kibana/pull/81664 + */ +export const attemptToURIDecode = (value: string) => { + let result = value; + try { + result = decodeURIComponent(value); + } catch (e) { + // do nothing + } + return result; +}; diff --git a/src/plugins/es_ui_shared/public/url/index.ts b/src/plugins/es_ui_shared/public/url/index.ts index 692e094f9eda49..a40885545ca0cf 100644 --- a/src/plugins/es_ui_shared/public/url/index.ts +++ b/src/plugins/es_ui_shared/public/url/index.ts @@ -18,3 +18,4 @@ */ export { extractQueryParams } from './extract_query_params'; +export { attemptToURIDecode } from './attempt_to_uri_decode'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_table/components/table_content.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_table/components/table_content.tsx index 3481a2f0d4a2a6..b655cb771e94db 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_table/components/table_content.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_table/components/table_content.tsx @@ -193,7 +193,7 @@ export const TableContent: React.FunctionComponent = ({ icon: 'list', onClick: () => { navigateToApp('management', { - path: `/data/index_management${getIndexListUri(`ilm.policy:${policy.name}`, true)}`, + path: `/data/index_management${getIndexListUri(`ilm.policy:"${policy.name}"`, true)}`, }); }, }); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts index 2fcf2a822cb2d1..82bd858240e1e1 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts @@ -186,3 +186,28 @@ export const createDataStreamPayload = (name: string): DataStream => ({ storageSize: '1b', maxTimeStamp: 420, }); + +export const createDataStreamBackingIndex = (indexName: string, dataStreamName: string) => ({ + health: '', + status: '', + primary: '', + replica: '', + documents: '', + documents_deleted: '', + size: '', + primary_size: '', + name: indexName, + data_stream: dataStreamName, +}); + +export const createNonDataStreamIndex = (name: string) => ({ + health: 'green', + status: 'open', + primary: 1, + replica: 1, + documents: 10000, + documents_deleted: 100, + size: '156kb', + primary_size: '156kb', + name, +}); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts index ade4a62ceb1989..23539659d2af2e 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts @@ -9,7 +9,13 @@ import { act } from 'react-dom/test-utils'; import { API_BASE_PATH } from '../../../common/constants'; import { setupEnvironment } from '../helpers'; -import { DataStreamsTabTestBed, setup, createDataStreamPayload } from './data_streams_tab.helpers'; +import { + DataStreamsTabTestBed, + setup, + createDataStreamPayload, + createDataStreamBackingIndex, + createNonDataStreamIndex, +} from './data_streams_tab.helpers'; describe('Data Streams tab', () => { const { server, httpRequestsMockHelpers } = setupEnvironment(); @@ -85,29 +91,8 @@ describe('Data Streams tab', () => { } = httpRequestsMockHelpers; setLoadIndicesResponse([ - { - health: '', - status: '', - primary: '', - replica: '', - documents: '', - documents_deleted: '', - size: '', - primary_size: '', - name: 'data-stream-index', - data_stream: 'dataStream1', - }, - { - health: 'green', - status: 'open', - primary: 1, - replica: 1, - documents: 10000, - documents_deleted: 100, - size: '156kb', - primary_size: '156kb', - name: 'non-data-stream-index', - }, + createDataStreamBackingIndex('data-stream-index', 'dataStream1'), + createNonDataStreamIndex('non-data-stream-index'), ]); const dataStreamForDetailPanel = createDataStreamPayload('dataStream1'); @@ -260,4 +245,46 @@ describe('Data Streams tab', () => { }); }); }); + + describe('when there are special characters', () => { + beforeEach(async () => { + const { + setLoadIndicesResponse, + setLoadDataStreamsResponse, + setLoadDataStreamResponse, + } = httpRequestsMockHelpers; + + setLoadIndicesResponse([ + createDataStreamBackingIndex('data-stream-index', '%dataStream'), + createDataStreamBackingIndex('data-stream-index2', 'dataStream2'), + ]); + + const dataStreamDollarSign = createDataStreamPayload('%dataStream'); + setLoadDataStreamsResponse([dataStreamDollarSign]); + setLoadDataStreamResponse(dataStreamDollarSign); + + testBed = await setup(); + await act(async () => { + testBed.actions.goToDataStreamsList(); + }); + testBed.component.update(); + }); + + describe('detail panel', () => { + test('opens when the data stream name in the table is clicked', async () => { + const { actions, findDetailPanel, findDetailPanelTitle } = testBed; + await actions.clickNameAt(0); + expect(findDetailPanel().length).toBe(1); + expect(findDetailPanelTitle()).toBe('%dataStream'); + }); + + test('clicking the indices count navigates to the backing indices', async () => { + const { table, actions } = testBed; + await actions.clickIndicesAt(0); + expect(table.getMetaData('indexTable').tableCellsValues).toEqual([ + ['', '', '', '', '', '', '', '%dataStream'], + ]); + }); + }); + }); }); diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_details/component_template_details.tsx b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_details/component_template_details.tsx index 0f5bc64c358b9e..6d9aa58d6c86b8 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_details/component_template_details.tsx +++ b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_details/component_template_details.tsx @@ -20,12 +20,17 @@ import { EuiBadge, } from '@elastic/eui'; -import { SectionLoading, TabSettings, TabAliases, TabMappings } from '../shared_imports'; +import { + SectionLoading, + TabSettings, + TabAliases, + TabMappings, + attemptToURIDecode, +} from '../shared_imports'; import { useComponentTemplatesContext } from '../component_templates_context'; import { TabSummary } from './tab_summary'; import { ComponentTemplateTabs, TabType } from './tabs'; import { ManageButton, ManageAction } from './manage_button'; -import { attemptToDecodeURI } from '../lib'; export interface Props { componentTemplateName: string; @@ -47,7 +52,7 @@ export const ComponentTemplateDetailsFlyoutContent: React.FunctionComponent { const { api } = useComponentTemplatesContext(); - const decodedComponentTemplateName = attemptToDecodeURI(componentTemplateName); + const decodedComponentTemplateName = attemptToURIDecode(componentTemplateName); const { data: componentTemplateDetails, isLoading, error } = api.useLoadComponentTemplate( decodedComponentTemplateName diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_list/component_template_list.tsx b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_list/component_template_list.tsx index 05f7f53969dedd..00ea3ebf794eed 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_list/component_template_list.tsx +++ b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_list/component_template_list.tsx @@ -11,9 +11,9 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { ScopedHistory } from 'kibana/public'; import { EuiLink, EuiText, EuiSpacer } from '@elastic/eui'; +import { attemptToURIDecode } from '../../../../shared_imports'; import { SectionLoading, ComponentTemplateDeserialized, GlobalFlyout } from '../shared_imports'; import { UIM_COMPONENT_TEMPLATE_LIST_LOAD } from '../constants'; -import { attemptToDecodeURI } from '../lib'; import { useComponentTemplatesContext } from '../component_templates_context'; import { ComponentTemplateDetailsFlyoutContent, @@ -84,7 +84,7 @@ export const ComponentTemplateList: React.FunctionComponent = ({ }), icon: 'pencil', handleActionClick: () => - goToEditComponentTemplate(attemptToDecodeURI(componentTemplateName)), + goToEditComponentTemplate(attemptToURIDecode(componentTemplateName)), }, { name: i18n.translate('xpack.idxMgmt.componentTemplateDetails.cloneActionLabel', { @@ -92,7 +92,7 @@ export const ComponentTemplateList: React.FunctionComponent = ({ }), icon: 'copy', handleActionClick: () => - goToCloneComponentTemplate(attemptToDecodeURI(componentTemplateName)), + goToCloneComponentTemplate(attemptToURIDecode(componentTemplateName)), }, { name: i18n.translate('xpack.idxMgmt.componentTemplateDetails.deleteButtonLabel', { @@ -103,7 +103,7 @@ export const ComponentTemplateList: React.FunctionComponent = ({ details._kbnMeta.usedBy.length > 0, closePopoverOnClick: true, handleActionClick: () => { - setComponentTemplatesToDelete([attemptToDecodeURI(componentTemplateName)]); + setComponentTemplatesToDelete([attemptToURIDecode(componentTemplateName)]); }, }, ]; diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_wizard/component_template_clone/component_template_clone.tsx b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_wizard/component_template_clone/component_template_clone.tsx index 94db623f313c79..6c03fcf5d99729 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_wizard/component_template_clone/component_template_clone.tsx +++ b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_wizard/component_template_clone/component_template_clone.tsx @@ -9,9 +9,8 @@ import { RouteComponentProps } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SectionLoading } from '../../shared_imports'; +import { SectionLoading, attemptToURIDecode } from '../../shared_imports'; import { useComponentTemplatesContext } from '../../component_templates_context'; -import { attemptToDecodeURI } from '../../lib'; import { ComponentTemplateCreate } from '../component_template_create'; export interface Params { @@ -20,7 +19,7 @@ export interface Params { export const ComponentTemplateClone: FunctionComponent> = (props) => { const { sourceComponentTemplateName } = props.match.params; - const decodedSourceName = attemptToDecodeURI(sourceComponentTemplateName); + const decodedSourceName = attemptToURIDecode(sourceComponentTemplateName); const { toasts, api } = useComponentTemplatesContext(); diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_wizard/component_template_edit/component_template_edit.tsx b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_wizard/component_template_edit/component_template_edit.tsx index 2bd3dfb34acb9a..934f86f7d7590c 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_wizard/component_template_edit/component_template_edit.tsx +++ b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_wizard/component_template_edit/component_template_edit.tsx @@ -9,8 +9,11 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiPageBody, EuiPageContent, EuiTitle, EuiSpacer, EuiCallOut } from '@elastic/eui'; import { useComponentTemplatesContext } from '../../component_templates_context'; -import { ComponentTemplateDeserialized, SectionLoading } from '../../shared_imports'; -import { attemptToDecodeURI } from '../../lib'; +import { + ComponentTemplateDeserialized, + SectionLoading, + attemptToURIDecode, +} from '../../shared_imports'; import { ComponentTemplateForm } from '../component_template_form'; interface MatchParams { @@ -28,7 +31,7 @@ export const ComponentTemplateEdit: React.FunctionComponent(false); const [saveError, setSaveError] = useState(null); - const decodedName = attemptToDecodeURI(name); + const decodedName = attemptToURIDecode(name); const { error, data: componentTemplate, isLoading } = api.useLoadComponentTemplate(decodedName); @@ -50,7 +53,9 @@ export const ComponentTemplateEdit: React.FunctionComponent { - let result: string; - - try { - result = decodeURI(value); - result = decodeURIComponent(result); - } catch (e) { - result = decodeURIComponent(value); - } - - return result; -}; diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/shared_imports.ts b/x-pack/plugins/index_management/public/application/components/component_templates/shared_imports.ts index ffd78925c16a07..4a9771bd5ba8a7 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/shared_imports.ts +++ b/x-pack/plugins/index_management/public/application/components/component_templates/shared_imports.ts @@ -20,6 +20,7 @@ export { NotAuthorizedSection, Forms, GlobalFlyout, + attemptToURIDecode, } from '../../../../../../../src/plugins/es_ui_shared/public'; export { diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_list.tsx index 4f2a5c4a27b7aa..19286523055f5c 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_list.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_list.tsx @@ -20,13 +20,17 @@ import { } from '@elastic/eui'; import { ScopedHistory } from 'kibana/public'; -import { reactRouterNavigate, extractQueryParams } from '../../../../shared_imports'; +import { + reactRouterNavigate, + extractQueryParams, + attemptToURIDecode, +} from '../../../../shared_imports'; import { useAppContext } from '../../../app_context'; import { SectionError, SectionLoading, Error } from '../../../components'; import { useLoadDataStreams } from '../../../services/api'; -import { encodePathForReactRouter, decodePathFromReactRouter } from '../../../services/routing'; +import { getIndexListUri } from '../../../services/routing'; import { documentationService } from '../../../services/documentation'; -import { Section } from '../../home'; +import { Section } from '../home'; import { DataStreamTable } from './data_stream_table'; import { DataStreamDetailPanel } from './data_stream_detail_panel'; @@ -206,7 +210,7 @@ export const DataStreamList: React.FunctionComponent { history.push(`/${Section.DataStreams}`); diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx index 642123c5114199..992d569bf3a427 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx @@ -12,9 +12,8 @@ import { ScopedHistory } from 'kibana/public'; import { DataStream } from '../../../../../../common/types'; import { UseRequestResponse, reactRouterNavigate } from '../../../../../shared_imports'; -import { encodePathForReactRouter } from '../../../../services/routing'; +import { getDataStreamDetailsLink, getIndexListUri } from '../../../../services/routing'; import { DataHealth } from '../../../../components'; -import { Section } from '../../../home'; import { DeleteDataStreamConfirmationModal } from '../delete_data_stream_confirmation_modal'; import { humanizeTimeStamp } from '../humanize_time_stamp'; @@ -45,13 +44,11 @@ export const DataStreamTable: React.FunctionComponent = ({ }), truncateText: true, sortable: true, - render: (name: DataStream['name'], item: DataStream) => { + render: (name: DataStream['name']) => { return ( {name} @@ -108,12 +105,7 @@ export const DataStreamTable: React.FunctionComponent = ({ render: (indices: DataStream['indices'], dataStream) => ( {indices.length} diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js index b4e003b667074b..7b09f20091110a 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -35,9 +35,9 @@ import { } from '@elastic/eui'; import { UIM_SHOW_DETAILS_CLICK } from '../../../../../../common/constants'; -import { reactRouterNavigate } from '../../../../../shared_imports'; +import { reactRouterNavigate, attemptToURIDecode } from '../../../../../shared_imports'; import { REFRESH_RATE_INDEX_LIST } from '../../../../constants'; -import { encodePathForReactRouter } from '../../../../services/routing'; +import { getDataStreamDetailsLink } from '../../../../services/routing'; import { documentationService } from '../../../../services/documentation'; import { AppContextConsumer } from '../../../../app_context'; import { renderBadges } from '../../../../lib/render_badges'; @@ -107,7 +107,7 @@ export class IndexTable extends Component { const { location, filterChanged } = this.props; const { filter } = qs.parse((location && location.search) || ''); if (filter) { - const decodedFilter = decodeURIComponent(filter); + const decodedFilter = attemptToURIDecode(filter); try { const filter = EuiSearchBar.Query.parse(decodedFilter); @@ -279,7 +279,7 @@ export class IndexTable extends Component { diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/legacy_templates/template_table/template_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/legacy_templates/template_table/template_table.tsx index 7ec6f1f94a2ab9..29b841f3bdc7a5 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/template_list/legacy_templates/template_table/template_table.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/legacy_templates/template_table/template_table.tsx @@ -13,7 +13,7 @@ import { UseRequestResponse, reactRouterNavigate } from '../../../../../../share import { TemplateListItem } from '../../../../../../../common'; import { UIM_TEMPLATE_SHOW_DETAILS_CLICK } from '../../../../../../../common/constants'; import { TemplateDeleteModal } from '../../../../../components'; -import { encodePathForReactRouter } from '../../../../../services/routing'; +import { getTemplateDetailsLink } from '../../../../../services/routing'; import { useServices } from '../../../../../app_context'; import { TemplateContentIndicator } from '../../../../../components/shared'; import { TemplateTypeIndicator } from '../../components'; @@ -53,10 +53,7 @@ export const LegacyTemplateTable: React.FunctionComponent = ({ uiMetricService.trackMetric('click', UIM_TEMPLATE_SHOW_DETAILS_CLICK) )} data-test-subj="templateDetailsLink" diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details_content.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details_content.tsx index 48083f324de3da..4899c5c664ebad 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details_content.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details_content.tsx @@ -34,7 +34,6 @@ import { import { UseRequestResponse } from '../../../../../shared_imports'; import { TemplateDeleteModal, SectionLoading, SectionError, Error } from '../../../../components'; import { useLoadIndexTemplate } from '../../../../services/api'; -import { decodePathFromReactRouter } from '../../../../services/routing'; import { useServices } from '../../../../app_context'; import { TabAliases, TabMappings, TabSettings } from '../../../../components/shared'; import { TemplateTypeIndicator } from '../components'; @@ -103,11 +102,7 @@ export const TemplateDetailsContent = ({ reload, }: Props) => { const { uiMetricService } = useServices(); - const decodedTemplateName = decodePathFromReactRouter(templateName); - const { error, data: templateDetails, isLoading } = useLoadIndexTemplate( - decodedTemplateName, - isLegacy - ); + const { error, data: templateDetails, isLoading } = useLoadIndexTemplate(templateName, isLegacy); const isCloudManaged = templateDetails?._kbnMeta.type === 'cloudManaged'; const [templateToDelete, setTemplateToDelete] = useState< Array<{ name: string; isLegacy?: boolean }> @@ -120,7 +115,7 @@ export const TemplateDetailsContent = ({

- {decodedTemplateName} + {templateName} {templateDetails && ( <>   @@ -303,8 +298,7 @@ export const TemplateDetailsContent = ({ defaultMessage: 'Delete', }), icon: 'trash', - onClick: () => - setTemplateToDelete([{ name: decodedTemplateName, isLegacy }]), + onClick: () => setTemplateToDelete([{ name: templateName, isLegacy }]), disabled: isCloudManaged, }, ], diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx index c711f457123fb1..f3e82223c30e6c 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx @@ -36,6 +36,7 @@ import { TemplateTable } from './template_table'; import { TemplateDetails } from './template_details'; import { LegacyTemplateTable } from './legacy_templates/template_table'; import { FilterListButton, Filters } from './components'; +import { attemptToURIDecode } from '../../../../shared_imports'; type FilterName = 'managed' | 'cloudManaged' | 'system'; interface MatchParams { @@ -100,7 +101,7 @@ export const TemplateList: React.FunctionComponent = ({ return ( <> uiMetricService.trackMetric('click', UIM_TEMPLATE_SHOW_DETAILS_CLICK) + {...reactRouterNavigate(history, getTemplateDetailsLink(name), () => + uiMetricService.trackMetric('click', UIM_TEMPLATE_SHOW_DETAILS_CLICK) )} data-test-subj="templateDetailsLink" > diff --git a/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx b/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx index 2aaecbd64ee28d..5bb355d90478bb 100644 --- a/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx +++ b/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx @@ -11,9 +11,10 @@ import { EuiPageBody, EuiPageContent, EuiTitle } from '@elastic/eui'; import { TemplateDeserialized } from '../../../../common'; import { TemplateForm, SectionLoading, SectionError, Error } from '../../components'; import { breadcrumbService } from '../../services/breadcrumbs'; -import { decodePathFromReactRouter, getTemplateDetailsLink } from '../../services/routing'; +import { getTemplateDetailsLink } from '../../services/routing'; import { saveTemplate, useLoadIndexTemplate } from '../../services/api'; import { getIsLegacyFromQueryParams } from '../../lib/index_templates'; +import { attemptToURIDecode } from '../../../shared_imports'; interface MatchParams { name: string; @@ -26,7 +27,7 @@ export const TemplateClone: React.FunctionComponent { - const decodedTemplateName = decodePathFromReactRouter(name); + const decodedTemplateName = attemptToURIDecode(name); const isLegacy = getIsLegacyFromQueryParams(location); const [isSaving, setIsSaving] = useState(false); diff --git a/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx b/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx index 6bdcd03fa5ca40..3e62f7f880f749 100644 --- a/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx +++ b/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx @@ -11,9 +11,10 @@ import { EuiPageBody, EuiPageContent, EuiTitle, EuiSpacer, EuiCallOut } from '@e import { TemplateDeserialized } from '../../../../common'; import { breadcrumbService } from '../../services/breadcrumbs'; import { useLoadIndexTemplate, updateTemplate } from '../../services/api'; -import { decodePathFromReactRouter, getTemplateDetailsLink } from '../../services/routing'; +import { getTemplateDetailsLink } from '../../services/routing'; import { SectionLoading, SectionError, TemplateForm, Error } from '../../components'; import { getIsLegacyFromQueryParams } from '../../lib/index_templates'; +import { attemptToURIDecode } from '../../../shared_imports'; interface MatchParams { name: string; @@ -26,7 +27,7 @@ export const TemplateEdit: React.FunctionComponent { - const decodedTemplateName = decodePathFromReactRouter(name); + const decodedTemplateName = attemptToURIDecode(name); const isLegacy = getIsLegacyFromQueryParams(location); const [isSaving, setIsSaving] = useState(false); @@ -51,7 +52,7 @@ export const TemplateEdit: React.FunctionComponent { diff --git a/x-pack/plugins/index_management/public/application/services/routing.ts b/x-pack/plugins/index_management/public/application/services/routing.ts index 68bf06409e6ab5..d12198d6b17a5b 100644 --- a/x-pack/plugins/index_management/public/application/services/routing.ts +++ b/x-pack/plugins/index_management/public/application/services/routing.ts @@ -6,9 +6,8 @@ export const getTemplateListLink = () => `/templates`; -export const getTemplateDetailsLink = (name: string, isLegacy?: boolean, withHash = false) => { - const baseUrl = `/templates/${encodePathForReactRouter(name)}`; - let url = withHash ? `#${baseUrl}` : baseUrl; +export const getTemplateDetailsLink = (name: string, isLegacy?: boolean) => { + let url = `/templates/${encodeURIComponent(name)}`; if (isLegacy) { url = `${url}?legacy=${isLegacy}`; } @@ -16,7 +15,7 @@ export const getTemplateDetailsLink = (name: string, isLegacy?: boolean, withHas }; export const getTemplateEditLink = (name: string, isLegacy?: boolean) => { - let url = `/edit_template/${encodePathForReactRouter(name)}`; + let url = `/edit_template/${encodeURIComponent(name)}`; if (isLegacy) { url = `${url}?legacy=true`; } @@ -24,7 +23,7 @@ export const getTemplateEditLink = (name: string, isLegacy?: boolean) => { }; export const getTemplateCloneLink = (name: string, isLegacy?: boolean) => { - let url = `/clone_template/${encodePathForReactRouter(name)}`; + let url = `/clone_template/${encodeURIComponent(name)}`; if (isLegacy) { url = `${url}?legacy=true`; } @@ -32,9 +31,7 @@ export const getTemplateCloneLink = (name: string, isLegacy?: boolean) => { }; export const getILMPolicyPath = (policyName: string) => { - return encodeURI( - `/data/index_lifecycle_management/policies/edit/${encodeURIComponent(policyName)}` - ); + return `/data/index_lifecycle_management/policies/edit/${encodeURIComponent(policyName)}`; }; export const getIndexListUri = (filter?: string, includeHiddenIndices?: boolean) => { @@ -53,18 +50,6 @@ export const getIndexListUri = (filter?: string, includeHiddenIndices?: boolean) return '/indices'; }; -export const decodePathFromReactRouter = (pathname: string): string => { - let decodedPath; - try { - decodedPath = decodeURI(pathname); - decodedPath = decodeURIComponent(decodedPath); - } catch (_error) { - decodedPath = decodeURIComponent(pathname); - } - return decodeURIComponent(decodedPath); +export const getDataStreamDetailsLink = (name: string) => { + return encodeURI(`/data_streams/${encodeURIComponent(name)}`); }; - -// Need to add some additonal encoding/decoding logic to work with React Router -// For background, see: https://github.com/ReactTraining/history/issues/505 -export const encodePathForReactRouter = (pathname: string): string => - encodeURIComponent(encodeURIComponent(pathname)); diff --git a/x-pack/plugins/index_management/public/shared_imports.ts b/x-pack/plugins/index_management/public/shared_imports.ts index acb3eb512e2c18..360d3ba473db8b 100644 --- a/x-pack/plugins/index_management/public/shared_imports.ts +++ b/x-pack/plugins/index_management/public/shared_imports.ts @@ -14,6 +14,7 @@ export { Forms, extractQueryParams, GlobalFlyout, + attemptToURIDecode, } from '../../../../src/plugins/es_ui_shared/public'; export { diff --git a/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts b/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts index 34edcb6fb7477d..fa93d9bd0c563b 100644 --- a/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts @@ -83,15 +83,16 @@ export function registerGetOneRoute({ router, license, lib: { isEsError } }: Rou license.guardApiRoute(async (ctx, req, res) => { const { name } = req.params as TypeOf; const { callAsCurrentUser } = ctx.dataManagement!.client; - try { const [ { data_streams: dataStream }, { data_streams: dataStreamsStats }, ] = await Promise.all([ - callAsCurrentUser('dataManagement.getDataStream', { name }), + callAsCurrentUser('dataManagement.getDataStream', { + name, + }), ctx.core.elasticsearch.legacy.client.callAsCurrentUser('transport.request', { - path: `/_data_stream/${name}/_stats`, + path: `/_data_stream/${encodeURIComponent(name)}/_stats`, method: 'GET', query: { human: true, diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx index 6dc7769ac02a92..9465117b6b5895 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx @@ -9,10 +9,9 @@ import { RouteComponentProps } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SectionLoading, useKibana } from '../../../shared_imports'; +import { SectionLoading, useKibana, attemptToURIDecode } from '../../../shared_imports'; import { PipelinesCreate } from '../pipelines_create'; -import { attemptToURIDecode } from '../shared'; export interface ParamProps { sourceName: string; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx index 35ca1635ab9c3d..7e2e85ab23fb30 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx @@ -18,11 +18,10 @@ import { import { EuiCallOut } from '@elastic/eui'; import { Pipeline } from '../../../../common/types'; -import { useKibana, SectionLoading } from '../../../shared_imports'; +import { useKibana, SectionLoading, attemptToURIDecode } from '../../../shared_imports'; import { getListPath } from '../../services/navigation'; import { PipelineForm } from '../../components'; -import { attemptToURIDecode } from '../shared'; interface MatchParams { name: string; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/shared/attempt_to_uri_decode.ts b/x-pack/plugins/ingest_pipelines/public/application/sections/shared/attempt_to_uri_decode.ts deleted file mode 100644 index fe5a0d7932cbbf..00000000000000 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/shared/attempt_to_uri_decode.ts +++ /dev/null @@ -1,15 +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 const attemptToURIDecode = (value: string) => { - let result: string; - try { - result = decodeURI(decodeURIComponent(value)); - } catch (e) { - result = value; - } - return result; -}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/shared/index.ts b/x-pack/plugins/ingest_pipelines/public/application/sections/shared/index.ts deleted file mode 100644 index 9326d13851387b..00000000000000 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/shared/index.ts +++ /dev/null @@ -1,7 +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 { attemptToURIDecode } from './attempt_to_uri_decode'; diff --git a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts index 13de8a74225abd..bea3055c13b2de 100644 --- a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts +++ b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts @@ -24,6 +24,7 @@ export { XJson, JsonEditor, OnJsonEditorUpdateHandler, + attemptToURIDecode, } from '../../../../src/plugins/es_ui_shared/public/'; export {