diff --git a/x-pack/legacy/plugins/uptime/common/constants/client_defaults.ts b/x-pack/legacy/plugins/uptime/common/constants/client_defaults.ts index 66ac571e2b7a5..e991e0c6b82e1 100644 --- a/x-pack/legacy/plugins/uptime/common/constants/client_defaults.ts +++ b/x-pack/legacy/plugins/uptime/common/constants/client_defaults.ts @@ -18,11 +18,6 @@ export const CLIENT_DEFAULTS = { { start: 'now/M', end: 'now', label: 'Month to date' }, { start: 'now/y', end: 'now', label: 'Year to date' }, ], - /** - * Designate how many checks a monitor summary can have - * before condensing them. - */ - CONDENSED_CHECK_LIMIT: 12, DATE_RANGE_START: 'now-15m', DATE_RANGE_END: 'now', FILTERS: '', diff --git a/x-pack/legacy/plugins/uptime/public/state/sagas/index.ts b/x-pack/legacy/plugins/uptime/common/runtime_types/index.ts similarity index 69% rename from x-pack/legacy/plugins/uptime/public/state/sagas/index.ts rename to x-pack/legacy/plugins/uptime/common/runtime_types/index.ts index 7bb3c694f5120..3a5d0549c5d45 100644 --- a/x-pack/legacy/plugins/uptime/public/state/sagas/index.ts +++ b/x-pack/legacy/plugins/uptime/common/runtime_types/index.ts @@ -4,8 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -// import { fork } from 'redux-saga/effects'; - -// export function* rootSaga() { -// yield fork(); -// } +export * from './monitor/monitor_details'; diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/monitor/monitor_details.ts b/x-pack/legacy/plugins/uptime/common/runtime_types/monitor/monitor_details.ts new file mode 100644 index 0000000000000..246b9c22a08d7 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/common/runtime_types/monitor/monitor_details.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import * as t from 'io-ts'; + +// IO type for validation +export const ErrorType = t.partial({ + code: t.number, + message: t.string, + type: t.string, +}); + +// Typescript type for type checking +export type Error = t.TypeOf; + +export const MonitorDetailsType = t.intersection([ + t.type({ monitorId: t.string }), + t.partial({ error: ErrorType }), +]); +export type MonitorDetails = t.TypeOf; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_popover.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_popover.tsx index 4f12af75815e8..6e73090782b04 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_popover.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_popover.tsx @@ -9,7 +9,7 @@ import React, { useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { UptimeFilterButton } from './uptime_filter_button'; import { toggleSelectedItems } from './toggle_selected_item'; -import { LocationLink } from '../monitor_list/location_link'; +import { LocationLink } from '../monitor_list'; export interface FilterPopoverProps { fieldName: string; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/index.ts b/x-pack/legacy/plugins/uptime/public/components/functional/index.ts index bb8a1a3a2474a..42316fca99f34 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/functional/index.ts @@ -12,7 +12,6 @@ export { IntegrationLink } from './integration_link'; export { KueryBar } from './kuery_bar'; export { MonitorCharts } from './monitor_charts'; export { MonitorList } from './monitor_list'; -export { MonitorPageLink } from './monitor_page_link'; export { MonitorPageTitle } from './monitor_page_title'; export { MonitorStatusBar } from './monitor_status_bar'; export { OverviewPageParsingErrorCallout } from './overview_page_parsing_error_callout'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/check_list.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/check_list.test.tsx.snap deleted file mode 100644 index 7aa77c7daf60f..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/check_list.test.tsx.snap +++ /dev/null @@ -1,64 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CheckList component renders a list of checks 1`] = ` - - - - - - - - - - 127.0.0.1 - - - - - - - - - - - 127.0.0.2 - - - -`; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/condensed_check_list.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/condensed_check_list.test.tsx.snap deleted file mode 100644 index 58b0887c29b32..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/condensed_check_list.test.tsx.snap +++ /dev/null @@ -1,253 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CondensedCheckList component renders checks 1`] = ` - - - - - - - - - - - - - - - - - - 127.0.0.1 - - - a few moments ago - - , - - - - - - 127.0.0.2 - - - a few moments ago - - , - ] - } - delay="regular" - position="right" - title="Check statuses" - > - - 2 checks - - - - - - - - - - - - - - - - - - - - 127.0.0.1 - - - a few moments ago - - , - - - - - - 127.0.0.2 - - - a few moments ago - - , - ] - } - delay="regular" - position="right" - title="Check statuses" - > - - 2 checks - - - - -`; - -exports[`CondensedCheckList component renders null in place of child status with missing ip 1`] = ` - - - - - - - - - - - - - - - - - - 127.0.0.2 - - - a few moments ago - - , - ] - } - delay="regular" - position="right" - title="Check statuses" - > - - 2 checks - - - - - - - - - - - - - - - - - - - - 127.0.0.1 - - - a few moments ago - - , - - - - - - 127.0.0.2 - - - a few moments ago - - , - ] - } - delay="regular" - position="right" - title="Check statuses" - > - - 2 checks - - - - -`; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap deleted file mode 100644 index 3b0f76dfb1088..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap +++ /dev/null @@ -1,50 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`MonitorListDrawer component renders a Checklist when there is only one check 1`] = ` - -`; - -exports[`MonitorListDrawer component renders a CondensedCheckList when there are many checks 1`] = ` - -`; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap new file mode 100644 index 0000000000000..e52977749142d --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MonitorPageLink component renders a help link when link parameters present 1`] = ` + + + +`; + +exports[`MonitorPageLink component renders the link properly 1`] = ` + + + +`; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/to_condensed_check.test.ts.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/to_condensed_check.test.ts.snap deleted file mode 100644 index 44c284d551841..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/to_condensed_check.test.ts.snap +++ /dev/null @@ -1,104 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`toCondensedCheck condenses checks across location 1`] = ` -Array [ - Object { - "childStatuses": Array [ - Object { - "ip": "192.178.123.21", - "status": "up", - "timestamp": "123", - }, - Object { - "ip": "192.178.123.22", - "status": "down", - "timestamp": "124", - }, - Object { - "ip": "192.178.123.23", - "status": "up", - "timestamp": "113", - }, - ], - "location": "us-east-1", - "status": "mixed", - "timestamp": "124", - }, -] -`; - -exports[`toCondensedCheck creates the correct number of condensed checks for multiple locations 1`] = ` -Array [ - Object { - "childStatuses": Array [ - Object { - "ip": "192.178.123.21", - "status": "up", - "timestamp": "123", - }, - Object { - "ip": "192.178.123.22", - "status": "down", - "timestamp": "124", - }, - Object { - "ip": "192.178.123.23", - "status": "up", - "timestamp": "113", - }, - ], - "location": "us-east-1", - "status": "mixed", - "timestamp": "124", - }, - Object { - "childStatuses": Array [ - Object { - "ip": "192.178.123.21", - "status": "up", - "timestamp": "121", - }, - Object { - "ip": "192.178.123.22", - "status": "down", - "timestamp": "132", - }, - Object { - "ip": "192.178.123.23", - "status": "up", - "timestamp": "115", - }, - ], - "location": "us-west-1", - "status": "mixed", - "timestamp": "132", - }, -] -`; - -exports[`toCondensedCheck infers an "up" status for a series of "up" checks 1`] = ` -Array [ - Object { - "childStatuses": Array [ - Object { - "ip": "192.178.123.21", - "status": "up", - "timestamp": "123", - }, - Object { - "ip": "192.178.123.22", - "status": "up", - "timestamp": "124", - }, - Object { - "ip": "192.178.123.23", - "status": "up", - "timestamp": "113", - }, - ], - "location": "us-east-1", - "status": "up", - "timestamp": "124", - }, -] -`; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/check_list.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/check_list.test.tsx deleted file mode 100644 index bc0e1f11002fd..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/check_list.test.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import moment from 'moment'; -import React from 'react'; -import { Check } from '../../../../../common/graphql/types'; -import { CheckList } from '../check_list'; - -describe('CheckList component', () => { - let checks: Check[]; - - beforeAll(() => { - moment.prototype.toLocaleString = jest.fn(() => '2019-06-21 15:29:26'); - moment.prototype.from = jest.fn(() => 'a few moments ago'); - }); - - beforeEach(() => { - checks = [ - { - monitor: { - ip: '127.0.0.1', - status: 'up', - }, - timestamp: '123', - }, - { - monitor: { - ip: '127.0.0.2', - status: 'up', - }, - observer: { - geo: { - name: 'us-east-1', - }, - }, - timestamp: 'up', - }, - ]; - }); - - it('renders a list of checks', () => { - const component = shallowWithIntl(); - expect(component).toMatchSnapshot(); - }); -}); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/condensed_check_list.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/condensed_check_list.test.tsx deleted file mode 100644 index 378167b6e5b05..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/condensed_check_list.test.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import React from 'react'; -import moment from 'moment'; -import { CondensedCheck } from '../types'; -import { CondensedCheckList } from '../condensed_check_list'; - -describe('CondensedCheckList component', () => { - let checks: CondensedCheck[]; - - beforeAll(() => { - moment.prototype.toLocaleString = jest.fn(() => '2019-06-21 15:29:26'); - moment.prototype.from = jest.fn(() => 'a few moments ago'); - }); - - beforeEach(() => { - checks = [ - { - childStatuses: [ - { - ip: '127.0.0.1', - status: 'up', - timestamp: '123', - }, - { - ip: '127.0.0.2', - status: 'down', - timestamp: '122', - }, - ], - location: 'us-east-1', - status: 'mixed', - timestamp: '123', - }, - { - childStatuses: [ - { - ip: '127.0.0.1', - status: 'up', - timestamp: '120', - }, - { - ip: '127.0.0.2', - status: 'up', - timestamp: '121', - }, - ], - location: 'us-west-1', - status: 'up', - timestamp: '125', - }, - ]; - }); - - it('renders checks', () => { - const component = shallowWithIntl( - - ); - expect(component).toMatchSnapshot(); - }); - - it('renders null in place of child status with missing ip', () => { - checks[0].childStatuses[0].ip = undefined; - const component = shallowWithIntl( - - ); - expect(component).toMatchSnapshot(); - }); -}); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_page_link.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_page_link.test.tsx new file mode 100644 index 0000000000000..dd6e9c66d395b --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_page_link.test.tsx @@ -0,0 +1,26 @@ +/* + * 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 { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import { MonitorPageLink } from '../monitor_page_link'; + +describe('MonitorPageLink component', () => { + it('renders the link properly', () => { + const component = shallowWithIntl( + + ); + expect(component).toMatchSnapshot(); + }); + + it('renders a help link when link parameters present', () => { + const linkParameters = 'selectedPingStatus=down'; + const component = shallowWithIntl( + + ); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/to_condensed_check.test.ts b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/to_condensed_check.test.ts deleted file mode 100644 index 6b8a2fd3c0e78..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/to_condensed_check.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Check } from '../../../../../common/graphql/types'; -import { toCondensedCheck } from '../to_condensed_check'; - -describe('toCondensedCheck', () => { - let checks: Check[]; - beforeEach(() => { - checks = [ - { - observer: { - geo: { - name: 'us-east-1', - }, - }, - timestamp: '123', - monitor: { - ip: '192.178.123.21', - status: 'up', - }, - }, - { - observer: { - geo: { - name: 'us-east-1', - }, - }, - timestamp: '124', - monitor: { - ip: '192.178.123.22', - status: 'down', - }, - }, - { - observer: { - geo: { - name: 'us-east-1', - }, - }, - timestamp: '113', - monitor: { - ip: '192.178.123.23', - status: 'up', - }, - }, - ]; - }); - - it('condenses checks across location', () => { - expect(toCondensedCheck(checks)).toMatchSnapshot(); - }); - - it('infers an "up" status for a series of "up" checks', () => { - checks = [ - { - observer: { - geo: { - name: 'us-east-1', - }, - }, - timestamp: '123', - monitor: { - ip: '192.178.123.21', - status: 'up', - }, - }, - { - observer: { - geo: { - name: 'us-east-1', - }, - }, - timestamp: '124', - monitor: { - ip: '192.178.123.22', - status: 'up', - }, - }, - { - observer: { - geo: { - name: 'us-east-1', - }, - }, - timestamp: '113', - monitor: { - ip: '192.178.123.23', - status: 'up', - }, - }, - ]; - const result = toCondensedCheck(checks); - expect(result).toMatchSnapshot(); - }); - - it('creates the correct number of condensed checks for multiple locations', () => { - checks = [ - { - observer: { - geo: { - name: 'us-east-1', - }, - }, - timestamp: '123', - monitor: { - ip: '192.178.123.21', - status: 'up', - }, - }, - { - observer: { - geo: { - name: 'us-east-1', - }, - }, - timestamp: '124', - monitor: { - ip: '192.178.123.22', - status: 'down', - }, - }, - { - observer: { - geo: { - name: 'us-east-1', - }, - }, - timestamp: '113', - monitor: { - ip: '192.178.123.23', - status: 'up', - }, - }, - { - observer: { - geo: { - name: 'us-west-1', - }, - }, - timestamp: '121', - monitor: { - ip: '192.178.123.21', - status: 'up', - }, - }, - { - observer: { - geo: { - name: 'us-west-1', - }, - }, - timestamp: '132', - monitor: { - ip: '192.178.123.22', - status: 'down', - }, - }, - { - observer: { - geo: { - name: 'us-west-1', - }, - }, - timestamp: '115', - monitor: { - ip: '192.178.123.23', - status: 'up', - }, - }, - ]; - const result = toCondensedCheck(checks); - expect(result).toMatchSnapshot(); - }); -}); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/check_list.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/check_list.tsx deleted file mode 100644 index 8beb3f7fa5a37..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/check_list.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Fragment } from 'react'; -import { EuiFlexGrid, EuiFlexItem, EuiText } from '@elastic/eui'; -import { get } from 'lodash'; -import { MonitorListStatusColumn } from './monitor_list_status_column'; -import { Check } from '../../../../common/graphql/types'; -import { LocationLink } from './location_link'; - -interface CheckListProps { - checks: Check[]; -} - -export const CheckList = ({ checks }: CheckListProps) => { - return ( - - {checks.map(check => { - const location = get(check, 'observer.geo.name', null); - const agentId = get(check, 'agent.id', 'null'); - const key = location + agentId + check.monitor.ip; - return ( - - - - - - - - - - {check.monitor.ip} - - - - ); - })} - - ); -}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/condensed_check_list.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/condensed_check_list.tsx deleted file mode 100644 index 7ac4b6e82602f..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/condensed_check_list.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - EuiFlexGrid, - EuiFlexItem, - EuiHealth, - EuiToolTip, - EuiBadge, - EuiFlexGroup, -} from '@elastic/eui'; -import moment from 'moment'; -import React from 'react'; -import { CondensedCheck, CondensedCheckStatus } from './types'; -import { MonitorListStatusColumn } from './monitor_list_status_column'; -import { LocationLink } from './location_link'; - -const getBadgeColor = (status: string, successColor: string, dangerColor: string) => { - switch (status) { - case 'up': - return successColor; - case 'down': - return dangerColor; - case 'mixed': - return 'secondary'; - default: - return undefined; - } -}; - -const getHealthColor = (dangerColor: string, status: string, successColor: string) => { - switch (status) { - case 'up': - return successColor; - case 'down': - return dangerColor; - default: - return 'primary'; - } -}; - -interface CondensedCheckListProps { - condensedChecks: CondensedCheck[]; - successColor: string; - dangerColor: string; -} - -export const CondensedCheckList = ({ - condensedChecks, - dangerColor, - successColor, -}: CondensedCheckListProps) => ( - - {condensedChecks.map(({ childStatuses, location, status, timestamp }: CondensedCheck) => ( - - - - - - - - - - - - - - ip ? ( - - - - - {ip} - {moment(parseInt(condensedTimestamp, 10)).fromNow()} - - ) : null - )} - > - {`${childStatuses.length} checks`} - - - - ))} - -); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/index.ts b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/index.ts index 717e66e4dbe1a..a83330a7a3a0b 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/index.ts @@ -6,3 +6,4 @@ export { MonitorList } from './monitor_list'; export { Criteria, Pagination } from './types'; +export { LocationLink } from './monitor_list_drawer'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx index 5774453ff67ab..274a7ab0be9be 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx @@ -30,9 +30,8 @@ import { MonitorListStatusColumn } from './monitor_list_status_column'; import { formatUptimeGraphQLErrorList } from '../../../lib/helper/format_error_list'; import { ExpandedRowMap } from './types'; import { MonitorListDrawer } from './monitor_list_drawer'; -import { CLIENT_DEFAULTS } from '../../../../common/constants'; import { MonitorBarSeries } from '../charts'; -import { MonitorPageLink } from '../monitor_page_link'; +import { MonitorPageLink } from './monitor_page_link'; import { MonitorListActionsPopover } from './monitor_list_actions_popover'; import { OverviewPageLink } from '../overview_page_link'; @@ -56,7 +55,6 @@ export const MonitorListComponent = (props: Props) => { absoluteStartDate, absoluteEndDate, dangerColor, - successColor, data, errors, hasActiveFilters, @@ -69,6 +67,19 @@ export const MonitorListComponent = (props: Props) => { const nextPagePagination = get(data, 'monitorStates.nextPagePagination'); const prevPagePagination = get(data, 'monitorStates.prevPagePagination'); + const getExpandedRowMap = () => { + return drawerIds.reduce((map: ExpandedRowMap, id: string) => { + return { + ...map, + [id]: ( + monitorId === id)} + /> + ), + }; + }, {}); + }; + return ( @@ -95,21 +106,7 @@ export const MonitorListComponent = (props: Props) => { isExpandable={true} hasActions={true} itemId="monitor_id" - itemIdToExpandedRowMap={drawerIds.reduce((map: ExpandedRowMap, id: string) => { - return { - ...map, - [id]: ( - monitorId === id) : undefined - } - successColor={successColor} - dangerColor={dangerColor} - /> - ), - }; - }, {})} + itemIdToExpandedRowMap={getExpandedRowMap()} items={items} // TODO: not needed without sorting and pagination // onChange={onChange} @@ -148,11 +145,7 @@ export const MonitorListComponent = (props: Props) => { defaultMessage: 'Name', }), render: (name: string, summary: MonitorSummary) => ( - + {name ? name : `Unnamed - ${summary.monitor_id}`} ), @@ -231,9 +224,9 @@ export const MonitorListComponent = (props: Props) => { }, } )} - iconType={drawerIds.find(item => item === id) ? 'arrowUp' : 'arrowDown'} + iconType={drawerIds.includes(id) ? 'arrowUp' : 'arrowDown'} onClick={() => { - if (drawerIds.find(i => id === i)) { + if (drawerIds.includes(id)) { updateDrawerIds(drawerIds.filter(p => p !== id)); } else { updateDrawerIds([...drawerIds, id]); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer.tsx deleted file mode 100644 index ac54518b6f49f..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { MonitorSummary } from '../../../../common/graphql/types'; -import { CheckList } from './check_list'; -import { toCondensedCheck } from './to_condensed_check'; -import { CondensedCheckList } from './condensed_check_list'; - -interface MonitorListDrawerProps { - summary?: MonitorSummary; - dangerColor: string; - successColor: string; - /** - * The number of checks the component should fully render - * before squashing them to single rows with condensed details. - */ - condensedCheckLimit: number; -} - -/** - * The elements shown when the user expands the monitor list rows. - */ -export const MonitorListDrawer = ({ - condensedCheckLimit, - dangerColor, - successColor, - summary, -}: MonitorListDrawerProps) => { - if (!summary || !summary.state.checks) { - return null; - } - if (summary.state.checks.length < condensedCheckLimit) { - return ; - } else { - return ( - - ); - } -}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/location_link.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/location_link.test.tsx.snap similarity index 96% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/location_link.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/location_link.test.tsx.snap index 281372023ce29..877f1fc6d7c85 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/location_link.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/location_link.test.tsx.snap @@ -16,6 +16,7 @@ exports[`LocationLink component renders a help link when location not present 1` exports[`LocationLink component renders the location when present 1`] = ` us-east-1 diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap new file mode 100644 index 0000000000000..80e064e25e1a5 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap @@ -0,0 +1,109 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MonitorListDrawer component renders a MonitorListDrawer when there are many checks 1`] = ` + + + + + https://expired.badssl.com + + + + + + + + +`; + +exports[`MonitorListDrawer component renders a MonitorListDrawer when there is only one check 1`] = ` + + + + + https://expired.badssl.com + + + + + + + + +`; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_list.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_list.test.tsx.snap new file mode 100644 index 0000000000000..94162a19a2daa --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_list.test.tsx.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MonitorStatusList component renders checks 1`] = ` + + + + + , + } + } + /> + + +`; + +exports[`MonitorStatusList component renders null in place of child status with missing ip 1`] = ` + + + + + , + } + } + /> + + +`; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap new file mode 100644 index 0000000000000..e2caf6f718728 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap @@ -0,0 +1,43 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MonitorStatusRow component renders status row when status is down 1`] = ` + + + + + + +`; + +exports[`MonitorStatusRow component renders status row when status is up 1`] = ` + + + + + + +`; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap new file mode 100644 index 0000000000000..cdda21b75770a --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap @@ -0,0 +1,59 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MostRecentError component renders properly with empty data 1`] = ` +Array [ +
+

