mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
* WIP. Trying some things. * Introduce useUrlParams hook to modify path. * WIP code on setting/reading date range from url params. * Create constants file for app defaults. * Add functions for parsing supported URL parameters. * Add index entry and update useUrlParams hook. * Remove defaults and update application to store persisted state in the URL. * More temp code. * Support URL params and fix filters. * Update Monitor page to accept URL parameters. * Rename a test folder and add tests for new helper functions. * Add functional test for filter query. * Add missing prop to test component. * Update breadcrumb functions to accept search parameters. * Update app to support forwarding of search params in in-app links. * Write snapshot for status bar test. * Fix memory leak.
This commit is contained in:
parent
5d5bfced0c
commit
a532edf7bf
36 changed files with 883 additions and 466 deletions
16
x-pack/plugins/uptime/common/constants/client_defaults.ts
Normal file
16
x-pack/plugins/uptime/common/constants/client_defaults.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* 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 CLIENT_DEFAULTS = {
|
||||
// 60 seconds
|
||||
AUTOREFRESH_INTERVAL: 60 * 1000,
|
||||
// polling defaults to "on"
|
||||
AUTOREFRESH_IS_PAUSED: false,
|
||||
DATE_RANGE_START: 'now-15m',
|
||||
DATE_RANGE_END: 'now',
|
||||
SEARCH: '',
|
||||
SELECTED_PING_LIST_STATUS: 'down',
|
||||
};
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { CLIENT_DEFAULTS } from './client_defaults';
|
||||
export { INDEX_NAMES } from './index_names';
|
||||
export { PLUGIN } from './plugin';
|
||||
export { QUERY } from './query';
|
||||
|
|
|
@ -11,16 +11,18 @@ export interface UMBreadcrumb {
|
|||
href?: string;
|
||||
}
|
||||
|
||||
export const overviewBreadcrumb: UMBreadcrumb = {
|
||||
const makeOverviewBreadcrumb = (search?: string): UMBreadcrumb => ({
|
||||
text: i18n.translate('xpack.uptime.breadcrumbs.overviewBreadcrumbText', {
|
||||
defaultMessage: 'Uptime',
|
||||
}),
|
||||
href: '#/',
|
||||
};
|
||||
href: `#/${search ? search : ''}`,
|
||||
});
|
||||
|
||||
export const getOverviewPageBreadcrumbs = (): UMBreadcrumb[] => [overviewBreadcrumb];
|
||||
export const getOverviewPageBreadcrumbs = (search?: string): UMBreadcrumb[] => [
|
||||
makeOverviewBreadcrumb(search),
|
||||
];
|
||||
|
||||
export const getMonitorPageBreadcrumb = (name: string): UMBreadcrumb[] => [
|
||||
overviewBreadcrumb,
|
||||
export const getMonitorPageBreadcrumb = (name: string, search?: string): UMBreadcrumb[] => [
|
||||
makeOverviewBreadcrumb(search),
|
||||
{ text: name },
|
||||
];
|
||||
|
|
|
@ -1,196 +1,200 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FilterBar component renders the component without errors 1`] = `
|
||||
<EuiSearchBar
|
||||
box={
|
||||
Object {
|
||||
"incremental": false,
|
||||
<div
|
||||
data-test-subj="xpack.uptime.filterBar"
|
||||
>
|
||||
<EuiSearchBar
|
||||
box={
|
||||
Object {
|
||||
"incremental": false,
|
||||
}
|
||||
}
|
||||
}
|
||||
className="euiFlexGroup--gutterSmall"
|
||||
filters={
|
||||
Array [
|
||||
Object {
|
||||
"field": "monitor.status",
|
||||
"items": Array [
|
||||
Object {
|
||||
"name": "Up",
|
||||
"value": "up",
|
||||
},
|
||||
Object {
|
||||
"name": "Down",
|
||||
"value": "down",
|
||||
},
|
||||
],
|
||||
"type": "field_value_toggle_group",
|
||||
},
|
||||
Object {
|
||||
"field": "monitor.id",
|
||||
"multiSelect": false,
|
||||
"name": "ID",
|
||||
"options": Array [
|
||||
Object {
|
||||
"value": "auto-tcp-0X81440A68E839814C",
|
||||
"view": "auto-tcp-0X81440A68E839814C",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0X3675F89EF0612091",
|
||||
"view": "auto-http-0X3675F89EF0612091",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0X970CBD2F2102BFA8",
|
||||
"view": "auto-http-0X970CBD2F2102BFA8",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0X131221E73F825974",
|
||||
"view": "auto-http-0X131221E73F825974",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0X9CB71300ABD5A2A8",
|
||||
"view": "auto-http-0X9CB71300ABD5A2A8",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0XD9AE729FC1C1E04A",
|
||||
"view": "auto-http-0XD9AE729FC1C1E04A",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0XDD2D4E60FD4A61C3",
|
||||
"view": "auto-http-0XDD2D4E60FD4A61C3",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0XA8096548ECEB85B7",
|
||||
"view": "auto-http-0XA8096548ECEB85B7",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0XC9CDA429418EDC2B",
|
||||
"view": "auto-http-0XC9CDA429418EDC2B",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0XE3B163481423197D",
|
||||
"view": "auto-http-0XE3B163481423197D",
|
||||
},
|
||||
],
|
||||
"searchThreshold": 2,
|
||||
"type": "field_value_selection",
|
||||
},
|
||||
Object {
|
||||
"field": "monitor.name",
|
||||
"multiSelect": false,
|
||||
"name": "Name",
|
||||
"options": Array [],
|
||||
"searchThreshold": 2,
|
||||
"type": "field_value_selection",
|
||||
},
|
||||
Object {
|
||||
"field": "url.full",
|
||||
"multiSelect": false,
|
||||
"name": "URL",
|
||||
"options": Array [
|
||||
Object {
|
||||
"value": "tcp://localhost:9200",
|
||||
"view": "tcp://localhost:9200",
|
||||
},
|
||||
Object {
|
||||
"value": "http://localhost:12349/",
|
||||
"view": "http://localhost:12349/",
|
||||
},
|
||||
Object {
|
||||
"value": "http://www.google.com/",
|
||||
"view": "http://www.google.com/",
|
||||
},
|
||||
Object {
|
||||
"value": "https://www.google.com/",
|
||||
"view": "https://www.google.com/",
|
||||
},
|
||||
Object {
|
||||
"value": "https://www.github.com/",
|
||||
"view": "https://www.github.com/",
|
||||
},
|
||||
Object {
|
||||
"value": "http://www.reddit.com/",
|
||||
"view": "http://www.reddit.com/",
|
||||
},
|
||||
Object {
|
||||
"value": "https://www.elastic.co",
|
||||
"view": "https://www.elastic.co",
|
||||
},
|
||||
Object {
|
||||
"value": "http://www.example.com/",
|
||||
"view": "http://www.example.com/",
|
||||
},
|
||||
Object {
|
||||
"value": "https://www.wikipedia.org/",
|
||||
"view": "https://www.wikipedia.org/",
|
||||
},
|
||||
Object {
|
||||
"value": "https://news.google.com/",
|
||||
"view": "https://news.google.com/",
|
||||
},
|
||||
],
|
||||
"searchThreshold": 2,
|
||||
"type": "field_value_selection",
|
||||
},
|
||||
Object {
|
||||
"field": "url.port",
|
||||
"multiSelect": false,
|
||||
"name": "Port",
|
||||
"options": Array [
|
||||
Object {
|
||||
"value": 9200,
|
||||
"view": 9200,
|
||||
},
|
||||
Object {
|
||||
"value": 12349,
|
||||
"view": 12349,
|
||||
},
|
||||
],
|
||||
"searchThreshold": 2,
|
||||
"type": "field_value_selection",
|
||||
},
|
||||
Object {
|
||||
"field": "monitor.type",
|
||||
"multiSelect": false,
|
||||
"name": "Scheme",
|
||||
"options": Array [
|
||||
Object {
|
||||
"value": "tcp",
|
||||
"view": "tcp",
|
||||
},
|
||||
Object {
|
||||
"value": "http",
|
||||
"view": "http",
|
||||
},
|
||||
],
|
||||
"searchThreshold": 2,
|
||||
"type": "field_value_selection",
|
||||
},
|
||||
]
|
||||
}
|
||||
onChange={[MockFunction]}
|
||||
schema={
|
||||
Object {
|
||||
"fields": Object {
|
||||
"monitor.host": Object {
|
||||
"type": "string",
|
||||
className="euiFlexGroup--gutterSmall"
|
||||
filters={
|
||||
Array [
|
||||
Object {
|
||||
"field": "monitor.status",
|
||||
"items": Array [
|
||||
Object {
|
||||
"name": "Up",
|
||||
"value": "up",
|
||||
},
|
||||
Object {
|
||||
"name": "Down",
|
||||
"value": "down",
|
||||
},
|
||||
],
|
||||
"type": "field_value_toggle_group",
|
||||
},
|
||||
"monitor.id": Object {
|
||||
"type": "string",
|
||||
Object {
|
||||
"field": "monitor.id",
|
||||
"multiSelect": false,
|
||||
"name": "ID",
|
||||
"options": Array [
|
||||
Object {
|
||||
"value": "auto-tcp-0X81440A68E839814C",
|
||||
"view": "auto-tcp-0X81440A68E839814C",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0X3675F89EF0612091",
|
||||
"view": "auto-http-0X3675F89EF0612091",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0X970CBD2F2102BFA8",
|
||||
"view": "auto-http-0X970CBD2F2102BFA8",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0X131221E73F825974",
|
||||
"view": "auto-http-0X131221E73F825974",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0X9CB71300ABD5A2A8",
|
||||
"view": "auto-http-0X9CB71300ABD5A2A8",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0XD9AE729FC1C1E04A",
|
||||
"view": "auto-http-0XD9AE729FC1C1E04A",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0XDD2D4E60FD4A61C3",
|
||||
"view": "auto-http-0XDD2D4E60FD4A61C3",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0XA8096548ECEB85B7",
|
||||
"view": "auto-http-0XA8096548ECEB85B7",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0XC9CDA429418EDC2B",
|
||||
"view": "auto-http-0XC9CDA429418EDC2B",
|
||||
},
|
||||
Object {
|
||||
"value": "auto-http-0XE3B163481423197D",
|
||||
"view": "auto-http-0XE3B163481423197D",
|
||||
},
|
||||
],
|
||||
"searchThreshold": 2,
|
||||
"type": "field_value_selection",
|
||||
},
|
||||
"monitor.ip": Object {
|
||||
"type": "string",
|
||||
Object {
|
||||
"field": "monitor.name",
|
||||
"multiSelect": false,
|
||||
"name": "Name",
|
||||
"options": Array [],
|
||||
"searchThreshold": 2,
|
||||
"type": "field_value_selection",
|
||||
},
|
||||
"monitor.scheme": Object {
|
||||
"type": "string",
|
||||
Object {
|
||||
"field": "url.full",
|
||||
"multiSelect": false,
|
||||
"name": "URL",
|
||||
"options": Array [
|
||||
Object {
|
||||
"value": "tcp://localhost:9200",
|
||||
"view": "tcp://localhost:9200",
|
||||
},
|
||||
Object {
|
||||
"value": "http://localhost:12349/",
|
||||
"view": "http://localhost:12349/",
|
||||
},
|
||||
Object {
|
||||
"value": "http://www.google.com/",
|
||||
"view": "http://www.google.com/",
|
||||
},
|
||||
Object {
|
||||
"value": "https://www.google.com/",
|
||||
"view": "https://www.google.com/",
|
||||
},
|
||||
Object {
|
||||
"value": "https://www.github.com/",
|
||||
"view": "https://www.github.com/",
|
||||
},
|
||||
Object {
|
||||
"value": "http://www.reddit.com/",
|
||||
"view": "http://www.reddit.com/",
|
||||
},
|
||||
Object {
|
||||
"value": "https://www.elastic.co",
|
||||
"view": "https://www.elastic.co",
|
||||
},
|
||||
Object {
|
||||
"value": "http://www.example.com/",
|
||||
"view": "http://www.example.com/",
|
||||
},
|
||||
Object {
|
||||
"value": "https://www.wikipedia.org/",
|
||||
"view": "https://www.wikipedia.org/",
|
||||
},
|
||||
Object {
|
||||
"value": "https://news.google.com/",
|
||||
"view": "https://news.google.com/",
|
||||
},
|
||||
],
|
||||
"searchThreshold": 2,
|
||||
"type": "field_value_selection",
|
||||
},
|
||||
"monitor.status": Object {
|
||||
"type": "string",
|
||||
Object {
|
||||
"field": "url.port",
|
||||
"multiSelect": false,
|
||||
"name": "Port",
|
||||
"options": Array [
|
||||
Object {
|
||||
"value": 9200,
|
||||
"view": 9200,
|
||||
},
|
||||
Object {
|
||||
"value": 12349,
|
||||
"view": 12349,
|
||||
},
|
||||
],
|
||||
"searchThreshold": 2,
|
||||
"type": "field_value_selection",
|
||||
},
|
||||
"url.port": Object {
|
||||
"type": "number",
|
||||
Object {
|
||||
"field": "monitor.type",
|
||||
"multiSelect": false,
|
||||
"name": "Scheme",
|
||||
"options": Array [
|
||||
Object {
|
||||
"value": "tcp",
|
||||
"view": "tcp",
|
||||
},
|
||||
Object {
|
||||
"value": "http",
|
||||
"view": "http",
|
||||
},
|
||||
],
|
||||
"searchThreshold": 2,
|
||||
"type": "field_value_selection",
|
||||
},
|
||||
},
|
||||
"strict": true,
|
||||
]
|
||||
}
|
||||
}
|
||||
/>
|
||||
onChange={[MockFunction]}
|
||||
schema={
|
||||
Object {
|
||||
"fields": Object {
|
||||
"monitor.host": Object {
|
||||
"type": "string",
|
||||
},
|
||||
"monitor.id": Object {
|
||||
"type": "string",
|
||||
},
|
||||
"monitor.ip": Object {
|
||||
"type": "string",
|
||||
},
|
||||
"monitor.scheme": Object {
|
||||
"type": "string",
|
||||
},
|
||||
"monitor.status": Object {
|
||||
"type": "string",
|
||||
},
|
||||
"url.port": Object {
|
||||
"type": "number",
|
||||
},
|
||||
},
|
||||
"strict": true,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`MonitorStatusBar component renders duration in ms, not us 1`] = `
|
||||
<div
|
||||
class="euiPanel euiPanel--paddingMedium"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div
|
||||
aria-label="Monitor status"
|
||||
class="euiHealth"
|
||||
style="line-height:inherit"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<svg
|
||||
class="euiIcon euiIcon--medium euiIcon--success"
|
||||
focusable="false"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<defs>
|
||||
<circle
|
||||
cx="8"
|
||||
cy="8"
|
||||
id="dot-a"
|
||||
r="4"
|
||||
/>
|
||||
</defs>
|
||||
<use
|
||||
href="#dot-a"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
Up
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<a
|
||||
aria-label="Monitor URL link"
|
||||
class="euiLink euiLink--primary"
|
||||
href="https://www.example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
https://www.example.com/
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Monitor duration in milliseconds"
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
1235ms
|
||||
</div>
|
||||
<div
|
||||
aria-label="Time since last check"
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
15 minutes ago
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import { renderWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { Ping } from '../../../../common/graphql/types';
|
||||
|
@ -15,7 +16,9 @@ describe('MonitorStatusBar component', () => {
|
|||
beforeEach(() => {
|
||||
monitorStatus = [
|
||||
{
|
||||
timestamp: '1554820772000',
|
||||
timestamp: moment(new Date())
|
||||
.subtract(15, 'm')
|
||||
.toString(),
|
||||
monitor: {
|
||||
duration: {
|
||||
us: 1234567,
|
||||
|
@ -33,6 +36,6 @@ describe('MonitorStatusBar component', () => {
|
|||
const component = renderWithIntl(
|
||||
<MonitorStatusBarComponent loading={false} data={{ monitorStatus }} monitorId="foo" />
|
||||
);
|
||||
expect(component.text()).toContain('1235ms');
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -193,6 +193,7 @@ describe('PingList component', () => {
|
|||
data={{ allPings }}
|
||||
onUpdateApp={jest.fn()}
|
||||
onSelectedStatusUpdate={jest.fn()}
|
||||
selectedOption="down"
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
|
|
|
@ -23,13 +23,17 @@ import { ErrorListItem, Ping } from '../../../common/graphql/types';
|
|||
import { UptimeGraphQLQueryProps, withUptimeGraphQL } from '../higher_order';
|
||||
import { errorListQuery } from '../../queries';
|
||||
|
||||
interface ErrorListProps {
|
||||
linkParameters?: string;
|
||||
}
|
||||
|
||||
interface ErrorListQueryResult {
|
||||
errorList?: ErrorListItem[];
|
||||
}
|
||||
|
||||
type Props = UptimeGraphQLQueryProps<ErrorListQueryResult>;
|
||||
type Props = UptimeGraphQLQueryProps<ErrorListQueryResult> & ErrorListProps;
|
||||
|
||||
export const ErrorListComponent = ({ data, loading }: Props) => (
|
||||
export const ErrorListComponent = ({ data, linkParameters, loading }: Props) => (
|
||||
<EuiPanel paddingSize="s">
|
||||
<EuiTitle size="xs">
|
||||
<h5>
|
||||
|
@ -70,7 +74,7 @@ export const ErrorListComponent = ({ data, loading }: Props) => (
|
|||
}),
|
||||
render: (id: string, { name }: ErrorListItem) => (
|
||||
<EuiLink>
|
||||
<Link to={`/monitor/${id}`}>{name || id}</Link>
|
||||
<Link to={`/monitor/${id}${linkParameters}`}>{name || id}</Link>
|
||||
</EuiLink>
|
||||
),
|
||||
width: '25%',
|
||||
|
@ -106,7 +110,7 @@ export const ErrorListComponent = ({ data, loading }: Props) => (
|
|||
</EuiPanel>
|
||||
);
|
||||
|
||||
export const ErrorList = withUptimeGraphQL<ErrorListQueryResult>(
|
||||
export const ErrorList = withUptimeGraphQL<ErrorListQueryResult, ErrorListProps>(
|
||||
ErrorListComponent,
|
||||
errorListQuery
|
||||
);
|
||||
|
|
|
@ -20,7 +20,7 @@ interface FilterBarQueryResult {
|
|||
}
|
||||
|
||||
interface FilterBarProps {
|
||||
currentQuery?: object;
|
||||
currentQuery?: string;
|
||||
updateQuery: UptimeSearchBarQueryChangeHandler;
|
||||
}
|
||||
|
||||
|
@ -125,14 +125,16 @@ export const FilterBarComponent = ({ currentQuery, data, updateQuery }: Props) =
|
|||
},
|
||||
];
|
||||
return (
|
||||
<EuiSearchBar
|
||||
box={{ incremental: false }}
|
||||
className="euiFlexGroup--gutterSmall"
|
||||
onChange={updateQuery}
|
||||
filters={filters}
|
||||
query={currentQuery}
|
||||
schema={filterBarSearchSchema}
|
||||
/>
|
||||
<div data-test-subj="xpack.uptime.filterBar">
|
||||
<EuiSearchBar
|
||||
box={{ incremental: false }}
|
||||
className="euiFlexGroup--gutterSmall"
|
||||
onChange={updateQuery}
|
||||
filters={filters}
|
||||
query={currentQuery}
|
||||
schema={filterBarSearchSchema}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ interface MonitorListQueryResult {
|
|||
|
||||
interface MonitorListProps {
|
||||
dangerColor: string;
|
||||
linkParameters?: string;
|
||||
}
|
||||
|
||||
type Props = UptimeGraphQLQueryProps<MonitorListQueryResult> & MonitorListProps;
|
||||
|
@ -52,7 +53,7 @@ const monitorListPagination = {
|
|||
pageSizeOptions: [5, 10, 20, 50],
|
||||
};
|
||||
|
||||
export const MonitorListComponent = ({ dangerColor, data, loading }: Props) => (
|
||||
export const MonitorListComponent = ({ dangerColor, data, linkParameters, loading }: Props) => (
|
||||
<EuiPanel paddingSize="s">
|
||||
<EuiTitle size="xs">
|
||||
<h5>
|
||||
|
@ -98,7 +99,10 @@ export const MonitorListComponent = ({ dangerColor, data, loading }: Props) => (
|
|||
}),
|
||||
render: (id: string, monitor: LatestMonitor) => (
|
||||
<EuiLink>
|
||||
<Link data-test-subj={`monitor-page-link-${id}`} to={`/monitor/${id}`}>
|
||||
<Link
|
||||
data-test-subj={`monitor-page-link-${id}`}
|
||||
to={`/monitor/${id}${linkParameters}`}
|
||||
>
|
||||
{monitor.ping && monitor.ping.monitor && monitor.ping.monitor.name
|
||||
? monitor.ping.monitor.name
|
||||
: id}
|
||||
|
|
|
@ -95,7 +95,7 @@ export const MonitorStatusBarComponent = ({ data, monitorId }: Props) => {
|
|||
)}
|
||||
grow={false}
|
||||
>
|
||||
{moment(parseInt(timestamp, 10)).fromNow()}
|
||||
{moment(new Date(timestamp).valueOf()).fromNow()}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
|
|
|
@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { get } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import React, { Fragment, useEffect, useState } from 'react';
|
||||
import React, { Fragment, useEffect } from 'react';
|
||||
import { Ping, PingResults } from '../../../common/graphql/types';
|
||||
import { convertMicrosecondsToMilliseconds as microsToMillis } from '../../lib/helper';
|
||||
import { UptimeGraphQLQueryProps, withUptimeGraphQL } from '../higher_order';
|
||||
|
@ -35,6 +35,7 @@ interface PingListQueryResult {
|
|||
interface PingListProps {
|
||||
onUpdateApp: () => void;
|
||||
onSelectedStatusUpdate: (status: string | null) => void;
|
||||
selectedOption: string;
|
||||
}
|
||||
|
||||
type Props = UptimeGraphQLQueryProps<PingListQueryResult> & PingListProps;
|
||||
|
@ -44,8 +45,9 @@ export const PingListComponent = ({
|
|||
loading,
|
||||
onSelectedStatusUpdate,
|
||||
onUpdateApp,
|
||||
selectedOption,
|
||||
}: Props) => {
|
||||
const [statusOptions] = useState<EuiComboBoxOptionProps[]>([
|
||||
const statusOptions: EuiComboBoxOptionProps[] = [
|
||||
{
|
||||
label: i18n.translate('xpack.uptime.pingList.statusOptions.allStatusOptionLabel', {
|
||||
defaultMessage: 'All',
|
||||
|
@ -64,8 +66,7 @@ export const PingListComponent = ({
|
|||
}),
|
||||
value: 'down',
|
||||
},
|
||||
]);
|
||||
const [selectedOption, setSelectedOption] = useState<EuiComboBoxOptionProps>(statusOptions[2]);
|
||||
];
|
||||
const columns = [
|
||||
{
|
||||
field: 'monitor.status',
|
||||
|
@ -195,18 +196,17 @@ export const PingListComponent = ({
|
|||
<EuiComboBox
|
||||
isClearable={false}
|
||||
singleSelection={{ asPlainText: true }}
|
||||
selectedOptions={[selectedOption || statusOptions[2]]}
|
||||
selectedOptions={[
|
||||
statusOptions.find(({ value }) => value === selectedOption) || statusOptions[2],
|
||||
]}
|
||||
options={statusOptions}
|
||||
aria-label={i18n.translate('xpack.uptime.pingList.statusLabel', {
|
||||
defaultMessage: 'Status',
|
||||
})}
|
||||
onChange={(selectedOptions: EuiComboBoxOptionProps[]) => {
|
||||
if (selectedOptions[0]) {
|
||||
setSelectedOption(selectedOptions[0]);
|
||||
}
|
||||
if (typeof selectedOptions[0].value === 'string') {
|
||||
// @ts-ignore it's definitely a string
|
||||
onSelectedStatusUpdate(
|
||||
// @ts-ignore it's definitely a string
|
||||
selectedOptions[0].value !== '' ? selectedOptions[0].value : null
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 { EuiSuperDatePicker } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { useUrlParams } from '../../hooks';
|
||||
|
||||
// TODO: when EUI exports types for this, this should be replaced
|
||||
interface SuperDateRangePickerRangeChangedEvent {
|
||||
start: string;
|
||||
end: string;
|
||||
}
|
||||
|
||||
interface SuperDateRangePickerRefreshChangedEvent {
|
||||
isPaused: boolean;
|
||||
refreshInterval?: number;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
history: any;
|
||||
location: any;
|
||||
refreshApp: () => void;
|
||||
}
|
||||
|
||||
type UptimeDatePickerProps = Props;
|
||||
|
||||
export const UptimeDatePicker = (props: UptimeDatePickerProps) => {
|
||||
const { history, location, refreshApp } = props;
|
||||
const [
|
||||
{ autorefreshInterval, autorefreshIsPaused, dateRangeStart, dateRangeEnd },
|
||||
updateUrl,
|
||||
] = useUrlParams(history, location);
|
||||
return (
|
||||
<EuiSuperDatePicker
|
||||
start={dateRangeStart}
|
||||
end={dateRangeEnd}
|
||||
isPaused={autorefreshIsPaused}
|
||||
refreshInterval={autorefreshInterval}
|
||||
onTimeChange={({ start, end }: SuperDateRangePickerRangeChangedEvent) => {
|
||||
updateUrl({ dateRangeStart: start, dateRangeEnd: end });
|
||||
refreshApp();
|
||||
}}
|
||||
// @ts-ignore onRefresh is not defined on EuiSuperDatePicker's type yet
|
||||
onRefresh={refreshApp}
|
||||
onRefreshChange={({ isPaused, refreshInterval }: SuperDateRangePickerRefreshChangedEvent) => {
|
||||
updateUrl({
|
||||
autorefreshInterval:
|
||||
refreshInterval === undefined ? autorefreshInterval : refreshInterval,
|
||||
autorefreshPaused: isPaused,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -36,21 +36,41 @@ export function withUptimeGraphQL<T, P = {}>(WrappedComponent: any, query: any)
|
|||
|
||||
return withApollo((props: Props) => {
|
||||
const { lastRefresh } = useContext(UptimeRefreshContext);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [data, setData] = useState<T | undefined>(undefined);
|
||||
const [errors, setErrors] = useState<GraphQLError[] | undefined>(undefined);
|
||||
let updateState = (
|
||||
loadingVal: boolean,
|
||||
dataVal: T | undefined,
|
||||
errorsVal: GraphQLError[] | undefined
|
||||
) => {
|
||||
setLoading(loadingVal);
|
||||
setData(dataVal);
|
||||
setErrors(errorsVal);
|
||||
};
|
||||
const { client, implementsCustomErrorState, variables } = props;
|
||||
const fetch = () => {
|
||||
setLoading(true);
|
||||
client.query<T>({ fetchPolicy: 'network-only', query, variables }).then((result: any) => {
|
||||
setData(result.data);
|
||||
setLoading(result.loading);
|
||||
setErrors(result.errors);
|
||||
updateState(result.loading, result.data, result.errors);
|
||||
});
|
||||
};
|
||||
useEffect(
|
||||
() => {
|
||||
fetch();
|
||||
|
||||
/**
|
||||
* If the `then` handler in `fetch`'s promise is fired after
|
||||
* this component has unmounted, it will try to set state on an
|
||||
* unmounted component, which indicates a memory leak and will trigger
|
||||
* React warnings.
|
||||
*
|
||||
* We counteract this side effect by providing a cleanup function that will
|
||||
* reassign the update function to do nothing with the returned values.
|
||||
*/
|
||||
return () => {
|
||||
updateState = () => {};
|
||||
};
|
||||
},
|
||||
[variables, lastRefresh]
|
||||
);
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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 { useUrlParams } from '../use_url_params';
|
||||
|
||||
describe('useUrlParams', () => {
|
||||
it('returns the expected params and an update function', () => {
|
||||
const history: any[] = [];
|
||||
const location = { pathname: '/', search: '_g=()' };
|
||||
const [params, updateFunction] = useUrlParams(history, location);
|
||||
expect(params).toEqual({
|
||||
autorefreshInterval: 60000,
|
||||
autorefreshIsPaused: false,
|
||||
dateRangeStart: 'now-15m',
|
||||
dateRangeEnd: 'now',
|
||||
search: '',
|
||||
selectedPingStatus: 'down',
|
||||
});
|
||||
expect(updateFunction).toBeInstanceOf(Function);
|
||||
});
|
||||
|
||||
it('returns an update URL function that pushes a new URL to the history object', () => {
|
||||
const history: any[] = [];
|
||||
const location = { pathname: '/', search: '_g=()' };
|
||||
const [, updateFunction] = useUrlParams(history, location);
|
||||
const nextPath = updateFunction({ search: 'monitor.id:foo status:down' });
|
||||
expect(nextPath).toEqual('/?_g=()&search=monitor.id%3Afoo%20status%3Adown');
|
||||
expect(history).toHaveLength(1);
|
||||
expect(history[0]).toEqual({
|
||||
pathname: '/',
|
||||
search: '_g=()&search=monitor.id%3Afoo%20status%3Adown',
|
||||
});
|
||||
});
|
||||
});
|
7
x-pack/plugins/uptime/public/hooks/index.ts
Normal file
7
x-pack/plugins/uptime/public/hooks/index.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { useUrlParams } from './use_url_params';
|
36
x-pack/plugins/uptime/public/hooks/use_url_params.ts
Normal file
36
x-pack/plugins/uptime/public/hooks/use_url_params.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 qs from 'querystring';
|
||||
import {
|
||||
UptimeUrlParams,
|
||||
getSupportedUrlParams,
|
||||
} from '../lib/helper/url_params/get_supported_url_params';
|
||||
|
||||
interface Location {
|
||||
pathname: string;
|
||||
search: string;
|
||||
}
|
||||
|
||||
export const useUrlParams = (
|
||||
history: any,
|
||||
location: Location
|
||||
): [UptimeUrlParams, (updatedParams: any) => string] => {
|
||||
const { pathname, search } = location;
|
||||
const currentParams: any = qs.parse(search[0] === '?' ? search.slice(1) : search);
|
||||
|
||||
const updateUrl = (updatedParams: any) => {
|
||||
const updatedSearch = qs.stringify({ ...currentParams, ...updatedParams });
|
||||
history.push({
|
||||
pathname,
|
||||
search: updatedSearch,
|
||||
});
|
||||
|
||||
return `${pathname}?${updatedSearch}`;
|
||||
};
|
||||
|
||||
return [getSupportedUrlParams(currentParams), updateUrl];
|
||||
};
|
|
@ -9,7 +9,6 @@ import { unmountComponentAtNode } from 'react-dom';
|
|||
import chrome from 'ui/chrome';
|
||||
import { PLUGIN } from '../../../../common/constants';
|
||||
import { UMBreadcrumb } from '../../../breadcrumbs';
|
||||
import { UptimePersistedState } from '../../../uptime_app';
|
||||
import { BootstrapUptimeApp, UMFrameworkAdapter } from '../../lib';
|
||||
import { CreateGraphQLClient } from './framework_adapter_types';
|
||||
import { renderUptimeKibanaGlobalHelp } from './kibana_global_help';
|
||||
|
@ -18,25 +17,11 @@ export class UMKibanaFrameworkAdapter implements UMFrameworkAdapter {
|
|||
private uiRoutes: any;
|
||||
private xsrfHeader: string;
|
||||
private uriPath: string;
|
||||
private defaultDateRangeStart: string;
|
||||
private defaultDateRangeEnd: string;
|
||||
private defaultAutorefreshInterval: number;
|
||||
private defaultAutorefreshIsPaused: boolean;
|
||||
|
||||
constructor(
|
||||
uiRoutes: any,
|
||||
dateRangeStart?: string,
|
||||
dateRangeEnd?: string,
|
||||
autorefreshInterval?: number,
|
||||
autorefreshIsPaused?: boolean
|
||||
) {
|
||||
constructor(uiRoutes: any) {
|
||||
this.uiRoutes = uiRoutes;
|
||||
this.xsrfHeader = chrome.getXsrfToken();
|
||||
this.uriPath = `${chrome.getBasePath()}/api/uptime/graphql`;
|
||||
this.defaultDateRangeStart = dateRangeStart || 'now-15m';
|
||||
this.defaultDateRangeEnd = dateRangeEnd || 'now';
|
||||
this.defaultAutorefreshInterval = autorefreshInterval || 60 * 1000;
|
||||
this.defaultAutorefreshIsPaused = autorefreshIsPaused || true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,9 +73,6 @@ export class UMKibanaFrameworkAdapter implements UMFrameworkAdapter {
|
|||
// determine whether dark mode is enabled
|
||||
const darkMode = config.get('theme:darkMode', false) || false;
|
||||
|
||||
// get current persisted state, if any
|
||||
const persistedState = this.initializePersistedState();
|
||||
|
||||
/**
|
||||
* We pass this global help setup as a prop to the app, because for
|
||||
* localization it's necessary to have the provider mounted before
|
||||
|
@ -103,13 +85,6 @@ export class UMKibanaFrameworkAdapter implements UMFrameworkAdapter {
|
|||
return () => ReactDOM.unmountComponentAtNode(element);
|
||||
});
|
||||
|
||||
const {
|
||||
autorefreshIsPaused,
|
||||
autorefreshInterval,
|
||||
dateRangeStart,
|
||||
dateRangeEnd,
|
||||
} = persistedState;
|
||||
|
||||
ReactDOM.render(
|
||||
renderComponent({
|
||||
basePath,
|
||||
|
@ -118,11 +93,6 @@ export class UMKibanaFrameworkAdapter implements UMFrameworkAdapter {
|
|||
kibanaBreadcrumbs,
|
||||
routerBasename,
|
||||
client: graphQLClient,
|
||||
initialAutorefreshIsPaused: autorefreshIsPaused,
|
||||
initialAutorefreshInterval: autorefreshInterval,
|
||||
initialDateRangeStart: dateRangeStart,
|
||||
initialDateRangeEnd: dateRangeEnd,
|
||||
persistState: this.updatePersistedState,
|
||||
renderGlobalHelpControls,
|
||||
}),
|
||||
elem
|
||||
|
@ -152,41 +122,4 @@ export class UMKibanaFrameworkAdapter implements UMFrameworkAdapter {
|
|||
unmountComponentAtNode(elem);
|
||||
});
|
||||
};
|
||||
|
||||
private initializePersistedState = (): UptimePersistedState => {
|
||||
const uptimeConfigurationData = window.localStorage.getItem(PLUGIN.LOCAL_STORAGE_KEY);
|
||||
const defaultState: UptimePersistedState = {
|
||||
autorefreshIsPaused: this.defaultAutorefreshIsPaused,
|
||||
autorefreshInterval: this.defaultAutorefreshInterval,
|
||||
dateRangeStart: this.defaultDateRangeStart,
|
||||
dateRangeEnd: this.defaultDateRangeEnd,
|
||||
};
|
||||
try {
|
||||
if (uptimeConfigurationData) {
|
||||
const parsed = JSON.parse(uptimeConfigurationData) || {};
|
||||
const { dateRangeStart, dateRangeEnd } = parsed;
|
||||
// TODO: this is defensive code to ensure we don't encounter problems
|
||||
// when encountering older versions of the localStorage values.
|
||||
// The old code has never been released, so users don't need it, and this
|
||||
// code should be removed eventually.
|
||||
if (
|
||||
(dateRangeEnd && typeof dateRangeEnd === 'number') ||
|
||||
(dateRangeStart && typeof dateRangeStart === 'number')
|
||||
) {
|
||||
this.updatePersistedState(defaultState);
|
||||
return defaultState;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
} catch (e) {
|
||||
// TODO: this should result in a redirect to error page
|
||||
throw e;
|
||||
}
|
||||
this.updatePersistedState(defaultState);
|
||||
return defaultState;
|
||||
};
|
||||
|
||||
private updatePersistedState = (state: UptimePersistedState) => {
|
||||
window.localStorage.setItem(PLUGIN.LOCAL_STORAGE_KEY, JSON.stringify(state));
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 { stringifyUrlParams } from '../stringify_url_params';
|
||||
|
||||
describe('stringifyUrlParams', () => {
|
||||
it('creates expected string value', () => {
|
||||
const result = stringifyUrlParams({
|
||||
autorefreshInterval: 50000,
|
||||
autorefreshIsPaused: false,
|
||||
dateRangeStart: 'now-15m',
|
||||
dateRangeEnd: 'now',
|
||||
search: 'monitor.id: foo',
|
||||
selectedPingStatus: 'down',
|
||||
});
|
||||
expect(result).toEqual(
|
||||
'?autorefreshInterval=50000&autorefreshIsPaused=false&dateRangeStart=now-15m&dateRangeEnd=now&search=monitor.id%3A%20foo&selectedPingStatus=down'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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 qs from 'querystring';
|
||||
import { UptimeUrlParams } from './url_params/get_supported_url_params';
|
||||
|
||||
export const stringifyUrlParams = (params: UptimeUrlParams) => `?${qs.stringify(params)}`;
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 { getSupportedUrlParams } from '../get_supported_url_params';
|
||||
import { CLIENT_DEFAULTS } from '../../../../../common/constants';
|
||||
|
||||
describe('getSupportedUrlParams', () => {
|
||||
it('returns custom values', () => {
|
||||
const customValues = {
|
||||
autorefreshInterval: '23',
|
||||
autorefreshIsPaused: 'false',
|
||||
dateRangeStart: 'foo',
|
||||
dateRangeEnd: 'bar',
|
||||
search: 'monitor.status: down',
|
||||
selectedPingStatus: 'up',
|
||||
};
|
||||
const result = getSupportedUrlParams(customValues);
|
||||
expect(result).toEqual({
|
||||
autorefreshInterval: 23,
|
||||
autorefreshIsPaused: false,
|
||||
dateRangeStart: 'foo',
|
||||
dateRangeEnd: 'bar',
|
||||
search: 'monitor.status: down',
|
||||
selectedPingStatus: 'up',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns default values', () => {
|
||||
const {
|
||||
AUTOREFRESH_INTERVAL,
|
||||
AUTOREFRESH_IS_PAUSED,
|
||||
DATE_RANGE_START,
|
||||
DATE_RANGE_END,
|
||||
SEARCH,
|
||||
SELECTED_PING_LIST_STATUS,
|
||||
} = CLIENT_DEFAULTS;
|
||||
const result = getSupportedUrlParams({});
|
||||
expect(result).toEqual({
|
||||
autorefreshInterval: AUTOREFRESH_INTERVAL,
|
||||
autorefreshIsPaused: AUTOREFRESH_IS_PAUSED,
|
||||
dateRangeStart: DATE_RANGE_START,
|
||||
dateRangeEnd: DATE_RANGE_END,
|
||||
search: SEARCH,
|
||||
selectedPingStatus: SELECTED_PING_LIST_STATUS,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 { parseAutorefreshInterval } from '../parse_autorefresh_interval';
|
||||
|
||||
describe('parseAutorefreshInterval', () => {
|
||||
it('parses a number', () => {
|
||||
const result = parseAutorefreshInterval('23', 50);
|
||||
expect(result).toBe(23);
|
||||
});
|
||||
|
||||
it('returns default value for empty string', () => {
|
||||
const result = parseAutorefreshInterval('', 50);
|
||||
expect(result).toBe(50);
|
||||
});
|
||||
|
||||
it('returns default value for non-numeric string', () => {
|
||||
const result = parseAutorefreshInterval('abc', 50);
|
||||
expect(result).toBe(50);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 { parseIsPaused } from '../parse_is_paused';
|
||||
|
||||
describe('parseIsPaused', () => {
|
||||
it('parses correct true isPaused value', () => {
|
||||
expect(parseIsPaused('true', false)).toEqual(true);
|
||||
});
|
||||
|
||||
it('parses correct false isPaused value', () => {
|
||||
expect(parseIsPaused('false', true)).toEqual(false);
|
||||
});
|
||||
|
||||
it('uses default value for non-boolean string', () => {
|
||||
expect(parseIsPaused('foo', true)).toEqual(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 { parseIsPaused } from './parse_is_paused';
|
||||
import { parseAutorefreshInterval } from './parse_autorefresh_interval';
|
||||
import { CLIENT_DEFAULTS } from '../../../../common/constants';
|
||||
|
||||
export interface UptimeUrlParams {
|
||||
autorefreshInterval: number;
|
||||
autorefreshIsPaused: boolean;
|
||||
dateRangeStart: string;
|
||||
dateRangeEnd: string;
|
||||
search: string;
|
||||
selectedPingStatus: string;
|
||||
}
|
||||
|
||||
const {
|
||||
AUTOREFRESH_INTERVAL,
|
||||
AUTOREFRESH_IS_PAUSED,
|
||||
DATE_RANGE_START,
|
||||
DATE_RANGE_END,
|
||||
SEARCH,
|
||||
SELECTED_PING_LIST_STATUS,
|
||||
} = CLIENT_DEFAULTS;
|
||||
|
||||
export const getSupportedUrlParams = (params: {
|
||||
[key: string]: string | undefined;
|
||||
}): UptimeUrlParams => {
|
||||
const {
|
||||
autorefreshInterval,
|
||||
autorefreshIsPaused,
|
||||
dateRangeStart,
|
||||
dateRangeEnd,
|
||||
search,
|
||||
selectedPingStatus,
|
||||
} = params;
|
||||
|
||||
return {
|
||||
autorefreshInterval: parseAutorefreshInterval(autorefreshInterval, AUTOREFRESH_INTERVAL),
|
||||
autorefreshIsPaused: parseIsPaused(autorefreshIsPaused, AUTOREFRESH_IS_PAUSED),
|
||||
dateRangeStart: dateRangeStart || DATE_RANGE_START,
|
||||
dateRangeEnd: dateRangeEnd || DATE_RANGE_END,
|
||||
search: search || SEARCH,
|
||||
selectedPingStatus:
|
||||
selectedPingStatus === undefined ? SELECTED_PING_LIST_STATUS : selectedPingStatus,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { getSupportedUrlParams } from './get_supported_url_params';
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// TODO: add a comment explaining the purpose of this function
|
||||
export const parseAutorefreshInterval = (
|
||||
value: string | undefined,
|
||||
defaultValue: number
|
||||
): number => {
|
||||
const parsed = parseInt(value || '', 10);
|
||||
return isNaN(parsed) ? defaultValue : parsed;
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// TODO: add a comment explaining the purpose of this function
|
||||
export const parseIsPaused = (value: string | undefined, defaultValue: boolean): boolean => {
|
||||
if (value === 'true') {
|
||||
return true;
|
||||
}
|
||||
if (value === 'false') {
|
||||
return false;
|
||||
}
|
||||
return defaultValue;
|
||||
};
|
|
@ -22,10 +22,12 @@ import {
|
|||
} from '../components/functional';
|
||||
import { UMUpdateBreadcrumbs } from '../lib/lib';
|
||||
import { UptimeSettingsContext } from '../contexts';
|
||||
import { useUrlParams } from '../hooks';
|
||||
import { stringifyUrlParams } from '../lib/helper/stringify_url_params';
|
||||
|
||||
interface MonitorPageProps {
|
||||
history: { push: any };
|
||||
location: { pathname: string };
|
||||
location: { pathname: string; search: string };
|
||||
match: { params: { id: string } };
|
||||
// this is the query function provided by Apollo's Client API
|
||||
query: <T, TVariables = OperationVariables>(
|
||||
|
@ -34,33 +36,36 @@ interface MonitorPageProps {
|
|||
setBreadcrumbs: UMUpdateBreadcrumbs;
|
||||
}
|
||||
|
||||
export const MonitorPage = ({ location, query, setBreadcrumbs }: MonitorPageProps) => {
|
||||
export const MonitorPage = ({ history, location, query, setBreadcrumbs }: MonitorPageProps) => {
|
||||
const [monitorId] = useState<string>(location.pathname.replace(/^(\/monitor\/)/, ''));
|
||||
const [selectedStatus, setSelectedStatus] = useState<string | null>('down');
|
||||
const { colors, dateRangeStart, dateRangeEnd, refreshApp, setHeadingText } = useContext(
|
||||
UptimeSettingsContext
|
||||
);
|
||||
useEffect(() => {
|
||||
query({
|
||||
query: gql`
|
||||
query MonitorPageTitle($monitorId: String!) {
|
||||
monitorPageTitle: getMonitorPageTitle(monitorId: $monitorId) {
|
||||
id
|
||||
url
|
||||
name
|
||||
const { colors, refreshApp, setHeadingText } = useContext(UptimeSettingsContext);
|
||||
const [params, updateUrlParams] = useUrlParams(history, location);
|
||||
const { dateRangeStart, dateRangeEnd, selectedPingStatus } = params;
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
query({
|
||||
query: gql`
|
||||
query MonitorPageTitle($monitorId: String!) {
|
||||
monitorPageTitle: getMonitorPageTitle(monitorId: $monitorId) {
|
||||
id
|
||||
url
|
||||
name
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { monitorId },
|
||||
}).then((result: any) => {
|
||||
const { name, url, id } = result.data.monitorPageTitle;
|
||||
const heading: string = name || url || id;
|
||||
setBreadcrumbs(getMonitorPageBreadcrumb(heading, stringifyUrlParams(params)));
|
||||
if (setHeadingText) {
|
||||
setHeadingText(heading);
|
||||
}
|
||||
`,
|
||||
variables: { monitorId },
|
||||
}).then((result: any) => {
|
||||
const { name, url, id } = result.data.monitorPageTitle;
|
||||
const heading: string = name || url || id;
|
||||
setBreadcrumbs(getMonitorPageBreadcrumb(heading));
|
||||
if (setHeadingText) {
|
||||
setHeadingText(heading);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
});
|
||||
},
|
||||
[params]
|
||||
);
|
||||
return (
|
||||
<Fragment>
|
||||
<MonitorPageTitle monitorId={monitorId} variables={{ monitorId }} />
|
||||
|
@ -73,13 +78,16 @@ export const MonitorPage = ({ location, query, setBreadcrumbs }: MonitorPageProp
|
|||
<MonitorCharts {...colors} variables={{ dateRangeStart, dateRangeEnd, monitorId }} />
|
||||
<EuiSpacer size="s" />
|
||||
<PingList
|
||||
onSelectedStatusUpdate={setSelectedStatus}
|
||||
onSelectedStatusUpdate={(selectedStatus: string | null) =>
|
||||
updateUrlParams({ selectedPingStatus: selectedStatus || '' })
|
||||
}
|
||||
onUpdateApp={refreshApp}
|
||||
selectedOption={selectedPingStatus}
|
||||
variables={{
|
||||
dateRangeStart,
|
||||
dateRangeEnd,
|
||||
monitorId,
|
||||
status: selectedStatus,
|
||||
status: selectedPingStatus,
|
||||
}}
|
||||
/>
|
||||
</Fragment>
|
||||
|
|
|
@ -7,14 +7,21 @@
|
|||
// @ts-ignore EuiSearchBar missing
|
||||
import { EuiSearchBar, EuiSpacer } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { Fragment, useContext, useEffect, useState } from 'react';
|
||||
import React, { Fragment, useContext, useEffect } from 'react';
|
||||
import { getOverviewPageBreadcrumbs } from '../breadcrumbs';
|
||||
import { EmptyState, ErrorList, FilterBar, MonitorList, Snapshot } from '../components/functional';
|
||||
import { UMUpdateBreadcrumbs } from '../lib/lib';
|
||||
import { UptimeSettingsContext } from '../contexts';
|
||||
import { useUrlParams } from '../hooks';
|
||||
import { stringifyUrlParams } from '../lib/helper/stringify_url_params';
|
||||
|
||||
interface OverviewPageProps {
|
||||
basePath: string;
|
||||
history: any;
|
||||
location: {
|
||||
pathname: string;
|
||||
search: string;
|
||||
};
|
||||
setBreadcrumbs: UMUpdateBreadcrumbs;
|
||||
}
|
||||
|
||||
|
@ -22,12 +29,10 @@ type Props = OverviewPageProps;
|
|||
|
||||
export type UptimeSearchBarQueryChangeHandler = ({ query }: { query?: { text: string } }) => void;
|
||||
|
||||
export const OverviewPage = ({ basePath, setBreadcrumbs }: Props) => {
|
||||
const { colors, dateRangeStart, dateRangeEnd, refreshApp, setHeadingText } = useContext(
|
||||
UptimeSettingsContext
|
||||
);
|
||||
const [currentFilterQueryObj, setFilterQueryObj] = useState<object | undefined>(undefined);
|
||||
const [currentFilterQuery, setCurrentFilterQuery] = useState<string | undefined>(undefined);
|
||||
export const OverviewPage = ({ basePath, setBreadcrumbs, history, location }: Props) => {
|
||||
const { colors, refreshApp, setHeadingText } = useContext(UptimeSettingsContext);
|
||||
const [params, updateUrl] = useUrlParams(history, location);
|
||||
const { dateRangeStart, dateRangeEnd, search } = params;
|
||||
|
||||
useEffect(() => {
|
||||
setBreadcrumbs(getOverviewPageBreadcrumbs());
|
||||
|
@ -41,39 +46,46 @@ export const OverviewPage = ({ basePath, setBreadcrumbs }: Props) => {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const sharedProps = { dateRangeStart, dateRangeEnd, currentFilterQuery };
|
||||
const filterQueryString = search || '';
|
||||
const sharedProps = {
|
||||
dateRangeStart,
|
||||
dateRangeEnd,
|
||||
filters: search ? JSON.stringify(EuiSearchBar.Query.toESQuery(filterQueryString)) : undefined,
|
||||
};
|
||||
|
||||
const updateQuery: UptimeSearchBarQueryChangeHandler = ({ query }) => {
|
||||
try {
|
||||
let esQuery;
|
||||
if (query && query.text) {
|
||||
esQuery = EuiSearchBar.Query.toESQuery(query);
|
||||
if (query && typeof query.text !== 'undefined') {
|
||||
updateUrl({ search: query.text });
|
||||
}
|
||||
setFilterQueryObj(query);
|
||||
setCurrentFilterQuery(esQuery ? JSON.stringify(esQuery) : esQuery);
|
||||
if (refreshApp) {
|
||||
refreshApp();
|
||||
}
|
||||
} catch (e) {
|
||||
setFilterQueryObj(undefined);
|
||||
setCurrentFilterQuery(undefined);
|
||||
updateUrl({ search: '' });
|
||||
}
|
||||
};
|
||||
|
||||
const linkParameters = stringifyUrlParams(params);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EmptyState basePath={basePath} implementsCustomErrorState={true} variables={sharedProps}>
|
||||
<FilterBar
|
||||
currentQuery={currentFilterQueryObj}
|
||||
currentQuery={filterQueryString}
|
||||
updateQuery={updateQuery}
|
||||
variables={sharedProps}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<Snapshot colors={colors} variables={sharedProps} />
|
||||
<EuiSpacer size="s" />
|
||||
<MonitorList dangerColor={colors.danger} variables={sharedProps} />
|
||||
<MonitorList
|
||||
dangerColor={colors.danger}
|
||||
linkParameters={linkParameters}
|
||||
variables={sharedProps}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<ErrorList variables={sharedProps} />
|
||||
<ErrorList linkParameters={linkParameters} variables={sharedProps} />
|
||||
</EmptyState>
|
||||
</Fragment>
|
||||
);
|
||||
|
|
|
@ -24,12 +24,14 @@ import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
|
|||
import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ApolloProvider } from 'react-apollo';
|
||||
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
|
||||
import { BrowserRouter as Router, Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { overviewBreadcrumb, UMBreadcrumb } from './breadcrumbs';
|
||||
import { UMBreadcrumb } from './breadcrumbs';
|
||||
import { UMGraphQLClient, UMUpdateBreadcrumbs } from './lib/lib';
|
||||
import { MonitorPage, OverviewPage } from './pages';
|
||||
import { UptimeRefreshContext, UptimeSettingsContext } from './contexts';
|
||||
import { UptimeDatePicker } from './components/functional/uptime_date_picker';
|
||||
import { useUrlParams } from './hooks';
|
||||
|
||||
export interface UptimeAppColors {
|
||||
danger: string;
|
||||
|
@ -38,49 +40,21 @@ export interface UptimeAppColors {
|
|||
mean: string;
|
||||
}
|
||||
|
||||
export interface UptimePersistedState {
|
||||
autorefreshIsPaused: boolean;
|
||||
autorefreshInterval: number;
|
||||
dateRangeStart: string;
|
||||
dateRangeEnd: string;
|
||||
}
|
||||
|
||||
export interface UptimeAppProps {
|
||||
basePath: string;
|
||||
darkMode: boolean;
|
||||
client: UMGraphQLClient;
|
||||
initialDateRangeStart: string;
|
||||
initialDateRangeEnd: string;
|
||||
initialAutorefreshInterval: number;
|
||||
initialAutorefreshIsPaused: boolean;
|
||||
kibanaBreadcrumbs: UMBreadcrumb[];
|
||||
routerBasename: string;
|
||||
setBreadcrumbs: UMUpdateBreadcrumbs;
|
||||
persistState(state: UptimePersistedState): void;
|
||||
renderGlobalHelpControls(): void;
|
||||
}
|
||||
|
||||
// TODO: when EUI exports types for this, this should be replaced
|
||||
interface SuperDateRangePickerRangeChangedEvent {
|
||||
start: string;
|
||||
end: string;
|
||||
}
|
||||
|
||||
interface SuperDateRangePickerRefreshChangedEvent {
|
||||
isPaused: boolean;
|
||||
refreshInterval?: number;
|
||||
}
|
||||
|
||||
const Application = (props: UptimeAppProps) => {
|
||||
const {
|
||||
basePath,
|
||||
client,
|
||||
darkMode,
|
||||
initialAutorefreshIsPaused,
|
||||
initialAutorefreshInterval,
|
||||
initialDateRangeStart,
|
||||
initialDateRangeEnd,
|
||||
persistState,
|
||||
renderGlobalHelpControls,
|
||||
routerBasename,
|
||||
setBreadcrumbs,
|
||||
|
@ -103,19 +77,10 @@ const Application = (props: UptimeAppProps) => {
|
|||
};
|
||||
}
|
||||
|
||||
const [autorefreshIsPaused, setAutorefreshIsPaused] = useState<boolean>(
|
||||
initialAutorefreshIsPaused
|
||||
);
|
||||
const [autorefreshInterval, setAutorefreshInterval] = useState<number>(
|
||||
initialAutorefreshInterval
|
||||
);
|
||||
const [dateRangeStart, setDateRangeStart] = useState<string>(initialDateRangeStart);
|
||||
const [dateRangeEnd, setDateRangeEnd] = useState<string>(initialDateRangeEnd);
|
||||
const [lastRefresh, setLastRefresh] = useState<number>(Date.now());
|
||||
const [headingText, setHeadingText] = useState<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
setBreadcrumbs([overviewBreadcrumb]);
|
||||
renderGlobalHelpControls();
|
||||
}, []);
|
||||
|
||||
|
@ -126,97 +91,75 @@ const Application = (props: UptimeAppProps) => {
|
|||
return (
|
||||
<I18nContext>
|
||||
<Router basename={routerBasename}>
|
||||
<ApolloProvider client={client}>
|
||||
<UptimeSettingsContext.Provider
|
||||
value={{
|
||||
autorefreshInterval,
|
||||
autorefreshIsPaused,
|
||||
basePath,
|
||||
dateRangeStart,
|
||||
dateRangeEnd,
|
||||
colors,
|
||||
refreshApp,
|
||||
setHeadingText,
|
||||
}}
|
||||
>
|
||||
<UptimeRefreshContext.Provider value={{ lastRefresh }}>
|
||||
<EuiPage className="app-wrapper-panel " data-test-subj="uptimeApp">
|
||||
<div>
|
||||
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle>
|
||||
<h2>{headingText}</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
{
|
||||
// @ts-ignore onRefresh is not defined on EuiSuperDatePicker's type yet
|
||||
<EuiSuperDatePicker
|
||||
start={dateRangeStart}
|
||||
end={dateRangeEnd}
|
||||
isPaused={autorefreshIsPaused}
|
||||
refreshInterval={autorefreshInterval}
|
||||
onTimeChange={({ start, end }: SuperDateRangePickerRangeChangedEvent) => {
|
||||
setDateRangeStart(start);
|
||||
setDateRangeEnd(end);
|
||||
persistState({
|
||||
autorefreshInterval,
|
||||
autorefreshIsPaused,
|
||||
dateRangeStart,
|
||||
dateRangeEnd,
|
||||
});
|
||||
refreshApp();
|
||||
}}
|
||||
// @ts-ignore onRefresh is not defined on EuiSuperDatePicker's type yet
|
||||
onRefresh={refreshApp}
|
||||
onRefreshChange={({
|
||||
isPaused,
|
||||
refreshInterval,
|
||||
}: SuperDateRangePickerRefreshChangedEvent) => {
|
||||
setAutorefreshInterval(
|
||||
refreshInterval === undefined ? autorefreshInterval : refreshInterval
|
||||
);
|
||||
setAutorefreshIsPaused(isPaused);
|
||||
persistState({
|
||||
autorefreshInterval,
|
||||
autorefreshIsPaused,
|
||||
dateRangeStart,
|
||||
dateRangeEnd,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path="/"
|
||||
render={routerProps => (
|
||||
<OverviewPage
|
||||
basePath={basePath}
|
||||
setBreadcrumbs={setBreadcrumbs}
|
||||
{...routerProps}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path="/monitor/:id"
|
||||
render={routerProps => (
|
||||
<MonitorPage
|
||||
query={client.query}
|
||||
setBreadcrumbs={setBreadcrumbs}
|
||||
{...routerProps}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
</EuiPage>
|
||||
</UptimeRefreshContext.Provider>
|
||||
</UptimeSettingsContext.Provider>
|
||||
</ApolloProvider>
|
||||
<Route
|
||||
path="/"
|
||||
render={(rootRouteProps: RouteComponentProps) => {
|
||||
const [
|
||||
{ autorefreshInterval, autorefreshIsPaused, dateRangeStart, dateRangeEnd },
|
||||
] = useUrlParams(rootRouteProps.history, rootRouteProps.location);
|
||||
return (
|
||||
<ApolloProvider client={client}>
|
||||
<UptimeSettingsContext.Provider
|
||||
value={{
|
||||
autorefreshInterval,
|
||||
autorefreshIsPaused,
|
||||
basePath,
|
||||
dateRangeStart,
|
||||
dateRangeEnd,
|
||||
colors,
|
||||
refreshApp,
|
||||
setHeadingText,
|
||||
}}
|
||||
>
|
||||
<UptimeRefreshContext.Provider value={{ lastRefresh }}>
|
||||
<EuiPage className="app-wrapper-panel " data-test-subj="uptimeApp">
|
||||
<div>
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
justifyContent="spaceBetween"
|
||||
gutterSize="s"
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle>
|
||||
<h2>{headingText}</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<UptimeDatePicker refreshApp={refreshApp} {...rootRouteProps} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path="/"
|
||||
render={routerProps => (
|
||||
<OverviewPage
|
||||
basePath={basePath}
|
||||
setBreadcrumbs={setBreadcrumbs}
|
||||
{...routerProps}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path="/monitor/:id"
|
||||
render={routerProps => (
|
||||
<MonitorPage
|
||||
query={client.query}
|
||||
setBreadcrumbs={setBreadcrumbs}
|
||||
{...routerProps}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
</EuiPage>
|
||||
</UptimeRefreshContext.Provider>
|
||||
</UptimeSettingsContext.Provider>
|
||||
</ApolloProvider>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Router>
|
||||
</I18nContext>
|
||||
);
|
||||
|
|
|
@ -11,12 +11,22 @@ export default ({ getPageObjects }: KibanaFunctionalTestDefaultProviders) => {
|
|||
// TODO: add UI functional tests
|
||||
const pageObjects = getPageObjects(['uptime']);
|
||||
describe('overview page', () => {
|
||||
const DEFAULT_DATE_START = '2019-01-28 12:40:08.078';
|
||||
const DEFAULT_DATE_END = '2019-01-29 12:40:08.078';
|
||||
it('loads and displays uptime data based on date range', async () => {
|
||||
await pageObjects.uptime.goToUptimeOverviewAndLoadData(
|
||||
'2019-01-28 12:40:08.078',
|
||||
'2019-01-29 12:40:08.078',
|
||||
DEFAULT_DATE_START,
|
||||
DEFAULT_DATE_END,
|
||||
'monitor-page-link-auto-http-0X131221E73F825974'
|
||||
);
|
||||
});
|
||||
|
||||
it('runs filter query without issues', async () => {
|
||||
await pageObjects.uptime.inputFilterQuery(
|
||||
DEFAULT_DATE_START,
|
||||
DEFAULT_DATE_END,
|
||||
'monitor.status:up monitor.id:auto-http-0X131221E73F825974'
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -37,5 +37,16 @@ export const UptimePageProvider = ({
|
|||
throw new Error('Expected monitor name not found');
|
||||
}
|
||||
}
|
||||
|
||||
public async inputFilterQuery(
|
||||
datePickerStartValue: string,
|
||||
datePickerEndValue: string,
|
||||
filterQuery: string
|
||||
) {
|
||||
await pageObjects.common.navigateToApp('uptime');
|
||||
await pageObjects.timePicker.setAbsoluteRange(datePickerStartValue, datePickerEndValue);
|
||||
await uptimeService.setFilterText(filterQuery);
|
||||
await uptimeService.monitorIdExists('monitor-page-link-auto-http-0X131221E73F825974');
|
||||
}
|
||||
}();
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ import { KibanaFunctionalTestDefaultProviders } from '../../types/providers';
|
|||
|
||||
export const UptimeProvider = ({ getService }: KibanaFunctionalTestDefaultProviders) => {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const browser = getService('browser');
|
||||
return {
|
||||
async assertExists(key: string) {
|
||||
if (!(await testSubjects.exists(key))) {
|
||||
|
@ -23,5 +24,10 @@ export const UptimeProvider = ({ getService }: KibanaFunctionalTestDefaultProvid
|
|||
async getMonitorNameDisplayedOnPageTitle() {
|
||||
return await testSubjects.getVisibleText('monitor-page-title');
|
||||
},
|
||||
async setFilterText(filterQuery: string) {
|
||||
await testSubjects.click('xpack.uptime.filterBar');
|
||||
await testSubjects.setValue('xpack.uptime.filterBar', filterQuery);
|
||||
await browser.pressKeys(browser.keys.ENTER);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue