Skip to content

Commit 2a94139

Browse files
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 #81664 Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
1 parent 415a90f commit 2a94139

31 files changed

Lines changed: 220 additions & 165 deletions

File tree

src/plugins/es_ui_shared/public/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export {
5757

5858
export { Forms, ace, GlobalFlyout, XJson };
5959

60-
export { extractQueryParams } from './url';
60+
export { extractQueryParams, attemptToURIDecode } from './url';
6161

6262
/** dummy plugin, we just want esUiShared to have its own bundle */
6363
export function plugin() {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { attemptToURIDecode } from './attempt_to_uri_decode';
21+
22+
test('decodes an encoded string', () => {
23+
const encodedString = 'test%3F';
24+
expect(attemptToURIDecode(encodedString)).toBe('test?');
25+
});
26+
27+
// react router partially decodes %25 sequence to % in match params
28+
// https://github.com/elastic/kibana/pull/81664
29+
test('ignores the error if a string is already decoded', () => {
30+
const decodedString = 'test%';
31+
expect(attemptToURIDecode(decodedString)).toBe(decodedString);
32+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
/*
21+
* Use this function with any match params coming from react router to safely decode values.
22+
* https://github.com/elastic/kibana/pull/81664
23+
*/
24+
export const attemptToURIDecode = (value: string) => {
25+
let result = value;
26+
try {
27+
result = decodeURIComponent(value);
28+
} catch (e) {
29+
// do nothing
30+
}
31+
return result;
32+
};

src/plugins/es_ui_shared/public/url/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@
1818
*/
1919

2020
export { extractQueryParams } from './extract_query_params';
21+
export { attemptToURIDecode } from './attempt_to_uri_decode';

x-pack/plugins/index_lifecycle_management/public/application/sections/policy_table/components/table_content.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ export const TableContent: React.FunctionComponent<Props> = ({
193193
icon: 'list',
194194
onClick: () => {
195195
navigateToApp('management', {
196-
path: `/data/index_management${getIndexListUri(`ilm.policy:${policy.name}`, true)}`,
196+
path: `/data/index_management${getIndexListUri(`ilm.policy:"${policy.name}"`, true)}`,
197197
});
198198
},
199199
});

x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,28 @@ export const createDataStreamPayload = (name: string): DataStream => ({
186186
storageSize: '1b',
187187
maxTimeStamp: 420,
188188
});
189+
190+
export const createDataStreamBackingIndex = (indexName: string, dataStreamName: string) => ({
191+
health: '',
192+
status: '',
193+
primary: '',
194+
replica: '',
195+
documents: '',
196+
documents_deleted: '',
197+
size: '',
198+
primary_size: '',
199+
name: indexName,
200+
data_stream: dataStreamName,
201+
});
202+
203+
export const createNonDataStreamIndex = (name: string) => ({
204+
health: 'green',
205+
status: 'open',
206+
primary: 1,
207+
replica: 1,
208+
documents: 10000,
209+
documents_deleted: 100,
210+
size: '156kb',
211+
primary_size: '156kb',
212+
name,
213+
});

x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ import { act } from 'react-dom/test-utils';
99
import { API_BASE_PATH } from '../../../common/constants';
1010
import { setupEnvironment } from '../helpers';
1111

12-
import { DataStreamsTabTestBed, setup, createDataStreamPayload } from './data_streams_tab.helpers';
12+
import {
13+
DataStreamsTabTestBed,
14+
setup,
15+
createDataStreamPayload,
16+
createDataStreamBackingIndex,
17+
createNonDataStreamIndex,
18+
} from './data_streams_tab.helpers';
1319

1420
describe('Data Streams tab', () => {
1521
const { server, httpRequestsMockHelpers } = setupEnvironment();
@@ -85,29 +91,8 @@ describe('Data Streams tab', () => {
8591
} = httpRequestsMockHelpers;
8692

8793
setLoadIndicesResponse([
88-
{
89-
health: '',
90-
status: '',
91-
primary: '',
92-
replica: '',
93-
documents: '',
94-
documents_deleted: '',
95-
size: '',
96-
primary_size: '',
97-
name: 'data-stream-index',
98-
data_stream: 'dataStream1',
99-
},
100-
{
101-
health: 'green',
102-
status: 'open',
103-
primary: 1,
104-
replica: 1,
105-
documents: 10000,
106-
documents_deleted: 100,
107-
size: '156kb',
108-
primary_size: '156kb',
109-
name: 'non-data-stream-index',
110-
},
94+
createDataStreamBackingIndex('data-stream-index', 'dataStream1'),
95+
createNonDataStreamIndex('non-data-stream-index'),
11196
]);
11297

11398
const dataStreamForDetailPanel = createDataStreamPayload('dataStream1');
@@ -260,4 +245,46 @@ describe('Data Streams tab', () => {
260245
});
261246
});
262247
});
248+
249+
describe('when there are special characters', () => {
250+
beforeEach(async () => {
251+
const {
252+
setLoadIndicesResponse,
253+
setLoadDataStreamsResponse,
254+
setLoadDataStreamResponse,
255+
} = httpRequestsMockHelpers;
256+
257+
setLoadIndicesResponse([
258+
createDataStreamBackingIndex('data-stream-index', '%dataStream'),
259+
createDataStreamBackingIndex('data-stream-index2', 'dataStream2'),
260+
]);
261+
262+
const dataStreamDollarSign = createDataStreamPayload('%dataStream');
263+
setLoadDataStreamsResponse([dataStreamDollarSign]);
264+
setLoadDataStreamResponse(dataStreamDollarSign);
265+
266+
testBed = await setup();
267+
await act(async () => {
268+
testBed.actions.goToDataStreamsList();
269+
});
270+
testBed.component.update();
271+
});
272+
273+
describe('detail panel', () => {
274+
test('opens when the data stream name in the table is clicked', async () => {
275+
const { actions, findDetailPanel, findDetailPanelTitle } = testBed;
276+
await actions.clickNameAt(0);
277+
expect(findDetailPanel().length).toBe(1);
278+
expect(findDetailPanelTitle()).toBe('%dataStream');
279+
});
280+
281+
test('clicking the indices count navigates to the backing indices', async () => {
282+
const { table, actions } = testBed;
283+
await actions.clickIndicesAt(0);
284+
expect(table.getMetaData('indexTable').tableCellsValues).toEqual([
285+
['', '', '', '', '', '', '', '%dataStream'],
286+
]);
287+
});
288+
});
289+
});
263290
});

x-pack/plugins/index_management/public/application/components/component_templates/component_template_details/component_template_details.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,17 @@ import {
2020
EuiBadge,
2121
} from '@elastic/eui';
2222

23-
import { SectionLoading, TabSettings, TabAliases, TabMappings } from '../shared_imports';
23+
import {
24+
SectionLoading,
25+
TabSettings,
26+
TabAliases,
27+
TabMappings,
28+
attemptToURIDecode,
29+
} from '../shared_imports';
2430
import { useComponentTemplatesContext } from '../component_templates_context';
2531
import { TabSummary } from './tab_summary';
2632
import { ComponentTemplateTabs, TabType } from './tabs';
2733
import { ManageButton, ManageAction } from './manage_button';
28-
import { attemptToDecodeURI } from '../lib';
2934

3035
export interface Props {
3136
componentTemplateName: string;
@@ -47,7 +52,7 @@ export const ComponentTemplateDetailsFlyoutContent: React.FunctionComponent<Prop
4752
}) => {
4853
const { api } = useComponentTemplatesContext();
4954

50-
const decodedComponentTemplateName = attemptToDecodeURI(componentTemplateName);
55+
const decodedComponentTemplateName = attemptToURIDecode(componentTemplateName);
5156

5257
const { data: componentTemplateDetails, isLoading, error } = api.useLoadComponentTemplate(
5358
decodedComponentTemplateName

x-pack/plugins/index_management/public/application/components/component_templates/component_template_list/component_template_list.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import { FormattedMessage } from '@kbn/i18n/react';
1111
import { ScopedHistory } from 'kibana/public';
1212
import { EuiLink, EuiText, EuiSpacer } from '@elastic/eui';
1313

14+
import { attemptToURIDecode } from '../../../../shared_imports';
1415
import { SectionLoading, ComponentTemplateDeserialized, GlobalFlyout } from '../shared_imports';
1516
import { UIM_COMPONENT_TEMPLATE_LIST_LOAD } from '../constants';
16-
import { attemptToDecodeURI } from '../lib';
1717
import { useComponentTemplatesContext } from '../component_templates_context';
1818
import {
1919
ComponentTemplateDetailsFlyoutContent,
@@ -84,15 +84,15 @@ export const ComponentTemplateList: React.FunctionComponent<Props> = ({
8484
}),
8585
icon: 'pencil',
8686
handleActionClick: () =>
87-
goToEditComponentTemplate(attemptToDecodeURI(componentTemplateName)),
87+
goToEditComponentTemplate(attemptToURIDecode(componentTemplateName)),
8888
},
8989
{
9090
name: i18n.translate('xpack.idxMgmt.componentTemplateDetails.cloneActionLabel', {
9191
defaultMessage: 'Clone',
9292
}),
9393
icon: 'copy',
9494
handleActionClick: () =>
95-
goToCloneComponentTemplate(attemptToDecodeURI(componentTemplateName)),
95+
goToCloneComponentTemplate(attemptToURIDecode(componentTemplateName)),
9696
},
9797
{
9898
name: i18n.translate('xpack.idxMgmt.componentTemplateDetails.deleteButtonLabel', {
@@ -103,7 +103,7 @@ export const ComponentTemplateList: React.FunctionComponent<Props> = ({
103103
details._kbnMeta.usedBy.length > 0,
104104
closePopoverOnClick: true,
105105
handleActionClick: () => {
106-
setComponentTemplatesToDelete([attemptToDecodeURI(componentTemplateName)]);
106+
setComponentTemplatesToDelete([attemptToURIDecode(componentTemplateName)]);
107107
},
108108
},
109109
];

x-pack/plugins/index_management/public/application/components/component_templates/component_template_wizard/component_template_clone/component_template_clone.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@ import { RouteComponentProps } from 'react-router-dom';
99
import { i18n } from '@kbn/i18n';
1010
import { FormattedMessage } from '@kbn/i18n/react';
1111

12-
import { SectionLoading } from '../../shared_imports';
12+
import { SectionLoading, attemptToURIDecode } from '../../shared_imports';
1313
import { useComponentTemplatesContext } from '../../component_templates_context';
14-
import { attemptToDecodeURI } from '../../lib';
1514
import { ComponentTemplateCreate } from '../component_template_create';
1615

1716
export interface Params {
@@ -20,7 +19,7 @@ export interface Params {
2019

2120
export const ComponentTemplateClone: FunctionComponent<RouteComponentProps<Params>> = (props) => {
2221
const { sourceComponentTemplateName } = props.match.params;
23-
const decodedSourceName = attemptToDecodeURI(sourceComponentTemplateName);
22+
const decodedSourceName = attemptToURIDecode(sourceComponentTemplateName);
2423

2524
const { toasts, api } = useComponentTemplatesContext();
2625

0 commit comments

Comments
 (0)