+ Most recent error +

+
, + , +] +`; + +exports[`MostRecentError component validates props with shallow render 1`] = ` + + + +`; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json new file mode 100644 index 0000000000000..64adf3642fb22 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json @@ -0,0 +1,623 @@ +{ + "data": { + "monitorStates": { + "prevPagePagination": null, + "nextPagePagination": null, + "totalSummaryCount": { "count": 147428, "__typename": "DocCount" }, + "summaries": [ + { + "monitor_id": "andrewvc-com", + "histogram": { + "count": 60, + "points": [ + { + "timestamp": 1570538088000, + "up": 8, + "down": 0, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538124000, + "up": 16, + "down": 0, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538160000, + "up": 12, + "down": 0, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538196000, + "up": 16, + "down": 0, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538232000, + "up": 8, + "down": 0, + "__typename": "SummaryHistogramPoint" + } + ], + "__typename": "SummaryHistogram" + }, + "state": { + "agent": null, + "checks": [ + { + "agent": { "id": "8f9a37fb-573a-4fdc-9895-440a5b39c250", "__typename": "Agent" }, + "container": null, + "kubernetes": null, + "monitor": { + "ip": "185.199.108.153", + "name": "", + "status": "up", + "__typename": "CheckMonitor" + }, + "observer": { + "geo": { "name": null, "location": null, "__typename": "CheckGeo" }, + "__typename": "CheckObserver" + }, + "timestamp": "1570538246145", + "__typename": "Check" + }, + { + "agent": { "id": "8f9a37fb-573a-4fdc-9895-440a5b39c250", "__typename": "Agent" }, + "container": null, + "kubernetes": null, + "monitor": { + "ip": "185.199.109.153", + "name": "", + "status": "up", + "__typename": "CheckMonitor" + }, + "observer": { + "geo": { "name": null, "location": null, "__typename": "CheckGeo" }, + "__typename": "CheckObserver" + }, + "timestamp": "1570538246145", + "__typename": "Check" + }, + { + "agent": { "id": "8f9a37fb-573a-4fdc-9895-440a5b39c250", "__typename": "Agent" }, + "container": null, + "kubernetes": null, + "monitor": { + "ip": "185.199.110.153", + "name": "", + "status": "up", + "__typename": "CheckMonitor" + }, + "observer": { + "geo": { "name": null, "location": null, "__typename": "CheckGeo" }, + "__typename": "CheckObserver" + }, + "timestamp": "1570538246145", + "__typename": "Check" + }, + { + "agent": { "id": "8f9a37fb-573a-4fdc-9895-440a5b39c250", "__typename": "Agent" }, + "container": null, + "kubernetes": null, + "monitor": { + "ip": "185.199.111.153", + "name": "", + "status": "up", + "__typename": "CheckMonitor" + }, + "observer": { + "geo": { "name": null, "location": null, "__typename": "CheckGeo" }, + "__typename": "CheckObserver" + }, + "timestamp": "1570538246145", + "__typename": "Check" + } + ], + "geo": null, + "observer": { + "geo": { "name": [], "location": null, "__typename": "StateGeo" }, + "__typename": "StateObserver" + }, + "monitor": { + "id": null, + "name": null, + "status": "up", + "type": null, + "__typename": "MonitorState" + }, + "summary": { "up": 4, "down": 0, "geo": null, "__typename": "Summary" }, + "url": { + "full": "http://blog.andrewvc.com", + "domain": "blog.andrewvc.com", + "__typename": "StateUrl" + }, + "timestamp": 1570538246145, + "error": null, + "__typename": "State" + }, + "__typename": "MonitorSummary" + }, + { + "monitor_id": "bad-ssl", + "histogram": { + "count": 16, + "points": [ + { + "timestamp": 1570538088000, + "up": 0, + "down": 3, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538124000, + "up": 0, + "down": 4, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538160000, + "up": 0, + "down": 3, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538196000, + "up": 0, + "down": 4, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538232000, + "up": 0, + "down": 2, + "__typename": "SummaryHistogramPoint" + } + ], + "__typename": "SummaryHistogram" + }, + "state": { + "agent": null, + "checks": [ + { + "agent": { "id": "8f9a37fb-573a-4fdc-9895-440a5b39c250", "__typename": "Agent" }, + "container": null, + "kubernetes": null, + "monitor": { + "ip": "104.154.89.105", + "name": "", + "status": "down", + "__typename": "CheckMonitor" + }, + "observer": { + "geo": { "name": null, "location": null, "__typename": "CheckGeo" }, + "__typename": "CheckObserver" + }, + "timestamp": "1570538246144", + "__typename": "Check" + } + ], + "geo": null, + "observer": { + "geo": { "name": [], "location": null, "__typename": "StateGeo" }, + "__typename": "StateObserver" + }, + "monitor": { + "id": null, + "name": null, + "status": "down", + "type": null, + "__typename": "MonitorState" + }, + "summary": { "up": 0, "down": 1, "geo": null, "__typename": "Summary" }, + "url": { + "full": "https://expired.badssl.com", + "domain": "expired.badssl.com", + "__typename": "StateUrl" + }, + "timestamp": 1570538246144, + "error": null, + "__typename": "State" + }, + "__typename": "MonitorSummary" + }, + { + "monitor_id": "elastic-co", + "histogram": { + "count": 72, + "points": [ + { + "timestamp": 1570538088000, + "up": 4, + "down": 4, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538124000, + "up": 8, + "down": 8, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538160000, + "up": 8, + "down": 8, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538196000, + "up": 12, + "down": 12, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538232000, + "up": 4, + "down": 4, + "__typename": "SummaryHistogramPoint" + } + ], + "__typename": "SummaryHistogram" + }, + "state": { + "agent": null, + "checks": , + "geo": null, + "observer": { + "geo": { "name": [], "location": null, "__typename": "StateGeo" }, + "__typename": "StateObserver" + }, + "monitor": { + "id": null, + "name": "elastic", + "status": "mixed", + "type": null, + "__typename": "MonitorState" + }, + "summary": { "up": 4, "down": 4, "geo": null, "__typename": "Summary" }, + "url": { + "full": "https://www.elastic.co", + "domain": "www.elastic.co", + "__typename": "StateUrl" + }, + "timestamp": 1570538236414, + "error": null, + "__typename": "State" + }, + "__typename": "MonitorSummary" + }, + { + "monitor_id": "kibana-local", + "histogram": { + "count": 66, + "points": [ + { + "timestamp": 1570538052000, + "up": 1, + "down": 1, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538088000, + "up": 7, + "down": 7, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538124000, + "up": 7, + "down": 7, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538160000, + "up": 7, + "down": 7, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538196000, + "up": 8, + "down": 8, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538232000, + "up": 3, + "down": 3, + "__typename": "SummaryHistogramPoint" + } + ], + "__typename": "SummaryHistogram" + }, + "state": { + "agent": null, + "checks": [ + { + "agent": { "id": "8f9a37fb-573a-4fdc-9895-440a5b39c250", "__typename": "Agent" }, + "container": null, + "kubernetes": null, + "monitor": { + "ip": "127.0.0.1", + "name": "", + "status": "up", + "__typename": "CheckMonitor" + }, + "observer": { + "geo": { "name": null, "location": null, "__typename": "CheckGeo" }, + "__typename": "CheckObserver" + }, + "timestamp": "1570538246144", + "__typename": "Check" + }, + { + "agent": { "id": "8f9a37fb-573a-4fdc-9895-440a5b39c250", "__typename": "Agent" }, + "container": null, + "kubernetes": null, + "monitor": { + "ip": "::1", + "name": "", + "status": "down", + "__typename": "CheckMonitor" + }, + "observer": { + "geo": { "name": null, "location": null, "__typename": "CheckGeo" }, + "__typename": "CheckObserver" + }, + "timestamp": "1570538246145", + "__typename": "Check" + } + ], + "geo": null, + "observer": { + "geo": { "name": [], "location": null, "__typename": "StateGeo" }, + "__typename": "StateObserver" + }, + "monitor": { + "id": null, + "name": null, + "status": "mixed", + "type": null, + "__typename": "MonitorState" + }, + "summary": { "up": 1, "down": 1, "geo": null, "__typename": "Summary" }, + "url": { + "full": "http://localhost:5601/", + "domain": "localhost", + "__typename": "StateUrl" + }, + "timestamp": 1570538246145, + "error": null, + "__typename": "State" + }, + "__typename": "MonitorSummary" + }, + { + "monitor_id": "localhost", + "histogram": { + "count": 28, + "points": [ + { + "timestamp": 1570538052000, + "up": 12, + "down": 0, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538088000, + "up": 3, + "down": 0, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538124000, + "up": 4, + "down": 0, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538160000, + "up": 3, + "down": 0, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538196000, + "up": 4, + "down": 0, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538232000, + "up": 2, + "down": 0, + "__typename": "SummaryHistogramPoint" + } + ], + "__typename": "SummaryHistogram" + }, + "state": { + "agent": null, + "checks": [ + { + "agent": { "id": "8f9a37fb-573a-4fdc-9895-440a5b39c250", "__typename": "Agent" }, + "container": null, + "kubernetes": null, + "monitor": { + "ip": "127.0.0.1", + "name": "localhost", + "status": "up", + "__typename": "CheckMonitor" + }, + "observer": { + "geo": { "name": null, "location": null, "__typename": "CheckGeo" }, + "__typename": "CheckObserver" + }, + "timestamp": "1570538246143", + "__typename": "Check" + } + ], + "geo": null, + "observer": { + "geo": { "name": [], "location": null, "__typename": "StateGeo" }, + "__typename": "StateObserver" + }, + "monitor": { + "id": null, + "name": "localhost", + "status": "up", + "type": null, + "__typename": "MonitorState" + }, + "summary": { "up": 1, "down": 0, "geo": null, "__typename": "Summary" }, + "url": { + "full": "http://localhost:9200", + "domain": "localhost", + "__typename": "StateUrl" + }, + "timestamp": 1570538246143, + "error": null, + "__typename": "State" + }, + "__typename": "MonitorSummary" + }, + { + "monitor_id": "secure-avc", + "histogram": { + "count": 64, + "points": [ + { + "timestamp": 1570538088000, + "up": 12, + "down": 0, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538124000, + "up": 16, + "down": 0, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538160000, + "up": 12, + "down": 0, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538196000, + "up": 16, + "down": 0, + "__typename": "SummaryHistogramPoint" + }, + { + "timestamp": 1570538232000, + "up": 8, + "down": 0, + "__typename": "SummaryHistogramPoint" + } + ], + "__typename": "SummaryHistogram" + }, + "state": { + "agent": null, + "checks": [ + { + "agent": { "id": "8f9a37fb-573a-4fdc-9895-440a5b39c250", "__typename": "Agent" }, + "container": null, + "kubernetes": null, + "monitor": { + "ip": "185.199.108.153", + "name": "", + "status": "up", + "__typename": "CheckMonitor" + }, + "observer": { + "geo": { "name": null, "location": null, "__typename": "CheckGeo" }, + "__typename": "CheckObserver" + }, + "timestamp": "1570538246145", + "__typename": "Check" + }, + { + "agent": { "id": "8f9a37fb-573a-4fdc-9895-440a5b39c250", "__typename": "Agent" }, + "container": null, + "kubernetes": null, + "monitor": { + "ip": "185.199.109.153", + "name": "", + "status": "up", + "__typename": "CheckMonitor" + }, + "observer": { + "geo": { "name": null, "location": null, "__typename": "CheckGeo" }, + "__typename": "CheckObserver" + }, + "timestamp": "1570538246145", + "__typename": "Check" + }, + { + "agent": { "id": "8f9a37fb-573a-4fdc-9895-440a5b39c250", "__typename": "Agent" }, + "container": null, + "kubernetes": null, + "monitor": { + "ip": "185.199.110.153", + "name": "", + "status": "up", + "__typename": "CheckMonitor" + }, + "observer": { + "geo": { "name": null, "location": null, "__typename": "CheckGeo" }, + "__typename": "CheckObserver" + }, + "timestamp": "1570538246145", + "__typename": "Check" + }, + { + "agent": { "id": "8f9a37fb-573a-4fdc-9895-440a5b39c250", "__typename": "Agent" }, + "container": null, + "kubernetes": null, + "monitor": { + "ip": "185.199.111.153", + "name": "", + "status": "up", + "__typename": "CheckMonitor" + }, + "observer": { + "geo": { "name": null, "location": null, "__typename": "CheckGeo" }, + "__typename": "CheckObserver" + }, + "timestamp": "1570538246145", + "__typename": "Check" + } + ], + "geo": null, + "observer": { + "geo": { "name": [], "location": null, "__typename": "StateGeo" }, + "__typename": "StateObserver" + }, + "monitor": { + "id": null, + "name": null, + "status": "up", + "type": null, + "__typename": "MonitorState" + }, + "summary": { "up": 4, "down": 0, "geo": null, "__typename": "Summary" }, + "url": { + "full": "https://blog.andrewvc.com", + "domain": "blog.andrewvc.com", + "__typename": "StateUrl" + }, + "timestamp": 1570538246145, + "error": null, + "__typename": "State" + }, + "__typename": "MonitorSummary" + } + ], + "__typename": "MonitorSummaryResult" + } + } +} diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/location_link.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/location_link.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/location_link.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/location_link.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_drawer.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx similarity index 59% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_drawer.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx index cd3511257bcaa..aca43f550aa14 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_drawer.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx @@ -3,14 +3,16 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import { MonitorSummary, Check } from '../../../../../common/graphql/types'; +import 'jest'; +import { MonitorSummary, Check } from '../../../../../../common/graphql/types'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import React from 'react'; -import { MonitorListDrawer } from '../monitor_list_drawer'; +import { MonitorListDrawerComponent } from '../monitor_list_drawer'; describe('MonitorListDrawer component', () => { let summary: MonitorSummary; + let loadMonitorDetails: any; + let monitorDetails: any; beforeEach(() => { summary = { @@ -30,13 +32,30 @@ describe('MonitorListDrawer component', () => { down: 0, }, timestamp: '123', + url: { + domain: 'expired.badssl.com', + full: 'https://expired.badssl.com', + }, + }, + }; + monitorDetails = { + monitorId: 'bad-ssl', + error: { + type: 'io', + message: + 'Get https://expired.badssl.com: x509: certificate has expired or is not yet valid', }, }; + loadMonitorDetails = () => null; }); it('renders nothing when no summary data is present', () => { const component = shallowWithIntl( - + ); expect(component).toEqual({}); }); @@ -44,29 +63,27 @@ describe('MonitorListDrawer component', () => { it('renders nothing when no check data is present', () => { delete summary.state.checks; const component = shallowWithIntl( - ); expect(component).toEqual({}); }); - it('renders a Checklist when there is only one check', () => { + it('renders a MonitorListDrawer when there is only one check', () => { const component = shallowWithIntl( - ); expect(component).toMatchSnapshot(); }); - it('renders a CondensedCheckList when there are many checks', () => { + it('renders a MonitorListDrawer when there are many checks', () => { const checks: Check[] = [ { monitor: { @@ -92,11 +109,10 @@ describe('MonitorListDrawer component', () => { ]; summary.state.checks = checks; const component = shallowWithIntl( - ); expect(component).toMatchSnapshot(); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_status_list.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_status_list.test.tsx new file mode 100644 index 0000000000000..8c07d0b1a7d22 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_status_list.test.tsx @@ -0,0 +1,141 @@ +/* + * 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 { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import moment from 'moment'; +import { MonitorStatusList } from '../monitor_status_list'; +import { Check } from '../../../../../../common/graphql/types'; + +describe('MonitorStatusList component', () => { + let checks: Check[]; + + beforeAll(() => { + moment.prototype.toLocaleString = jest.fn(() => '2019-06-21 15:29:26'); + moment.prototype.from = jest.fn(() => 'a few moments ago'); + }); + + beforeEach(() => { + checks = [ + { + agent: { id: '8f9a37fb-573a-4fdc-9895-440a5b39c250' }, + monitor: { + ip: '151.101.130.217', + name: 'elastic', + status: 'up', + }, + observer: { + geo: { name: null, location: null }, + }, + timestamp: '1570538236414', + }, + { + agent: { id: '8f9a37fb-573a-4fdc-9895-440a5b39c250' }, + monitor: { + ip: '151.101.194.217', + name: 'elastic', + status: 'up', + }, + observer: { + geo: { name: null, location: null }, + }, + timestamp: '1570538236414', + }, + { + agent: { id: '8f9a37fb-573a-4fdc-9895-440a5b39c250' }, + monitor: { + ip: '151.101.2.217', + name: 'elastic', + status: 'up', + }, + observer: { + geo: { name: null, location: null }, + }, + timestamp: '1570538236414', + }, + { + agent: { id: '8f9a37fb-573a-4fdc-9895-440a5b39c250' }, + container: null, + kubernetes: null, + monitor: { + ip: '151.101.66.217', + name: 'elastic', + status: 'up', + }, + observer: { + geo: { name: null, location: null }, + }, + timestamp: '1570538236414', + }, + { + agent: { id: '8f9a37fb-573a-4fdc-9895-440a5b39c250' }, + container: null, + kubernetes: null, + monitor: { + ip: '2a04:4e42:200::729', + name: 'elastic', + status: 'down', + }, + observer: { + geo: { name: null, location: null }, + }, + timestamp: '1570538236414', + }, + { + agent: { id: '8f9a37fb-573a-4fdc-9895-440a5b39c250' }, + container: null, + kubernetes: null, + monitor: { + ip: '2a04:4e42:400::729', + name: 'elastic', + status: 'down', + }, + observer: { + geo: { name: null, location: null }, + }, + timestamp: '1570538236414', + }, + { + agent: { id: '8f9a37fb-573a-4fdc-9895-440a5b39c250' }, + container: null, + kubernetes: null, + monitor: { + ip: '2a04:4e42:600::729', + name: 'elastic', + status: 'down', + }, + observer: { + geo: { name: null, location: null }, + }, + timestamp: '1570538236414', + }, + { + agent: { id: '8f9a37fb-573a-4fdc-9895-440a5b39c250' }, + container: null, + kubernetes: null, + monitor: { + ip: '2a04:4e42::729', + name: 'elastic', + status: 'down', + }, + observer: { + geo: { name: null, location: null }, + }, + timestamp: '1570538236414', + }, + ]; + }); + + it('renders checks', () => { + const component = shallowWithIntl(); + expect(component).toMatchSnapshot(); + }); + + it('renders null in place of child status with missing ip', () => { + const component = shallowWithIntl(); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_status_row.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_status_row.test.tsx new file mode 100644 index 0000000000000..0353d0197f7f7 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_status_row.test.tsx @@ -0,0 +1,31 @@ +/* + * 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 { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import { MonitorStatusRow } from '../monitor_status_row'; + +describe('MonitorStatusRow component', () => { + let locationNames: Set; + + beforeEach(() => { + locationNames = new Set(['Berlin', 'Islamabad', 'London']); + }); + + it('renders status row when status is up', () => { + const component = shallowWithIntl( + + ); + expect(component).toMatchSnapshot(); + }); + + it('renders status row when status is down', () => { + const component = shallowWithIntl( + + ); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx new file mode 100644 index 0000000000000..71eab73cd52d6 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx @@ -0,0 +1,43 @@ +/* + * 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 { shallowWithIntl, renderWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import { BrowserRouter as Router } from 'react-router-dom'; +import { MostRecentError } from '../most_recent_error'; + +describe('MostRecentError component', () => { + let monitorDetails: any; + + beforeEach(() => { + monitorDetails = { + monitorId: 'bad-ssl', + error: { + type: 'io', + message: + 'Get https://expired.badssl.com: x509: certificate has expired or is not yet valid', + }, + }; + }); + + it('validates props with shallow render', () => { + const component = shallowWithIntl( + + + + ); + expect(component).toMatchSnapshot(); + }); + + it('renders properly with empty data', () => { + const component = renderWithIntl( + + + + ); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/index.ts b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/index.ts new file mode 100644 index 0000000000000..73fb07db60de8 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { MonitorListDrawer } from './monitor_list_drawer'; +export { LocationLink } from './location_link'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/location_link.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/location_link.tsx similarity index 93% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/location_link.tsx rename to x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/location_link.tsx index 70aaebc4d358e..72c1dbfd85604 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/location_link.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/location_link.tsx @@ -22,7 +22,9 @@ const locationDocsLink = */ export const LocationLink = ({ location, textSize }: LocationLinkProps) => { return location ? ( - {location} + + {location} + ) : ( {i18n.translate('xpack.uptime.monitorList.geoName.helpLinkAnnotation', { diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx new file mode 100644 index 0000000000000..25f8f9718d411 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx @@ -0,0 +1,89 @@ +/* + * 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 React, { useEffect } from 'react'; +import { EuiLink, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; +import { get } from 'lodash'; +import styled from 'styled-components'; +import { connect } from 'react-redux'; +import { MonitorSummary } from '../../../../../common/graphql/types'; +import { AppState } from '../../../../state'; +import { fetchMonitorDetails } from '../../../../state/actions/monitor'; +import { MostRecentError } from './most_recent_error'; +import { getMonitorDetails } from '../../../../state/selectors'; +import { MonitorStatusList } from './monitor_status_list'; + +const ContainerDiv = styled.div` + padding: 10px; + width: 100%; +`; + +interface MonitorListDrawerProps { + /** + * Monitor Summary + */ + summary: MonitorSummary; + + /** + * Monitor details to be fetched from rest api using monitorId + */ + monitorDetails: any; + + /** + * Redux action to trigger , loading monitor details + */ + loadMonitorDetails: typeof fetchMonitorDetails; +} + +/** + * The elements shown when the user expands the monitor list rows. + */ + +export function MonitorListDrawerComponent({ + summary, + loadMonitorDetails, + monitorDetails, +}: MonitorListDrawerProps) { + if (!summary || !summary.state.checks) { + return null; + } + useEffect(() => { + loadMonitorDetails(summary.monitor_id); + }, []); + + const monitorUrl: string | undefined = get(summary.state.url, 'full', undefined); + + return ( + + + + + {monitorUrl} + + + + + + + {monitorDetails && monitorDetails.error && ( + + )} + + ); +} + +const mapStateToProps = (state: AppState, { summary }: any) => ({ + monitorDetails: getMonitorDetails(state, summary), +}); + +const mapDispatchToProps = (dispatch: any) => ({ + loadMonitorDetails: (monitorId: string) => dispatch(fetchMonitorDetails(monitorId)), +}); + +export const MonitorListDrawer = connect( + mapStateToProps, + mapDispatchToProps +)(MonitorListDrawerComponent); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_status_list.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_status_list.tsx new file mode 100644 index 0000000000000..82e415cd5e8ae --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_status_list.tsx @@ -0,0 +1,59 @@ +/* + * 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 React from 'react'; +import { get } from 'lodash'; +import { EuiCallOut } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Check } from '../../../../../common/graphql/types'; +import { LocationLink } from './location_link'; +import { MonitorStatusRow } from './monitor_status_row'; + +interface MonitorStatusListProps { + /** + * Recent List of checks performed on monitor + */ + checks: Check[]; +} + +export const UP = 'up'; +export const DOWN = 'down'; +export const UNNAMED_LOCATION = 'unnamed-location'; + +export const MonitorStatusList = ({ checks }: MonitorStatusListProps) => { + const upChecks: Set = new Set(); + const downChecks: Set = new Set(); + + checks.forEach((check: Check) => { + // Doing this way because name is either string or null, get() default value only works on undefined value + const location = get(check, 'observer.geo.name', null) || UNNAMED_LOCATION; + + if (check.monitor.status === UP) { + upChecks.add(location); + } else if (check.monitor.status === DOWN) { + downChecks.add(location); + } + }); + + // if monitor is down in one dns, it will be considered down so removing it from up list + const absUpChecks: Set = new Set([...upChecks].filter(item => !downChecks.has(item))); + + return ( + <> + + + {(downChecks.has(UNNAMED_LOCATION) || upChecks.has(UNNAMED_LOCATION)) && ( + + }} + /> + + )} + + ); +}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_status_row.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_status_row.tsx new file mode 100644 index 0000000000000..90aa887a78356 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_status_row.tsx @@ -0,0 +1,63 @@ +/* + * 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 React, { useContext } from 'react'; +import { EuiHealth, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { UptimeSettingsContext } from '../../../../contexts'; +import { UNNAMED_LOCATION, UP } from './monitor_status_list'; + +interface MonitorStatusRowProps { + /** + * Recent List of checks performed on monitor + */ + locationNames: Set; + /** + * Monitor status for this of locations + */ + status: string; +} + +export const MonitorStatusRow = ({ locationNames, status }: MonitorStatusRowProps) => { + const { + colors: { success, danger }, + } = useContext(UptimeSettingsContext); + + const color = status === UP ? success : danger; + + let checkListArray = [...locationNames]; + // If un-named location exists, move it to end + if (locationNames.has(UNNAMED_LOCATION)) { + checkListArray = checkListArray.filter(item => item !== UNNAMED_LOCATION); + checkListArray.push(UNNAMED_LOCATION); + } + + if (locationNames.size === 0) { + return null; + } + + const locations = checkListArray.join(', '); + return ( + <> + + {status === UP ? ( + + ) : ( + + )} + + + + ); +}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/most_recent_error.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/most_recent_error.tsx new file mode 100644 index 0000000000000..12ea8c52adeb0 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/most_recent_error.tsx @@ -0,0 +1,51 @@ +/* + * 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 React from 'react'; +import { EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { MonitorPageLink } from '../monitor_page_link'; +import { useUrlParams } from '../../../../hooks'; +import { stringifyUrlParams } from '../../../../lib/helper/stringify_url_params'; + +interface RecentError { + message: string; + type: string; +} + +interface MostRecentErrorProps { + /** + * error returned from API for monitor details + */ + error: RecentError; + + /** + * monitorId to be used for link to detail page + */ + monitorId: string; +} + +export const MostRecentError = ({ error, monitorId }: MostRecentErrorProps) => { + const [getUrlParams] = useUrlParams(); + const { absoluteDateRangeStart, absoluteDateRangeEnd, ...params } = getUrlParams(); + params.selectedPingStatus = 'down'; + const linkParameters = stringifyUrlParams(params); + + return ( + <> + +

+ {i18n.translate('xpack.uptime.monitorList.mostRecentError.title', { + defaultMessage: 'Most recent error', + description: 'Most Recent Error title in Monitor List Expanded row', + })} +

+
+ + {error.message} + + + ); +}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_page_link.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_page_link.tsx new file mode 100644 index 0000000000000..803b399810508 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_page_link.tsx @@ -0,0 +1,40 @@ +/* + * 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 { EuiLink } from '@elastic/eui'; +import { Link } from 'react-router-dom'; +import React, { FunctionComponent } from 'react'; + +interface DetailPageLinkProps { + /** + * MonitorId to be used to redirect to detail page + */ + monitorId: string; + /** + * Link parameters usually filter states + */ + linkParameters: string | undefined; +} + +export const MonitorPageLink: FunctionComponent = ({ + children, + monitorId, + linkParameters, +}) => { + const getLocationTo = () => { + // encode monitorId param as 64 base string to make it a valid URL, since it can be a url + return linkParameters + ? `/monitor/${btoa(monitorId)}/${linkParameters}` + : `/monitor/${btoa(monitorId)}`; + }; + return ( + + + {children} + + + ); +}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/to_condensed_check.ts b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/to_condensed_check.ts deleted file mode 100644 index 18bfe19f2fd5f..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/to_condensed_check.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { get } from 'lodash'; -import { Check } from '../../../../common/graphql/types'; -import { CondensedCheck } from './types'; - -const inferCondensedFields = ( - check: CondensedCheck, - currentStatus: string, - currentTimestamp: string -) => { - const { status: condensedStatus, timestamp } = check; - if (condensedStatus !== currentStatus && condensedStatus !== 'mixed') { - check.status = 'mixed'; - } - if (timestamp < currentTimestamp) { - check.timestamp = currentTimestamp; - } -}; - -export const toCondensedCheck = (checks: Check[]) => { - const condensedChecks: Map = new Map(); - checks.forEach((check: Check) => { - const location = get(check, 'observer.geo.name', null); - const { - monitor: { ip, status }, - timestamp, - } = check; - let condensedCheck: CondensedCheck | undefined; - if ((condensedCheck = condensedChecks.get(location))) { - condensedCheck.childStatuses.push({ ip, status, timestamp }); - inferCondensedFields(condensedCheck, status, timestamp); - } else { - condensedChecks.set(location, { - childStatuses: [{ ip, status, timestamp }], - location, - status, - timestamp, - }); - } - }); - return Array.from(condensedChecks.values()); -}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_page_link.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_page_link.tsx deleted file mode 100644 index 4bfa2f95c3f77..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_page_link.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiLink } from '@elastic/eui'; -import { Link } from 'react-router-dom'; -import React, { FunctionComponent } from 'react'; - -interface DetailPageLinkProps { - id: string; - location: string | undefined; - linkParameters: string | undefined; -} - -export const MonitorPageLink: FunctionComponent = ({ - children, - id, - location, - linkParameters, -}) => ( - - - {children} - - -); diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/monitor.ts b/x-pack/legacy/plugins/uptime/public/state/actions/monitor.ts new file mode 100644 index 0000000000000..40738740e5841 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/actions/monitor.ts @@ -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. + */ + +export const FETCH_MONITOR_DETAILS = 'FETCH_MONITOR_DETAILS'; +export const FETCH_MONITOR_DETAILS_SUCCESS = 'FETCH_MONITOR_DETAILS_SUCCESS'; +export const FETCH_MONITOR_DETAILS_FAIL = 'FETCH_MONITOR_DETAILS_FAIL'; + +export interface MonitorDetailsState { + monitorId: string; + error: Error; +} + +interface GetMonitorDetailsAction { + type: typeof FETCH_MONITOR_DETAILS; + payload: string; +} + +interface GetMonitorDetailsSuccessAction { + type: typeof FETCH_MONITOR_DETAILS_SUCCESS; + payload: MonitorDetailsState; +} + +interface GetMonitorDetailsFailAction { + type: typeof FETCH_MONITOR_DETAILS_FAIL; + payload: any; +} + +export function fetchMonitorDetails(monitorId: string): GetMonitorDetailsAction { + return { + type: FETCH_MONITOR_DETAILS, + payload: monitorId, + }; +} + +export function fetchMonitorDetailsSuccess( + monitorDetailsState: MonitorDetailsState +): GetMonitorDetailsSuccessAction { + return { + type: FETCH_MONITOR_DETAILS_SUCCESS, + payload: monitorDetailsState, + }; +} + +export function fetchMonitorDetailsFail(error: any): GetMonitorDetailsFailAction { + return { + type: FETCH_MONITOR_DETAILS_FAIL, + payload: error, + }; +} + +export type MonitorActionTypes = + | GetMonitorDetailsAction + | GetMonitorDetailsSuccessAction + | GetMonitorDetailsFailAction; diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/ui.ts b/x-pack/legacy/plugins/uptime/public/state/actions/ui.ts index 7c18774e1d67d..f0234f903d3d8 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/ui.ts +++ b/x-pack/legacy/plugins/uptime/public/state/actions/ui.ts @@ -5,6 +5,7 @@ */ export const SET_INTEGRATION_POPOVER_STATE = 'SET_INTEGRATION_POPOVER_STATE'; +export const SET_BASE_PATH = 'SET_BASE_PATH'; export interface PopoverState { id: string; @@ -16,6 +17,11 @@ interface SetIntegrationPopoverAction { payload: PopoverState; } +interface SetBasePathAction { + type: typeof SET_BASE_PATH; + payload: string; +} + export function toggleIntegrationsPopover(popoverState: PopoverState): SetIntegrationPopoverAction { return { type: SET_INTEGRATION_POPOVER_STATE, @@ -23,4 +29,11 @@ export function toggleIntegrationsPopover(popoverState: PopoverState): SetIntegr }; } -export type UiActionTypes = SetIntegrationPopoverAction; +export function setBasePath(basePath: string): SetBasePathAction { + return { + type: SET_BASE_PATH, + payload: basePath, + }; +} + +export type UiActionTypes = SetIntegrationPopoverAction | SetBasePathAction; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/index.ts b/x-pack/legacy/plugins/uptime/public/state/api/index.ts new file mode 100644 index 0000000000000..e9b8082b417ba --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/api/index.ts @@ -0,0 +1,6 @@ +/* + * 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 * from './monitor'; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/monitor.ts b/x-pack/legacy/plugins/uptime/public/state/api/monitor.ts new file mode 100644 index 0000000000000..d043cf7119472 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/api/monitor.ts @@ -0,0 +1,29 @@ +/* + * 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 { ThrowReporter } from 'io-ts/lib/ThrowReporter'; +import { getApiPath } from '../../lib/helper'; +import { MonitorDetailsType, MonitorDetails } from '../../../common/runtime_types'; + +interface ApiRequest { + monitorId: string; + basePath: string; +} + +export const fetchMonitorDetails = async ({ + monitorId, + basePath, +}: ApiRequest): Promise => { + const url = getApiPath(`/api/uptime/monitor/details?monitorId=${monitorId}`, basePath); + const response = await fetch(url); + if (!response.ok) { + throw new Error(response.statusText); + } + return response.json().then(data => { + ThrowReporter.report(MonitorDetailsType.decode(data)); + return data; + }); +}; diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/index.ts b/x-pack/legacy/plugins/uptime/public/state/effects/index.ts new file mode 100644 index 0000000000000..92802f2e0c84a --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/effects/index.ts @@ -0,0 +1,12 @@ +/* + * 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 { fork } from 'redux-saga/effects'; +import { fetchMonitorDetailsEffect } from './monitor'; + +export function* rootEffect() { + yield fork(fetchMonitorDetailsEffect); +} diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/monitor.ts b/x-pack/legacy/plugins/uptime/public/state/effects/monitor.ts new file mode 100644 index 0000000000000..529b9041c9093 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/effects/monitor.ts @@ -0,0 +1,30 @@ +/* + * 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 { call, put, takeLatest, select } from 'redux-saga/effects'; +import { Action } from 'redux-actions'; +import { + FETCH_MONITOR_DETAILS, + FETCH_MONITOR_DETAILS_SUCCESS, + FETCH_MONITOR_DETAILS_FAIL, +} from '../actions/monitor'; +import { fetchMonitorDetails } from '../api'; +import { getBasePath } from '../selectors'; + +function* monitorDetailsEffect(action: Action) { + const monitorId: string = action.payload; + try { + const basePath = yield select(getBasePath); + const response = yield call(fetchMonitorDetails, { monitorId, basePath }); + yield put({ type: FETCH_MONITOR_DETAILS_SUCCESS, payload: response }); + } catch (error) { + yield put({ type: FETCH_MONITOR_DETAILS_FAIL, payload: error.message }); + } +} + +export function* fetchMonitorDetailsEffect() { + yield takeLatest(FETCH_MONITOR_DETAILS, monitorDetailsEffect); +} diff --git a/x-pack/legacy/plugins/uptime/public/state/index.ts b/x-pack/legacy/plugins/uptime/public/state/index.ts index 4ef3d26776a7e..01cffb636d33c 100644 --- a/x-pack/legacy/plugins/uptime/public/state/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/index.ts @@ -3,12 +3,18 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { compose, createStore } from 'redux'; +import { compose, createStore, applyMiddleware } from 'redux'; +import createSagaMiddleware from 'redux-saga'; import { rootReducer } from './reducers'; +import { rootEffect } from './effects'; const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; -export const store = createStore(rootReducer, composeEnhancers()); +const sagaMW = createSagaMiddleware(); + +export const store = createStore(rootReducer, composeEnhancers(applyMiddleware(sagaMW))); export type AppState = ReturnType; + +sagaMW.run(rootEffect); diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts index faa932a321cd1..186b02395b779 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts @@ -5,9 +5,10 @@ */ import { combineReducers } from 'redux'; - import { uiReducer } from './ui'; +import { monitorReducer } from './monitor'; export const rootReducer = combineReducers({ ui: uiReducer, + monitor: monitorReducer, }); diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor.ts new file mode 100644 index 0000000000000..4cacb6f8cab9e --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor.ts @@ -0,0 +1,53 @@ +/* + * 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 { + MonitorActionTypes, + MonitorDetailsState, + FETCH_MONITOR_DETAILS, + FETCH_MONITOR_DETAILS_SUCCESS, + FETCH_MONITOR_DETAILS_FAIL, +} from '../actions/monitor'; + +export interface MonitorState { + monitorDetailsList: MonitorDetailsState[]; + loading: boolean; + errors: any[]; +} + +const initialState: MonitorState = { + monitorDetailsList: [], + loading: false, + errors: [], +}; + +export function monitorReducer(state = initialState, action: MonitorActionTypes): MonitorState { + switch (action.type) { + case FETCH_MONITOR_DETAILS: + return { + ...state, + loading: true, + }; + case FETCH_MONITOR_DETAILS_SUCCESS: + const { monitorId } = action.payload; + return { + ...state, + monitorDetailsList: { + ...state.monitorDetailsList, + [monitorId]: action.payload, + }, + loading: false, + }; + case FETCH_MONITOR_DETAILS_FAIL: + const error = action.payload; + return { + ...state, + errors: [...state.errors, error], + }; + default: + return state; + } +} diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/ui.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/ui.ts index 1550b6c9936c3..d095d6ba961ca 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/ui.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/ui.ts @@ -4,14 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import { UiActionTypes, PopoverState, SET_INTEGRATION_POPOVER_STATE } from '../actions/ui'; +import { + UiActionTypes, + PopoverState, + SET_INTEGRATION_POPOVER_STATE, + SET_BASE_PATH, +} from '../actions/ui'; export interface UiState { integrationsPopoverOpen: PopoverState | null; + basePath: string; } const initialState: UiState = { integrationsPopoverOpen: null, + basePath: '', }; export function uiReducer(state = initialState, action: UiActionTypes): UiState { @@ -25,6 +32,12 @@ export function uiReducer(state = initialState, action: UiActionTypes): UiState open: popoverState.open, }, }; + case SET_BASE_PATH: + const basePath = action.payload; + return { + ...state, + basePath, + }; default: return state; } diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts index 223e7c029cb1c..59c3f0c31539f 100644 --- a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts @@ -6,3 +6,9 @@ import { AppState } from '../../state'; export const isIntegrationsPopupOpen = (state: AppState) => state.ui.integrationsPopoverOpen; + +export const getBasePath = (state: AppState) => state.ui.basePath; + +export const getMonitorDetails = (state: AppState, summary: any) => { + return state.monitor.monitorDetailsList[summary.monitor_id]; +}; diff --git a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx index 172b31a50ffbc..20513767afd5e 100644 --- a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx +++ b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx @@ -21,6 +21,7 @@ import { UptimeRefreshContext, UptimeSettingsContext, UMSettingsContextValues } import { UptimeDatePicker } from './components/functional/uptime_date_picker'; import { useUrlParams } from './hooks'; import { store } from './state'; +import { setBasePath } from './state/actions'; export interface UptimeAppColors { danger: string; @@ -142,6 +143,8 @@ const Application = (props: UptimeAppProps) => { }; }; + store.dispatch(setBasePath(basePath)); + return ( diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/adapter_types.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/adapter_types.ts index 8c184c3356989..996e80d2c8613 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/adapter_types.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/adapter_types.ts @@ -23,4 +23,5 @@ export interface UMMonitorsAdapter { ): Promise; getFilterBar(request: any, dateRangeStart: string, dateRangeEnd: string): Promise; getMonitorPageTitle(request: any, monitorId: string): Promise; + getMonitorDetails(request: any, monitorId: string): Promise; } diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/elasticsearch_monitors_adapter.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/elasticsearch_monitors_adapter.ts index f2d84d149344b..1a391e90f2a5e 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/elasticsearch_monitors_adapter.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/elasticsearch_monitors_adapter.ts @@ -16,6 +16,7 @@ import { import { getFilterClause, parseFilterQuery, getHistogramIntervalFormatted } from '../../helper'; import { DatabaseAdapter } from '../database'; import { UMMonitorsAdapter } from './adapter_types'; +import { MonitorDetails, Error } from '../../../../common/runtime_types'; const formatStatusBuckets = (time: any, buckets: any, docCount: any) => { let up = null; @@ -420,4 +421,47 @@ export class ElasticsearchMonitorsAdapter implements UMMonitorsAdapter { name: get(pageTitle, 'monitor.name', null), }; } + + public async getMonitorDetails(request: any, monitorId: string): Promise { + const params = { + index: INDEX_NAMES.HEARTBEAT, + body: { + size: 1, + query: { + bool: { + must: [ + { + exists: { + field: 'error', + }, + }, + ], + filter: [ + { + term: { + 'monitor.id': monitorId, + }, + }, + ], + }, + }, + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], + }, + }; + + const result = await this.database.search(request, params); + + const monitorError: Error | undefined = get(result, 'hits.hits[0]._source.error', undefined); + + return { + monitorId, + error: monitorError, + }; + } } diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/index.ts b/x-pack/legacy/plugins/uptime/server/rest_api/index.ts index 53ed7da4779a4..cc702362a57a8 100644 --- a/x-pack/legacy/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/legacy/plugins/uptime/server/rest_api/index.ts @@ -9,6 +9,7 @@ import { createGetAllRoute } from './pings'; import { createGetIndexPatternRoute } from './index_pattern'; import { createLogMonitorPageRoute, createLogOverviewPageRoute } from './telemetry'; import { UMRestApiRouteCreator } from './types'; +import { createGetMonitorDetailsRoute } from './monitors'; export * from './types'; export { createRouteWithAuth } from './create_route_with_auth'; @@ -18,4 +19,5 @@ export const restApiRoutes: UMRestApiRouteCreator[] = [ createLogMonitorPageRoute, createLogOverviewPageRoute, createGetIndexPatternRoute, + createGetMonitorDetailsRoute, ]; diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/monitors/index.ts b/x-pack/legacy/plugins/uptime/server/rest_api/monitors/index.ts new file mode 100644 index 0000000000000..2c4b9e9fb1f3e --- /dev/null +++ b/x-pack/legacy/plugins/uptime/server/rest_api/monitors/index.ts @@ -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 { createGetMonitorDetailsRoute } from './monitors_details'; diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/monitors/monitors_details.ts b/x-pack/legacy/plugins/uptime/server/rest_api/monitors/monitors_details.ts new file mode 100644 index 0000000000000..1440b55c1c137 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/server/rest_api/monitors/monitors_details.ts @@ -0,0 +1,25 @@ +/* + * 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 Joi from 'joi'; +import { UMServerLibs } from '../../lib/lib'; +import { MonitorDetails } from '../../../common/runtime_types/monitor/monitor_details'; + +export const createGetMonitorDetailsRoute = (libs: UMServerLibs) => ({ + method: 'GET', + path: '/api/uptime/monitor/details', + options: { + validate: { + query: Joi.object({ + monitorId: Joi.string(), + }), + }, + tags: ['access:uptime'], + }, + handler: async (request: any): Promise => { + const { monitorId } = request.query; + return await libs.monitors.getMonitorDetails(request, monitorId); + }, +});