diff --git a/x-pack/legacy/plugins/uptime/common/constants/constants.ts b/x-pack/legacy/plugins/uptime/common/constants/constants.ts
new file mode 100644
index 0000000000000..0c35bc9734486
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/common/constants/constants.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 const UNNAMED_LOCATION = 'Unnamed-location';
diff --git a/x-pack/legacy/plugins/uptime/common/constants/index.ts b/x-pack/legacy/plugins/uptime/common/constants/index.ts
index e3c4352f0a484..0a95960825f02 100644
--- a/x-pack/legacy/plugins/uptime/common/constants/index.ts
+++ b/x-pack/legacy/plugins/uptime/common/constants/index.ts
@@ -11,3 +11,4 @@ export { INDEX_NAMES } from './index_names';
export * from './capabilities';
export { PLUGIN } from './plugin';
export { QUERY, STATES } from './query';
+export * from './constants';
diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_map.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_map.test.tsx.snap
new file mode 100644
index 0000000000000..a03a89be35ec2
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_map.test.tsx.snap
@@ -0,0 +1,203 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`LocationMap component doesnt shows warning if geo is provided 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`LocationMap component renders correctly against snapshot 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`LocationMap component shows warning if geo information is missing 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap
new file mode 100644
index 0000000000000..fb949c7f3b4c2
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap
@@ -0,0 +1,123 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`LocationMissingWarning component renders correctly against snapshot 1`] = `
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`LocationMissingWarning component shallow render correctly against snapshot 1`] = `
+
+
+
+ Geo Information Missing
+
+ }
+ closePopover={[Function]}
+ display="inlineBlock"
+ hasArrow={true}
+ id="popover"
+ isOpen={false}
+ ownFocus={false}
+ panelPaddingSize="m"
+ >
+
+
+ observer.geo.??
+ ,
+ }
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+`;
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
index 37bb820adac94..437150083f76f 100644
--- 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
@@ -1,5 +1,82 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`LocationStatusTags component renders properly against props 1`] = `
+
+
+
+
+
+
+
+ Berlin
+
+
+
+
+
+ 1 Mon ago
+
+
+
+
+
+
+
+
+
+ Berlin
+
+
+
+
+
+ 1 Mon ago
+
+
+
+
+
+
+
+ Islamabad
+
+
+
+
+
+ 1 Mon ago
+
+
+
+
+
+
+`;
+
exports[`LocationStatusTags component renders when all locations are down 1`] = `
.c3 {
display: inline-block;
diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_map.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_map.test.tsx
new file mode 100644
index 0000000000000..46cb0c3d63a0e
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_map.test.tsx
@@ -0,0 +1,90 @@
+/*
+ * 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 { shallowWithIntl } from 'test_utils/enzyme_helpers';
+import { LocationMap } from '../location_map';
+import { MonitorLocations } from '../../../../../common/runtime_types';
+import { LocationMissingWarning } from '../location_missing';
+
+// Note For shallow test, we need absolute time strings
+describe('LocationMap component', () => {
+ let monitorLocations: MonitorLocations;
+
+ beforeEach(() => {
+ monitorLocations = {
+ monitorId: 'wapo',
+ locations: [
+ {
+ summary: { up: 4, down: 0 },
+ geo: { name: 'New York', location: { lat: '40.730610', lon: ' -73.935242' } },
+ timestamp: '2020-01-13T22:50:06.536Z',
+ },
+ {
+ summary: { up: 4, down: 0 },
+ geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-13T22:50:04.354Z',
+ },
+ {
+ summary: { up: 4, down: 0 },
+ geo: { name: 'Unnamed-location' },
+ timestamp: '2020-01-13T22:50:02.753Z',
+ },
+ ],
+ };
+ });
+
+ it('renders correctly against snapshot', () => {
+ const component = shallowWithIntl();
+ expect(component).toMatchSnapshot();
+ });
+
+ it('shows warning if geo information is missing', () => {
+ monitorLocations = {
+ monitorId: 'wapo',
+ locations: [
+ {
+ summary: { up: 4, down: 0 },
+ geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-13T22:50:04.354Z',
+ },
+ {
+ summary: { up: 4, down: 0 },
+ geo: { name: 'Unnamed-location' },
+ timestamp: '2020-01-13T22:50:02.753Z',
+ },
+ ],
+ };
+ const component = shallowWithIntl();
+ expect(component).toMatchSnapshot();
+
+ const warningComponent = component.find(LocationMissingWarning);
+ expect(warningComponent).toHaveLength(1);
+ });
+
+ it('doesnt shows warning if geo is provided', () => {
+ monitorLocations = {
+ monitorId: 'wapo',
+ locations: [
+ {
+ summary: { up: 4, down: 0 },
+ geo: { name: 'New York', location: { lat: '40.730610', lon: ' -73.935242' } },
+ timestamp: '2020-01-13T22:50:06.536Z',
+ },
+ {
+ summary: { up: 4, down: 0 },
+ geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: '2020-01-13T22:50:04.354Z',
+ },
+ ],
+ };
+ const component = shallowWithIntl();
+ expect(component).toMatchSnapshot();
+
+ const warningComponent = component.find(LocationMissingWarning);
+ expect(warningComponent).toHaveLength(0);
+ });
+});
diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_missing.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_missing.test.tsx
new file mode 100644
index 0000000000000..66e3b5952a3dc
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_missing.test.tsx
@@ -0,0 +1,21 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { renderWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
+import { LocationMissingWarning } from '../location_missing';
+
+describe('LocationMissingWarning component', () => {
+ it('shallow render correctly against snapshot', () => {
+ const component = shallowWithIntl();
+ expect(component).toMatchSnapshot();
+ });
+
+ it('renders correctly against snapshot', () => {
+ const component = renderWithIntl();
+ expect(component).toMatchSnapshot();
+ });
+});
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
index dcab589b794aa..2359938dbbc35 100644
--- 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
@@ -6,7 +6,7 @@
import React from 'react';
import moment from 'moment';
-import { renderWithIntl } from 'test_utils/enzyme_helpers';
+import { renderWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
import { MonitorLocation } from '../../../../../common/runtime_types/monitor';
import { LocationStatusTags } from '../';
@@ -14,6 +14,34 @@ import { LocationStatusTags } from '../';
describe('LocationStatusTags component', () => {
let monitorLocations: MonitorLocation[];
+ it('renders properly against props', () => {
+ monitorLocations = [
+ {
+ summary: { up: 4, down: 0 },
+ geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: moment()
+ .subtract('5', 'w')
+ .toISOString(),
+ },
+ {
+ summary: { up: 4, down: 0 },
+ geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: moment()
+ .subtract('5', 'w')
+ .toISOString(),
+ },
+ {
+ summary: { up: 0, down: 2 },
+ geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
+ timestamp: moment()
+ .subtract('5', 'w')
+ .toISOString(),
+ },
+ ];
+ const component = shallowWithIntl();
+ expect(component).toMatchSnapshot();
+ });
+
it('renders when there are many location', () => {
monitorLocations = [
{
diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx
index 9a9bf3fe71dc1..d35e1281260e2 100644
--- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx
@@ -6,10 +6,12 @@
import React from 'react';
import styled from 'styled-components';
-import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem, EuiErrorBoundary } from '@elastic/eui';
import { LocationStatusTags } from './location_status_tags';
import { EmbeddedMap, LocationPoint } from './embeddables/embedded_map';
import { MonitorLocations } from '../../../../common/runtime_types';
+import { UNNAMED_LOCATION } from '../../../../common/constants';
+import { LocationMissingWarning } from './location_missing';
// These height/width values are used to make sure map is in center of panel
// And to make sure, it doesn't take too much space
@@ -27,25 +29,34 @@ export const LocationMap = ({ monitorLocations }: LocationMapProps) => {
const upPoints: LocationPoint[] = [];
const downPoints: LocationPoint[] = [];
+ let isGeoInfoMissing = false;
+
if (monitorLocations?.locations) {
monitorLocations.locations.forEach((item: any) => {
- if (item.summary.down === 0) {
- upPoints.push(item.geo.location);
- } else {
- downPoints.push(item.geo.location);
+ if (item.geo?.name !== UNNAMED_LOCATION) {
+ if (item.summary.down === 0) {
+ upPoints.push(item.geo.location);
+ } else {
+ downPoints.push(item.geo.location);
+ }
+ } else if (item.geo?.name === UNNAMED_LOCATION) {
+ isGeoInfoMissing = true;
}
});
}
return (
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ {isGeoInfoMissing && }
+
+
+
+
+
+
);
};
diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_missing.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_missing.tsx
new file mode 100644
index 0000000000000..3a3319208ea3c
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_missing.tsx
@@ -0,0 +1,64 @@
+/*
+ * 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, { useState } from 'react';
+import {
+ EuiButton,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiPopover,
+ EuiSpacer,
+ EuiText,
+ EuiCode,
+} from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { LocationLink } from '../monitor_list/monitor_list_drawer';
+
+export const LocationMissingWarning = () => {
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
+
+ const togglePopover = () => {
+ setIsPopoverOpen(!isPopoverOpen);
+ };
+
+ const button = (
+
+ Geo Information Missing
+
+ );
+
+ return (
+
+
+
+
+ observer.geo.?? }}
+ />
+
+
+
+
+
+
+
+
+
+
+ );
+};
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 7a632dab5f2cc..b8735f682adef 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
@@ -25,6 +25,7 @@ const BadgeItem = styled.div`
margin-bottom: 5px;
`;
+// Set height so that it remains within panel, enough height to display 7 locations tags
const TagContainer = styled.div`
padding: 10px;
max-height: 229px;
@@ -65,7 +66,7 @@ export const LocationStatusTags = ({ locations }: Props) => {
return a.label > b.label ? 1 : b.label > a.label ? -1 : 0;
});
- moment.locale('en', {
+ moment.updateLocale('en', {
relativeTime: {
future: 'in %s',
past: '%s ago',
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
index 6934ecdcf639a..27ce47ff28b77 100644
--- 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
@@ -14,6 +14,25 @@ exports[`MonitorStatusList component renders checks 1`] = `
locationNames={Set {}}
status="up"
/>
+
+
+ ,
+ }
+ }
+ />
+
+
`;
@@ -31,5 +50,24 @@ exports[`MonitorStatusList component renders null in place of child status with
locationNames={Set {}}
status="up"
/>
+
+
+ ,
+ }
+ }
+ />
+
+
`;
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
index 66e9fce155ed2..04c5dc7d71371 100644
--- 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
@@ -11,6 +11,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { Check } from '../../../../../common/graphql/types';
import { LocationLink } from './location_link';
import { MonitorStatusRow } from './monitor_status_row';
+import { UNNAMED_LOCATION } from '../../../../../common/constants';
interface MonitorStatusListProps {
/**
@@ -21,7 +22,6 @@ interface MonitorStatusListProps {
export const UP = 'up';
export const DOWN = 'down';
-export const UNNAMED_LOCATION = 'unnamed-location';
export const MonitorStatusList = ({ checks }: MonitorStatusListProps) => {
const upChecks: Set = new Set();
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
index 66fe7fe4a5279..23f11b88517fc 100644
--- 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
@@ -8,7 +8,8 @@ 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';
+import { UP } from './monitor_status_list';
+import { UNNAMED_LOCATION } from '../../../../../common/constants';
interface MonitorStatusRowProps {
/**
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 b237fd8771f58..c86e0db9ae04a 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
@@ -5,7 +5,7 @@
*/
import { get } from 'lodash';
-import { INDEX_NAMES } from '../../../../common/constants';
+import { INDEX_NAMES, UNNAMED_LOCATION } from '../../../../common/constants';
import { MonitorChart, LocationDurationLine } from '../../../../common/graphql/types';
import { getHistogramIntervalFormatted } from '../../helper';
import { MonitorError, MonitorLocation } from '../../../../common/runtime_types';
@@ -347,28 +347,32 @@ export const elasticsearchMonitorsAdapter: UMMonitorsAdapter = {
const locations = result?.aggregations?.location?.buckets ?? [];
const getGeo = (locGeo: any) => {
- const { name, location } = locGeo;
- const latLon = location.trim().split(',');
- return {
- name,
- location: {
- lat: latLon[0],
- lon: latLon[1],
- },
- };
+ if (locGeo) {
+ const { name, location } = locGeo;
+ const latLon = location.trim().split(',');
+ return {
+ name,
+ location: {
+ lat: latLon[0],
+ lon: latLon[1],
+ },
+ };
+ } else {
+ return {
+ name: UNNAMED_LOCATION,
+ };
+ }
};
const monLocs: MonitorLocation[] = [];
locations.forEach((loc: any) => {
- if (loc?.key !== '__location_missing__') {
- const mostRecentLocation = loc.most_recent.hits.hits[0]._source;
- const location: MonitorLocation = {
- summary: mostRecentLocation?.summary,
- geo: getGeo(mostRecentLocation?.observer?.geo),
- timestamp: mostRecentLocation['@timestamp'],
- };
- monLocs.push(location);
- }
+ const mostRecentLocation = loc.most_recent.hits.hits[0]._source;
+ const location: MonitorLocation = {
+ summary: mostRecentLocation?.summary,
+ geo: getGeo(mostRecentLocation?.observer?.geo),
+ timestamp: mostRecentLocation['@timestamp'],
+ };
+ monLocs.push(location);
});
return {