[Uptime] Most recent checks info on details page (#54340)

* update API

* update query

* hide layer control and added loc tags

* update test

* remove unused comment

* update API

* remove capitalization

* style fix

* update types

* added location status number on details page

* useref instead of createRef

* update interface

* update import

* removed redundant file

* fix header for empty data

* refactor for most recent check

* remove redundant code

* remone unused translation

* update status bar

* update styling

* update snaps

* added API tests

* fix types

* fixing integration tests and a typo

* remove unused translations

* update tests

* fixed PR feedback

* update feedback

* update messaging

* update snap

* added timestamp in front of tags

* update snaps

* improve readability

* PR feedbacka and snaps

* PR feedbacka and snaps

* update txt

* snaps

* fix timestamp issue in tests

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Shahzad 2020-01-13 20:31:28 +01:00 committed by GitHub
parent 6f3ff99968
commit e90ca93687
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 768 additions and 30 deletions

View file

@ -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

View file

@ -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;
}
<div
class="c0"
>
<span>
<div
class="c1"
>
<span
class="euiBadge euiBadge--iconLeft"
style="background-color:#bd271e;color:#FFFFFF"
>
<span
class="euiBadge__content"
>
<span
class="euiBadge__text"
>
<div
class="euiText euiText--medium"
>
<div
class="c2"
>
Islamabad
</div>
</div>
</span>
</span>
</span>
<span
class="c3"
>
<div
class="euiText euiText--medium"
>
<div
class="euiTextColor euiTextColor--subdued"
>
3d ago
</div>
</div>
</span>
</div>
<div
class="c1"
>
<span
class="euiBadge euiBadge--iconLeft"
style="background-color:#bd271e;color:#FFFFFF"
>
<span
class="euiBadge__content"
>
<span
class="euiBadge__text"
>
<div
class="euiText euiText--medium"
>
<div
class="c2"
>
Berlin
</div>
</div>
</span>
</span>
</span>
<span
class="c3"
>
<div
class="euiText euiText--medium"
>
<div
class="euiTextColor euiTextColor--subdued"
>
3d ago
</div>
</div>
</span>
</div>
</span>
<span />
</div>
`;
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;
}
<div
class="c0"
>
<span />
<span>
<div
class="c1"
>
<span
class="euiBadge euiBadge--iconLeft"
style="background-color:#d3dae6;color:#000000"
>
<span
class="euiBadge__content"
>
<span
class="euiBadge__text"
>
<div
class="euiText euiText--medium"
>
<div
class="c2"
>
Islamabad
</div>
</div>
</span>
</span>
</span>
<span
class="c3"
>
<div
class="euiText euiText--medium"
>
<div
class="euiTextColor euiTextColor--subdued"
>
3d ago
</div>
</div>
</span>
</div>
<div
class="c1"
>
<span
class="euiBadge euiBadge--iconLeft"
style="background-color:#d3dae6;color:#000000"
>
<span
class="euiBadge__content"
>
<span
class="euiBadge__text"
>
<div
class="euiText euiText--medium"
>
<div
class="c2"
>
Berlin
</div>
</div>
</span>
</span>
</span>
<span
class="c3"
>
<div
class="euiText euiText--medium"
>
<div
class="euiTextColor euiTextColor--subdued"
>
3d ago
</div>
</div>
</span>
</div>
</span>
</div>
`;
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;
}
<div
class="c0"
>
<span>
<div
class="c1"
>
<span
class="euiBadge euiBadge--iconLeft"
style="background-color:#bd271e;color:#FFFFFF"
>
<span
class="euiBadge__content"
>
<span
class="euiBadge__text"
>
<div
class="euiText euiText--medium"
>
<div
class="c2"
>
Islamabad
</div>
</div>
</span>
</span>
</span>
<span
class="c3"
>
<div
class="euiText euiText--medium"
>
<div
class="euiTextColor euiTextColor--subdued"
>
3d ago
</div>
</div>
</span>
</div>
<div
class="c1"
>
<span
class="euiBadge euiBadge--iconLeft"
style="background-color:#bd271e;color:#FFFFFF"
>
<span
class="euiBadge__content"
>
<span
class="euiBadge__text"
>
<div
class="euiText euiText--medium"
>
<div
class="c2"
>
Berlin
</div>
</div>
</span>
</span>
</span>
<span
class="c3"
>
<div
class="euiText euiText--medium"
>
<div
class="euiTextColor euiTextColor--subdued"
>
3d ago
</div>
</div>
</span>
</div>
<div
class="c1"
>
<span
class="euiBadge euiBadge--iconLeft"
style="background-color:#bd271e;color:#FFFFFF"
>
<span
class="euiBadge__content"
>
<span
class="euiBadge__text"
>
<div
class="euiText euiText--medium"
>
<div
class="c2"
>
st-paul
</div>
</div>
</span>
</span>
</span>
<span
class="c3"
>
<div
class="euiText euiText--medium"
>
<div
class="euiTextColor euiTextColor--subdued"
>
3d ago
</div>
</div>
</span>
</div>
<div
class="c1"
>
<span
class="euiBadge euiBadge--iconLeft"
style="background-color:#bd271e;color:#FFFFFF"
>
<span
class="euiBadge__content"
>
<span
class="euiBadge__text"
>
<div
class="euiText euiText--medium"
>
<div
class="c2"
>
Tokya
</div>
</div>
</span>
</span>
</span>
<span
class="c3"
>
<div
class="euiText euiText--medium"
>
<div
class="euiTextColor euiTextColor--subdued"
>
3d ago
</div>
</div>
</span>
</div>
<div
class="c1"
>
<span
class="euiBadge euiBadge--iconLeft"
style="background-color:#bd271e;color:#FFFFFF"
>
<span
class="euiBadge__content"
>
<span
class="euiBadge__text"
>
<div
class="euiText euiText--medium"
>
<div
class="c2"
>
New York
</div>
</div>
</span>
</span>
</span>
<span
class="c3"
>
<div
class="euiText euiText--medium"
>
<div
class="euiTextColor euiTextColor--subdued"
>
3d ago
</div>
</div>
</span>
</div>
<div
class="c1"
>
<span
class="euiBadge euiBadge--iconLeft"
style="background-color:#bd271e;color:#FFFFFF"
>
<span
class="euiBadge__content"
>
<span
class="euiBadge__text"
>
<div
class="euiText euiText--medium"
>
<div
class="c2"
>
Toronto
</div>
</div>
</span>
</span>
</span>
<span
class="c3"
>
<div
class="euiText euiText--medium"
>
<div
class="euiTextColor euiTextColor--subdued"
>
3d ago
</div>
</div>
</span>
</div>
<div
class="c1"
>
<span
class="euiBadge euiBadge--iconLeft"
style="background-color:#bd271e;color:#FFFFFF"
>
<span
class="euiBadge__content"
>
<span
class="euiBadge__text"
>
<div
class="euiText euiText--medium"
>
<div
class="c2"
>
Sydney
</div>
</div>
</span>
</span>
</span>
<span
class="c3"
>
<div
class="euiText euiText--medium"
>
<div
class="euiTextColor euiTextColor--subdued"
>
3d ago
</div>
</div>
</span>
</div>
<div
class="c1"
>
<span
class="euiBadge euiBadge--iconLeft"
style="background-color:#bd271e;color:#FFFFFF"
>
<span
class="euiBadge__content"
>
<span
class="euiBadge__text"
>
<div
class="euiText euiText--medium"
>
<div
class="c2"
>
Paris
</div>
</div>
</span>
</span>
</span>
<span
class="c3"
>
<div
class="euiText euiText--medium"
>
<div
class="euiTextColor euiTextColor--subdued"
>
3d ago
</div>
</div>
</span>
</div>
</span>
<span />
</div>,
.c0 {
padding-left: 18px;
}
<div
class="c0"
>
<div
class="euiText euiText--medium"
>
<div
class="euiTextColor euiTextColor--subdued"
>
<h4>
1 Others ...
</h4>
</div>
</div>
</div>,
]
`;

View file

@ -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(<LocationStatusTags locations={monitorLocations} />);
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(<LocationStatusTags locations={monitorLocations} />);
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(<LocationStatusTags locations={monitorLocations} />);
expect(component).toMatchSnapshot();
});
});

View file

@ -5,3 +5,4 @@
*/
export * from './location_map';
export * from './location_status_tags';

View file

@ -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) => (
<BadgeItem key={ind}>
<EuiBadge color={color}>
<EuiText size="m">
<TextStyle>{item.label}</TextStyle>
</EuiText>
</EuiBadge>
<TimeStampSpan>
<EuiText color="subdued">{moment(item.timestamp).fromNow()}</EuiText>
</TimeStampSpan>
</BadgeItem>
);
return (
<TagContainer>
<span>
{downLocs.map((item, ind) => (
<BadgeItem key={ind}>
<EuiBadge color={danger}>
<EuiText size="m">
<TextStyle>{item}</TextStyle>
</EuiText>
</EuiBadge>
</BadgeItem>
))}
</span>
<span>
{upLocs.map((item, ind) => (
<BadgeItem key={ind}>
<EuiBadge color={gray}>
<EuiText size="m">
<TextStyle>{item}</TextStyle>
</EuiText>
</EuiBadge>
</BadgeItem>
))}
</span>
</TagContainer>
<>
<TagContainer>
<span>{downLocations.map((item, ind) => tagLabel(item, ind, danger))}</span>
<span>{upLocations.map((item, ind) => tagLabel(item, ind, gray))}</span>
</TagContainer>
{locations.length > 7 && (
<OtherLocationsDiv>
<EuiText color="subdued">
<h4>
<FormattedMessage
id="xpack.uptime.locationMap.locations.tags.others"
defaultMessage="{otherLoc} Others ..."
values={{
otherLoc: locations.length - 7,
}}
/>
</h4>
</EuiText>
</OtherLocationsDiv>
)}
</>
);
};

View file

@ -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(<StatusByLocations locations={monitorLocations} />);
@ -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(<StatusByLocations locations={monitorLocations} />);
@ -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(<StatusByLocations locations={monitorLocations} />);
@ -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(<StatusByLocations locations={monitorLocations} />);

View file

@ -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);
}