diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/monitor/locations.ts b/x-pack/legacy/plugins/uptime/common/runtime_types/monitor/locations.ts
index a40453b3671b7..ea3cfe677ca99 100644
--- a/x-pack/legacy/plugins/uptime/common/runtime_types/monitor/locations.ts
+++ b/x-pack/legacy/plugins/uptime/common/runtime_types/monitor/locations.ts
@@ -10,6 +10,7 @@ import { CheckGeoType, SummaryType } from '../common';
export const MonitorLocationType = t.partial({
summary: SummaryType,
geo: CheckGeoType,
+ timestamp: t.string,
});
// Typescript type for type checking
diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap
new file mode 100644
index 0000000000000..6228183e7c2b2
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap
@@ -0,0 +1,577 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`StatusByLocation component renders when all locations are down 1`] = `
+.c3 {
+ display: inline-block;
+ margin-left: 4px;
+}
+
+.c2 {
+ font-weight: 600;
+}
+
+.c1 {
+ margin-bottom: 5px;
+}
+
+.c0 {
+ padding: 10px;
+ max-height: 229px;
+ overflow: hidden;
+}
+
+
+`;
+
+exports[`StatusByLocation component renders when all locations are up 1`] = `
+.c3 {
+ display: inline-block;
+ margin-left: 4px;
+}
+
+.c2 {
+ font-weight: 600;
+}
+
+.c1 {
+ margin-bottom: 5px;
+}
+
+.c0 {
+ padding: 10px;
+ max-height: 229px;
+ overflow: hidden;
+}
+
+
+`;
+
+exports[`StatusByLocation component renders when there are many location 1`] = `
+Array [
+ .c3 {
+ display: inline-block;
+ margin-left: 4px;
+}
+
+.c2 {
+ font-weight: 600;
+}
+
+.c1 {
+ margin-bottom: 5px;
+}
+
+.c0 {
+ padding: 10px;
+ max-height: 229px;
+ overflow: hidden;
+}
+
+,
+ .c0 {
+ padding-left: 18px;
+}
+
+,
+]
+`;
diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_status_tags.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_status_tags.test.tsx
new file mode 100644
index 0000000000000..21e5881654533
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_status_tags.test.tsx
@@ -0,0 +1,101 @@
+/*
+ * 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 moment from 'moment';
+import { renderWithIntl } from 'test_utils/enzyme_helpers';
+import { MonitorLocation } from '../../../../../common/runtime_types/monitor';
+import { LocationStatusTags } from '../';
+
+describe('StatusByLocation component', () => {
+ let monitorLocations: MonitorLocation[];
+
+ const start = moment('2020-01-10T12:22:32.567Z');
+ beforeAll(() => {
+ moment.prototype.fromNow = jest.fn((date: string) => start.from(date));
+ });
+
+ it('renders when there are many location', () => {
+ monitorLocations = [
+ {
+ summary: { up: 0, down: 1 },
+ geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:32.567Z',
+ },
+ {
+ summary: { up: 0, down: 1 },
+ geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:28.825Z',
+ },
+ {
+ summary: { up: 0, down: 1 },
+ geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:31.586Z',
+ },
+ {
+ summary: { up: 0, down: 1 },
+ geo: { name: 'Tokya', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:25.771Z',
+ },
+ {
+ summary: { up: 0, down: 1 },
+ geo: { name: 'New York', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:27.485Z',
+ },
+ {
+ summary: { up: 0, down: 1 },
+ geo: { name: 'Toronto', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:28.815Z',
+ },
+ {
+ summary: { up: 0, down: 1 },
+ geo: { name: 'Sydney', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:32.132Z',
+ },
+ {
+ summary: { up: 0, down: 1 },
+ geo: { name: 'Paris', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:32.973Z',
+ },
+ ];
+ const component = renderWithIntl();
+ expect(component).toMatchSnapshot();
+ });
+
+ it('renders when all locations are up', () => {
+ monitorLocations = [
+ {
+ summary: { up: 4, down: 0 },
+ geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:32.567Z',
+ },
+ {
+ summary: { up: 4, down: 0 },
+ geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-08T12:22:28.825Z',
+ },
+ ];
+ const component = renderWithIntl();
+ expect(component).toMatchSnapshot();
+ });
+
+ it('renders when all locations are down', () => {
+ monitorLocations = [
+ {
+ summary: { up: 0, down: 2 },
+ geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-06T12:22:32.567Z',
+ },
+ {
+ summary: { up: 0, down: 2 },
+ geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:28.825Z',
+ },
+ ];
+ const component = renderWithIntl();
+ expect(component).toMatchSnapshot();
+ });
+});
diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/index.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/index.tsx
index 1f4b88b971c4c..140d33bbeef66 100644
--- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/index.tsx
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/index.tsx
@@ -5,3 +5,4 @@
*/
export * from './location_map';
+export * from './location_status_tags';
diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_status_tags.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_status_tags.tsx
index a10d8e02e6863..6563c03ad7c34 100644
--- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_status_tags.tsx
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_status_tags.tsx
@@ -7,9 +7,16 @@
import React, { useContext } from 'react';
import styled from 'styled-components';
import { EuiBadge, EuiText } from '@elastic/eui';
+import moment from 'moment';
+import { FormattedMessage } from '@kbn/i18n/react';
import { UptimeSettingsContext } from '../../../contexts';
import { MonitorLocation } from '../../../../common/runtime_types';
+const TimeStampSpan = styled.span`
+ display: inline-block;
+ margin-left: 4px;
+`;
+
const TextStyle = styled.div`
font-weight: 600;
`;
@@ -20,54 +27,97 @@ const BadgeItem = styled.div`
const TagContainer = styled.div`
padding: 10px;
- max-height: 200px;
+ max-height: 229px;
overflow: hidden;
`;
+const OtherLocationsDiv = styled.div`
+ padding-left: 18px;
+`;
+
interface Props {
locations: MonitorLocation[];
}
+interface StatusTag {
+ label: string;
+ timestamp: number;
+}
+
export const LocationStatusTags = ({ locations }: Props) => {
const {
colors: { gray, danger },
} = useContext(UptimeSettingsContext);
- const upLocs: string[] = [];
- const downLocs: string[] = [];
+ const upLocations: StatusTag[] = [];
+ const downLocations: StatusTag[] = [];
locations.forEach((item: any) => {
if (item.summary.down === 0) {
- upLocs.push(item.geo.name);
+ upLocations.push({ label: item.geo.name, timestamp: new Date(item.timestamp).valueOf() });
} else {
- downLocs.push(item.geo.name);
+ downLocations.push({ label: item.geo.name, timestamp: new Date(item.timestamp).valueOf() });
}
});
+ // Sort by recent timestamp
+ upLocations.sort((a, b) => {
+ return a.timestamp < b.timestamp ? 1 : b.timestamp < a.timestamp ? -1 : 0;
+ });
+
+ moment.locale('en', {
+ relativeTime: {
+ future: 'in %s',
+ past: '%s ago',
+ s: '%ds',
+ ss: '%ss',
+ m: '%dm',
+ mm: '%dm',
+ h: '%dh',
+ hh: '%dh',
+ d: '%dd',
+ dd: '%dd',
+ M: '%d Mon',
+ MM: '%d Mon',
+ y: '%d Yr',
+ yy: '%d Yr',
+ },
+ });
+
+ const tagLabel = (item: StatusTag, ind: number, color: string) => (
+
+
+
+ {item.label}
+
+
+
+ {moment(item.timestamp).fromNow()}
+
+
+ );
+
return (
-
-
- {downLocs.map((item, ind) => (
-
-
-
- {item}
-
-
-
- ))}
-
-
- {upLocs.map((item, ind) => (
-
-
-
- {item}
-
-
-
- ))}
-
-
+ <>
+
+ {downLocations.map((item, ind) => tagLabel(item, ind, danger))}
+ {upLocations.map((item, ind) => tagLabel(item, ind, gray))}
+
+ {locations.length > 7 && (
+
+
+
+
+
+
+
+ )}
+ >
);
};
diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/__test__/status_by_location.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/__test__/status_by_location.test.tsx
index 4e515a52b8de6..38864103564ca 100644
--- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/__test__/status_by_location.test.tsx
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/__test__/status_by_location.test.tsx
@@ -17,6 +17,7 @@ describe('StatusByLocation component', () => {
{
summary: { up: 4, down: 0 },
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:32.567Z',
},
{
summary: { up: 4, down: 0 },
@@ -32,6 +33,7 @@ describe('StatusByLocation component', () => {
{
summary: { up: 4, down: 0 },
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:32.567Z',
},
];
const component = renderWithIntl();
@@ -43,6 +45,7 @@ describe('StatusByLocation component', () => {
{
summary: { up: 0, down: 4 },
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:32.567Z',
},
];
const component = renderWithIntl();
@@ -54,10 +57,12 @@ describe('StatusByLocation component', () => {
{
summary: { up: 0, down: 4 },
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:32.567Z',
},
{
summary: { up: 0, down: 4 },
geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:32.567Z',
},
];
const component = renderWithIntl();
@@ -69,10 +74,12 @@ describe('StatusByLocation component', () => {
{
summary: { up: 0, down: 4 },
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:32.567Z',
},
{
summary: { up: 4, down: 0 },
geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-09T12:22:32.567Z',
},
];
const component = renderWithIntl();
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 37a9e032cd442..b237fd8771f58 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
@@ -334,7 +334,7 @@ export const elasticsearchMonitorsAdapter: UMMonitorsAdapter = {
order: 'desc',
},
},
- _source: ['monitor', 'summary', 'observer'],
+ _source: ['monitor', 'summary', 'observer', '@timestamp'],
},
},
},
@@ -365,6 +365,7 @@ export const elasticsearchMonitorsAdapter: UMMonitorsAdapter = {
const location: MonitorLocation = {
summary: mostRecentLocation?.summary,
geo: getGeo(mostRecentLocation?.observer?.geo),
+ timestamp: mostRecentLocation['@timestamp'],
};
monLocs.push(location);
}