Skip to content

Commit dbb06b9

Browse files
[Uptime] Search made easy (#88581) (#92632)
Co-authored-by: Kibana Machine <[email protected]> Co-authored-by: Shahzad <[email protected]>
1 parent 9ef5f01 commit dbb06b9

File tree

40 files changed

+814
-314
lines changed

40 files changed

+814
-314
lines changed

x-pack/plugins/translations/translations/ja-JP.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21781,7 +21781,6 @@
2178121781
"xpack.uptime.filterPopout.searchMessage.ariaLabel": "{title} を検索",
2178221782
"xpack.uptime.integrationLink.missingDataMessage": "この統合に必要なデータが見つかりませんでした。",
2178321783
"xpack.uptime.kueryBar.indexPatternMissingWarningMessage": "インデックスパターンの取得中にエラーが発生しました。",
21784-
"xpack.uptime.kueryBar.searchPlaceholder": "モニター ID、名前、プロトコルタイプなどを検索…",
2178521784
"xpack.uptime.locationAvailabilityViewToggleLegend": "トグルを表示",
2178621785
"xpack.uptime.locationMap.locations.missing.message": "重要な位置情報構成がありません。{codeBlock}フィールドを使用して、アップタイムチェック用に一意の地域を作成できます。",
2178721786
"xpack.uptime.locationMap.locations.missing.message1": "詳細については、ドキュメンテーションを参照してください。",

x-pack/plugins/translations/translations/zh-CN.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21831,7 +21831,6 @@
2183121831
"xpack.uptime.filterPopout.searchMessage.ariaLabel": "搜索 {title}",
2183221832
"xpack.uptime.integrationLink.missingDataMessage": "未找到此集成的所需数据。",
2183321833
"xpack.uptime.kueryBar.indexPatternMissingWarningMessage": "检索索引模式时出错。",
21834-
"xpack.uptime.kueryBar.searchPlaceholder": "搜索监测 ID、名称和协议类型......",
2183521834
"xpack.uptime.locationAvailabilityViewToggleLegend": "视图切换",
2183621835
"xpack.uptime.locationMap.locations.missing.message": "重要的地理位置配置缺失。您可以使用 {codeBlock} 字段为您的运行时间检查创建独特的地理区域。",
2183721836
"xpack.uptime.locationMap.locations.missing.message1": "在我们的文档中获取更多的信息。",

x-pack/plugins/uptime/common/constants/ui.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,5 @@ export enum CERT_STATUS {
6363
EXPIRED = 'EXPIRED',
6464
TOO_OLD = 'TOO_OLD',
6565
}
66+
67+
export const KQL_SYNTAX_LOCAL_STORAGE = 'xpack.uptime.kql.syntax';

x-pack/plugins/uptime/common/runtime_types/monitor/state.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export const FetchMonitorStatesQueryArgsType = t.intersection([
9696
pagination: t.string,
9797
filters: t.string,
9898
statusFilter: t.string,
99+
query: t.string,
99100
}),
100101
t.type({
101102
dateRangeStart: t.string,

x-pack/plugins/uptime/common/runtime_types/ping/histogram.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export interface GetPingHistogramParams {
2323
filters?: string;
2424
monitorId?: string;
2525
bucketSize?: string;
26+
query?: string;
2627
}
2728

2829
export interface HistogramResult {

x-pack/plugins/uptime/public/components/monitor/ping_histogram/ping_histogram_container.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ interface Props {
2121

2222
const Container: React.FC<Props & ResponsiveWrapperProps> = ({ height }) => {
2323
const {
24+
query,
2425
absoluteDateRangeStart,
2526
absoluteDateRangeEnd,
2627
dateRangeStart: dateStart,
@@ -37,8 +38,8 @@ const Container: React.FC<Props & ResponsiveWrapperProps> = ({ height }) => {
3738
const { loading, pingHistogram: data } = useSelector(selectPingHistogram);
3839

3940
useEffect(() => {
40-
dispatch(getPingHistogram({ monitorId, dateStart, dateEnd, filters: esKuery }));
41-
}, [dateStart, dateEnd, monitorId, lastRefresh, esKuery, dispatch]);
41+
dispatch(getPingHistogram.get({ monitorId, dateStart, dateEnd, query, filters: esKuery }));
42+
}, [dateStart, dateEnd, monitorId, lastRefresh, esKuery, dispatch, query]);
4243
return (
4344
<PingHistogramComponent
4445
data={data}

x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,11 @@ export const AlertMonitorStatus: React.FC<Props> = ({
9494
);
9595
useEffect(() => {
9696
dispatch(
97-
getSnapshotCountAction({ dateRangeStart: 'now-24h', dateRangeEnd: 'now', filters: esKuery })
97+
getSnapshotCountAction.get({
98+
dateRangeStart: 'now-24h',
99+
dateRangeEnd: 'now',
100+
filters: esKuery,
101+
})
98102
);
99103
}, [dispatch, esKuery]);
100104

x-pack/plugins/uptime/public/components/overview/kuery_bar/kuery_bar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export function KueryBar({
6868
let currentRequestCheck: string;
6969

7070
const [getUrlParams, updateUrlParams] = useUrlParams();
71-
const { search: kuery } = getUrlParams();
71+
const { search: kuery, query } = getUrlParams();
7272

7373
useEffect(() => {
7474
updateSearchText(kuery);
@@ -155,7 +155,7 @@ export function KueryBar({
155155
dataTestSubj={dataTestSubj}
156156
disabled={indexPatternMissing}
157157
isLoading={isLoadingSuggestions || loading}
158-
initialValue={defaultKuery || kuery}
158+
initialValue={defaultKuery || kuery || query}
159159
onChange={onChange}
160160
onSubmit={onSubmit}
161161
suggestions={state.suggestions.slice(0, suggestionLimit)}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import React from 'react';
9+
import { fireEvent } from '@testing-library/react';
10+
import { render } from '../../../../../lib/helper/rtl_helpers';
11+
import { SearchType } from './search_type';
12+
13+
describe('Kuery bar search type', () => {
14+
it('can change from simple to kq;', () => {
15+
let kqlSyntax = false;
16+
const setKqlSyntax = jest.fn((val: boolean) => {
17+
kqlSyntax = val;
18+
});
19+
20+
const { getByTestId } = render(
21+
<SearchType kqlSyntax={kqlSyntax} setKqlSyntax={setKqlSyntax} />
22+
);
23+
24+
// open popover to change
25+
fireEvent.click(getByTestId('syntaxChangeToKql'));
26+
27+
// change syntax
28+
fireEvent.click(getByTestId('toggleKqlSyntax'));
29+
30+
expect(setKqlSyntax).toHaveBeenCalledWith(true);
31+
expect(setKqlSyntax).toHaveBeenCalledTimes(1);
32+
});
33+
34+
it('can change from kql to simple;', () => {
35+
let kqlSyntax = false;
36+
const setKqlSyntax = jest.fn((val: boolean) => {
37+
kqlSyntax = val;
38+
});
39+
40+
const { getByTestId } = render(
41+
<SearchType kqlSyntax={kqlSyntax} setKqlSyntax={setKqlSyntax} />
42+
);
43+
44+
fireEvent.click(getByTestId('syntaxChangeToKql'));
45+
46+
fireEvent.click(getByTestId('toggleKqlSyntax'));
47+
48+
expect(setKqlSyntax).toHaveBeenCalledWith(true);
49+
expect(setKqlSyntax).toHaveBeenCalledTimes(1);
50+
});
51+
52+
it('clears the query on change to kql', () => {
53+
const setKqlSyntax = jest.fn();
54+
55+
const { history } = render(<SearchType kqlSyntax={true} setKqlSyntax={setKqlSyntax} />, {
56+
url: '/app/uptime?query=test',
57+
});
58+
59+
expect(history?.location.search).toBe('');
60+
});
61+
62+
it('clears the search param on change to simple syntax', () => {
63+
const setKqlSyntax = jest.fn();
64+
65+
const { history } = render(<SearchType kqlSyntax={false} setKqlSyntax={setKqlSyntax} />, {
66+
url: '/app/uptime?search=test',
67+
});
68+
69+
expect(history?.location.search).toBe('');
70+
});
71+
});
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import React, { useEffect, useState } from 'react';
9+
import {
10+
EuiPopover,
11+
EuiFormRow,
12+
EuiSwitch,
13+
EuiButtonEmpty,
14+
EuiPopoverTitle,
15+
EuiText,
16+
EuiSpacer,
17+
EuiLink,
18+
EuiButtonIcon,
19+
} from '@elastic/eui';
20+
import { FormattedMessage } from '@kbn/i18n/react';
21+
import { i18n } from '@kbn/i18n';
22+
import { useKibana } from '../../../../../../../../../src/plugins/kibana_react/public';
23+
import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
24+
import { useUrlParams } from '../../../../../hooks';
25+
import {
26+
CHANGE_SEARCH_BAR_SYNTAX,
27+
CHANGE_SEARCH_BAR_SYNTAX_SIMPLE,
28+
SYNTAX_OPTIONS_LABEL,
29+
} from '../translations';
30+
31+
const BoxesVerticalIcon = euiStyled(EuiButtonIcon)`
32+
padding: 10px 8px 0 8px;
33+
border-radius: 0;
34+
height: 38px;
35+
width: 32px;
36+
background-color: ${(props) => props.theme.eui.euiColorLightestShade};
37+
padding-top: 8px;
38+
padding-bottom: 8px;
39+
cursor: pointer;
40+
`;
41+
42+
interface Props {
43+
kqlSyntax: boolean;
44+
setKqlSyntax: (val: boolean) => void;
45+
}
46+
47+
export const SearchType = ({ kqlSyntax, setKqlSyntax }: Props) => {
48+
const {
49+
services: { docLinks },
50+
} = useKibana();
51+
52+
const [getUrlParams, updateUrlParams] = useUrlParams();
53+
54+
const { query, search } = getUrlParams();
55+
56+
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
57+
58+
const onButtonClick = () => setIsPopoverOpen((prevState) => !prevState);
59+
60+
const closePopover = () => setIsPopoverOpen(false);
61+
62+
useEffect(() => {
63+
if (kqlSyntax && query) {
64+
updateUrlParams({ query: '' });
65+
}
66+
67+
if (!kqlSyntax && search) {
68+
updateUrlParams({ search: '' });
69+
}
70+
}, [kqlSyntax, query, search, updateUrlParams]);
71+
72+
const button = kqlSyntax ? (
73+
<EuiButtonEmpty
74+
data-test-subj="syntaxChangeToSimple"
75+
onClick={onButtonClick}
76+
aria-label={CHANGE_SEARCH_BAR_SYNTAX_SIMPLE}
77+
title={CHANGE_SEARCH_BAR_SYNTAX_SIMPLE}
78+
>
79+
KQL
80+
</EuiButtonEmpty>
81+
) : (
82+
<BoxesVerticalIcon
83+
color="text"
84+
iconType="boxesVertical"
85+
onClick={onButtonClick}
86+
data-test-subj="syntaxChangeToKql"
87+
aria-label={CHANGE_SEARCH_BAR_SYNTAX}
88+
title={CHANGE_SEARCH_BAR_SYNTAX}
89+
/>
90+
);
91+
92+
return (
93+
<EuiPopover
94+
button={button}
95+
isOpen={isPopoverOpen}
96+
closePopover={closePopover}
97+
ownFocus={true}
98+
anchorPosition="downRight"
99+
>
100+
<div style={{ width: '360px' }}>
101+
<EuiPopoverTitle>{SYNTAX_OPTIONS_LABEL}</EuiPopoverTitle>
102+
<EuiText>
103+
<p>
104+
<KqlDescription href={docLinks!.links.query.kueryQuerySyntax} />
105+
</p>
106+
</EuiText>
107+
<EuiSpacer />
108+
<EuiFormRow label={KIBANA_QUERY_LANGUAGE} hasChildLabel={false}>
109+
<EuiSwitch
110+
name="switch"
111+
label={kqlSyntax ? 'On' : 'Off'}
112+
checked={kqlSyntax}
113+
onChange={() => setKqlSyntax(!kqlSyntax)}
114+
data-test-subj="toggleKqlSyntax"
115+
/>
116+
</EuiFormRow>
117+
</div>
118+
</EuiPopover>
119+
);
120+
};
121+
122+
const KqlDescription = ({ href }: { href: string }) => {
123+
return (
124+
<FormattedMessage
125+
id="xpack.uptime.queryBar.syntaxOptionsDescription"
126+
defaultMessage="The {docsLink} (KQL) offers a simplified query
127+
syntax and support for scripted fields. KQL also provides autocomplete if you have
128+
a Basic license or above. If you turn off KQL, Uptime
129+
uses simple wildcard search against {searchField} fields."
130+
values={{
131+
docsLink: (
132+
<EuiLink href={href} target="_blank" external>
133+
{KIBANA_QUERY_LANGUAGE}
134+
</EuiLink>
135+
),
136+
searchField: <strong>Monitor Name, ID, Url</strong>,
137+
}}
138+
/>
139+
);
140+
};
141+
142+
const KIBANA_QUERY_LANGUAGE = i18n.translate('xpack.uptime.query.queryBar.kqlFullLanguageName', {
143+
defaultMessage: 'Kibana Query Language',
144+
});

0 commit comments

Comments
 (0)