mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
39b2d71ce7
commit
775cd0b42a
70 changed files with 3195 additions and 1672 deletions
|
@ -16568,7 +16568,6 @@
|
||||||
"xpack.uptime.locationMap.locations.missing.message": "重要な位置情報構成がありません。{codeBlock}フィールドを使用して、アップタイムチェック用に一意の地域を作成できます。",
|
"xpack.uptime.locationMap.locations.missing.message": "重要な位置情報構成がありません。{codeBlock}フィールドを使用して、アップタイムチェック用に一意の地域を作成できます。",
|
||||||
"xpack.uptime.locationMap.locations.missing.message1": "詳細については、ドキュメンテーションを参照してください。",
|
"xpack.uptime.locationMap.locations.missing.message1": "詳細については、ドキュメンテーションを参照してください。",
|
||||||
"xpack.uptime.locationMap.locations.missing.title": "地理情報の欠測",
|
"xpack.uptime.locationMap.locations.missing.title": "地理情報の欠測",
|
||||||
"xpack.uptime.locationMap.locations.tags.others": "{otherLoc}その他 ...",
|
|
||||||
"xpack.uptime.locationName.helpLinkAnnotation": "場所を追加",
|
"xpack.uptime.locationName.helpLinkAnnotation": "場所を追加",
|
||||||
"xpack.uptime.ml.durationChart.exploreInMlApp": "ML アプリで探索",
|
"xpack.uptime.ml.durationChart.exploreInMlApp": "ML アプリで探索",
|
||||||
"xpack.uptime.ml.enableAnomalyDetectionPanel.anomalyDetectionTitle": "異常検知",
|
"xpack.uptime.ml.enableAnomalyDetectionPanel.anomalyDetectionTitle": "異常検知",
|
||||||
|
@ -16652,12 +16651,7 @@
|
||||||
"xpack.uptime.monitorStatusBar.locations.oneLocStatus": "{loc}場所での{status}",
|
"xpack.uptime.monitorStatusBar.locations.oneLocStatus": "{loc}場所での{status}",
|
||||||
"xpack.uptime.monitorStatusBar.locations.upStatus": "{loc}場所での{status}",
|
"xpack.uptime.monitorStatusBar.locations.upStatus": "{loc}場所での{status}",
|
||||||
"xpack.uptime.monitorStatusBar.monitorUrlLinkAriaLabel": "監視 URL リンク",
|
"xpack.uptime.monitorStatusBar.monitorUrlLinkAriaLabel": "監視 URL リンク",
|
||||||
"xpack.uptime.monitorStatusBar.sslCertificate.overview": "証明書概要",
|
|
||||||
"xpack.uptime.monitorStatusBar.sslCertificate.title": "証明書",
|
"xpack.uptime.monitorStatusBar.sslCertificate.title": "証明書",
|
||||||
"xpack.uptime.monitorStatusBar.sslCertificateExpired.badgeContent": "{emphasizedText}が期限切れになりました",
|
|
||||||
"xpack.uptime.monitorStatusBar.sslCertificateExpired.label.ariaLabel": "{validityDate}に期限切れになりました",
|
|
||||||
"xpack.uptime.monitorStatusBar.sslCertificateExpiry.badgeContent": "{emphasizedText}が期限切れになります",
|
|
||||||
"xpack.uptime.monitorStatusBar.sslCertificateExpiry.label.ariaLabel": "{validityDate}に期限切れになります",
|
|
||||||
"xpack.uptime.monitorStatusBar.timestampFromNowTextAriaLabel": "最終確認からの経過時間",
|
"xpack.uptime.monitorStatusBar.timestampFromNowTextAriaLabel": "最終確認からの経過時間",
|
||||||
"xpack.uptime.navigateToAlertingButton.content": "アラートを管理",
|
"xpack.uptime.navigateToAlertingButton.content": "アラートを管理",
|
||||||
"xpack.uptime.navigateToAlertingUi": "Uptime を離れてアラート管理ページに移動します",
|
"xpack.uptime.navigateToAlertingUi": "Uptime を離れてアラート管理ページに移動します",
|
||||||
|
|
|
@ -16574,7 +16574,6 @@
|
||||||
"xpack.uptime.locationMap.locations.missing.message": "重要的地理位置配置缺失。您可以使用 {codeBlock} 字段为您的运行时间检查创建独特的地理区域。",
|
"xpack.uptime.locationMap.locations.missing.message": "重要的地理位置配置缺失。您可以使用 {codeBlock} 字段为您的运行时间检查创建独特的地理区域。",
|
||||||
"xpack.uptime.locationMap.locations.missing.message1": "在我们的文档中获取更多的信息。",
|
"xpack.uptime.locationMap.locations.missing.message1": "在我们的文档中获取更多的信息。",
|
||||||
"xpack.uptime.locationMap.locations.missing.title": "地理信息缺失",
|
"xpack.uptime.locationMap.locations.missing.title": "地理信息缺失",
|
||||||
"xpack.uptime.locationMap.locations.tags.others": "{otherLoc} 其他......",
|
|
||||||
"xpack.uptime.locationName.helpLinkAnnotation": "添加位置",
|
"xpack.uptime.locationName.helpLinkAnnotation": "添加位置",
|
||||||
"xpack.uptime.ml.durationChart.exploreInMlApp": "在 ML 应用中浏览",
|
"xpack.uptime.ml.durationChart.exploreInMlApp": "在 ML 应用中浏览",
|
||||||
"xpack.uptime.ml.enableAnomalyDetectionPanel.anomalyDetectionTitle": "异常检测",
|
"xpack.uptime.ml.enableAnomalyDetectionPanel.anomalyDetectionTitle": "异常检测",
|
||||||
|
@ -16658,12 +16657,7 @@
|
||||||
"xpack.uptime.monitorStatusBar.locations.oneLocStatus": "在 {loc} 位置{status}",
|
"xpack.uptime.monitorStatusBar.locations.oneLocStatus": "在 {loc} 位置{status}",
|
||||||
"xpack.uptime.monitorStatusBar.locations.upStatus": "在 {loc} 位置{status}",
|
"xpack.uptime.monitorStatusBar.locations.upStatus": "在 {loc} 位置{status}",
|
||||||
"xpack.uptime.monitorStatusBar.monitorUrlLinkAriaLabel": "监测 URL 链接",
|
"xpack.uptime.monitorStatusBar.monitorUrlLinkAriaLabel": "监测 URL 链接",
|
||||||
"xpack.uptime.monitorStatusBar.sslCertificate.overview": "证书概览",
|
|
||||||
"xpack.uptime.monitorStatusBar.sslCertificate.title": "证书",
|
"xpack.uptime.monitorStatusBar.sslCertificate.title": "证书",
|
||||||
"xpack.uptime.monitorStatusBar.sslCertificateExpired.badgeContent": "{emphasizedText}过期",
|
|
||||||
"xpack.uptime.monitorStatusBar.sslCertificateExpired.label.ariaLabel": "已于 {validityDate}过期",
|
|
||||||
"xpack.uptime.monitorStatusBar.sslCertificateExpiry.badgeContent": "{emphasizedText}过期",
|
|
||||||
"xpack.uptime.monitorStatusBar.sslCertificateExpiry.label.ariaLabel": "将于 {validityDate}过期",
|
|
||||||
"xpack.uptime.monitorStatusBar.timestampFromNowTextAriaLabel": "自上次检查以来经过的时间",
|
"xpack.uptime.monitorStatusBar.timestampFromNowTextAriaLabel": "自上次检查以来经过的时间",
|
||||||
"xpack.uptime.navigateToAlertingButton.content": "管理告警",
|
"xpack.uptime.navigateToAlertingButton.content": "管理告警",
|
||||||
"xpack.uptime.navigateToAlertingUi": "离开 Uptime 并前往“Alerting 管理”页面",
|
"xpack.uptime.navigateToAlertingUi": "离开 Uptime 并前往“Alerting 管理”页面",
|
||||||
|
|
|
@ -6,15 +6,19 @@
|
||||||
|
|
||||||
import * as t from 'io-ts';
|
import * as t from 'io-ts';
|
||||||
|
|
||||||
export const LocationType = t.partial({
|
export const LocationType = t.type({
|
||||||
lat: t.string,
|
lat: t.string,
|
||||||
lon: t.string,
|
lon: t.string,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const CheckGeoType = t.partial({
|
export const CheckGeoType = t.intersection([
|
||||||
name: t.string,
|
t.type({
|
||||||
location: LocationType,
|
name: t.string,
|
||||||
});
|
}),
|
||||||
|
t.partial({
|
||||||
|
location: LocationType,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
export const SummaryType = t.partial({
|
export const SummaryType = t.partial({
|
||||||
up: t.number,
|
up: t.number,
|
||||||
|
@ -34,5 +38,6 @@ export const DateRangeType = t.type({
|
||||||
|
|
||||||
export type Summary = t.TypeOf<typeof SummaryType>;
|
export type Summary = t.TypeOf<typeof SummaryType>;
|
||||||
export type Location = t.TypeOf<typeof LocationType>;
|
export type Location = t.TypeOf<typeof LocationType>;
|
||||||
|
export type GeoPoint = t.TypeOf<typeof CheckGeoType>;
|
||||||
export type StatesIndexStatus = t.TypeOf<typeof StatesIndexStatusType>;
|
export type StatesIndexStatus = t.TypeOf<typeof StatesIndexStatusType>;
|
||||||
export type DateRange = t.TypeOf<typeof DateRangeType>;
|
export type DateRange = t.TypeOf<typeof DateRangeType>;
|
||||||
|
|
|
@ -7,17 +7,23 @@ import * as t from 'io-ts';
|
||||||
import { CheckGeoType, SummaryType } from '../common';
|
import { CheckGeoType, SummaryType } from '../common';
|
||||||
|
|
||||||
// IO type for validation
|
// IO type for validation
|
||||||
export const MonitorLocationType = t.partial({
|
export const MonitorLocationType = t.type({
|
||||||
|
up_history: t.number,
|
||||||
|
down_history: t.number,
|
||||||
|
timestamp: t.string,
|
||||||
summary: SummaryType,
|
summary: SummaryType,
|
||||||
geo: CheckGeoType,
|
geo: CheckGeoType,
|
||||||
timestamp: t.string,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Typescript type for type checking
|
// Typescript type for type checking
|
||||||
export type MonitorLocation = t.TypeOf<typeof MonitorLocationType>;
|
export type MonitorLocation = t.TypeOf<typeof MonitorLocationType>;
|
||||||
|
|
||||||
export const MonitorLocationsType = t.intersection([
|
export const MonitorLocationsType = t.intersection([
|
||||||
t.type({ monitorId: t.string }),
|
t.type({
|
||||||
|
monitorId: t.string,
|
||||||
|
up_history: t.number,
|
||||||
|
down_history: t.number,
|
||||||
|
}),
|
||||||
t.partial({ locations: t.array(MonitorLocationType) }),
|
t.partial({ locations: t.array(MonitorLocationType) }),
|
||||||
]);
|
]);
|
||||||
export type MonitorLocations = t.TypeOf<typeof MonitorLocationsType>;
|
export type MonitorLocations = t.TypeOf<typeof MonitorLocationsType>;
|
||||||
|
|
|
@ -18,6 +18,10 @@ export const EXPIRES_SOON = i18n.translate('xpack.uptime.certs.expireSoon', {
|
||||||
defaultMessage: 'Expires soon',
|
defaultMessage: 'Expires soon',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const EXPIRES = i18n.translate('xpack.uptime.certs.expires', {
|
||||||
|
defaultMessage: 'Expires',
|
||||||
|
});
|
||||||
|
|
||||||
export const SEARCH_CERTS = i18n.translate('xpack.uptime.certs.searchCerts', {
|
export const SEARCH_CERTS = i18n.translate('xpack.uptime.certs.searchCerts', {
|
||||||
defaultMessage: 'Search certificates',
|
defaultMessage: 'Search certificates',
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* 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 { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
|
export const URL_LABEL = i18n.translate('xpack.uptime.monitorList.table.url.name', {
|
||||||
|
defaultMessage: 'Url',
|
||||||
|
});
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
export * from './ml';
|
export * from './ml';
|
||||||
export * from './ping_list';
|
export * from './ping_list';
|
||||||
export * from './location_map';
|
export * from './status_details/location_map';
|
||||||
export * from './monitor_status_details';
|
export * from './status_details';
|
||||||
export * from './ping_histogram';
|
export * from './ping_histogram';
|
||||||
export * from './monitor_charts';
|
export * from './monitor_charts';
|
||||||
|
|
|
@ -1,282 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`LocationMap component doesnt shows warning if geo is provided 1`] = `
|
|
||||||
<EuiErrorBoundary>
|
|
||||||
<Styled(EuiFlexGroup)
|
|
||||||
gutterSize="none"
|
|
||||||
justifyContent="flexEnd"
|
|
||||||
wrap={true}
|
|
||||||
>
|
|
||||||
<Styled(EuiFlexItem)>
|
|
||||||
<LocationStatusTags
|
|
||||||
locations={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"geo": Object {
|
|
||||||
"location": Object {
|
|
||||||
"lat": "40.730610",
|
|
||||||
"lon": " -73.935242",
|
|
||||||
},
|
|
||||||
"name": "New York",
|
|
||||||
},
|
|
||||||
"summary": Object {
|
|
||||||
"down": 0,
|
|
||||||
"up": 4,
|
|
||||||
},
|
|
||||||
"timestamp": "2020-01-13T22:50:06.536Z",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"geo": Object {
|
|
||||||
"location": Object {
|
|
||||||
"lat": "52.487448",
|
|
||||||
"lon": " 13.394798",
|
|
||||||
},
|
|
||||||
"name": "Tokyo",
|
|
||||||
},
|
|
||||||
"summary": Object {
|
|
||||||
"down": 0,
|
|
||||||
"up": 4,
|
|
||||||
},
|
|
||||||
"timestamp": "2020-01-13T22:50:04.354Z",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Styled(EuiFlexItem)>
|
|
||||||
<EuiHideFor
|
|
||||||
sizes={
|
|
||||||
Array [
|
|
||||||
"xs",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<EuiFlexItem
|
|
||||||
grow={false}
|
|
||||||
>
|
|
||||||
<styled.div>
|
|
||||||
<EmbeddedMap
|
|
||||||
downPoints={Array []}
|
|
||||||
upPoints={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"lat": "40.730610",
|
|
||||||
"lon": " -73.935242",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"lat": "52.487448",
|
|
||||||
"lon": " 13.394798",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</styled.div>
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiHideFor>
|
|
||||||
</Styled(EuiFlexGroup)>
|
|
||||||
</EuiErrorBoundary>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`LocationMap component renders correctly against snapshot 1`] = `
|
|
||||||
<EuiErrorBoundary>
|
|
||||||
<Styled(EuiFlexGroup)
|
|
||||||
gutterSize="none"
|
|
||||||
justifyContent="flexEnd"
|
|
||||||
wrap={true}
|
|
||||||
>
|
|
||||||
<Styled(EuiFlexItem)>
|
|
||||||
<LocationStatusTags
|
|
||||||
locations={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"geo": Object {
|
|
||||||
"location": Object {
|
|
||||||
"lat": "40.730610",
|
|
||||||
"lon": " -73.935242",
|
|
||||||
},
|
|
||||||
"name": "New York",
|
|
||||||
},
|
|
||||||
"summary": Object {
|
|
||||||
"down": 0,
|
|
||||||
"up": 4,
|
|
||||||
},
|
|
||||||
"timestamp": "2020-01-13T22:50:06.536Z",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"geo": Object {
|
|
||||||
"location": Object {
|
|
||||||
"lat": "52.487448",
|
|
||||||
"lon": " 13.394798",
|
|
||||||
},
|
|
||||||
"name": "Tokyo",
|
|
||||||
},
|
|
||||||
"summary": Object {
|
|
||||||
"down": 0,
|
|
||||||
"up": 4,
|
|
||||||
},
|
|
||||||
"timestamp": "2020-01-13T22:50:04.354Z",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"geo": Object {
|
|
||||||
"name": "Unnamed-location",
|
|
||||||
},
|
|
||||||
"summary": Object {
|
|
||||||
"down": 0,
|
|
||||||
"up": 4,
|
|
||||||
},
|
|
||||||
"timestamp": "2020-01-13T22:50:02.753Z",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Styled(EuiFlexItem)>
|
|
||||||
<EuiHideFor
|
|
||||||
sizes={
|
|
||||||
Array [
|
|
||||||
"xs",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<EuiFlexItem
|
|
||||||
grow={false}
|
|
||||||
>
|
|
||||||
<LocationMissingWarning />
|
|
||||||
<styled.div>
|
|
||||||
<EmbeddedMap
|
|
||||||
downPoints={Array []}
|
|
||||||
upPoints={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"lat": "40.730610",
|
|
||||||
"lon": " -73.935242",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"lat": "52.487448",
|
|
||||||
"lon": " 13.394798",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</styled.div>
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiHideFor>
|
|
||||||
</Styled(EuiFlexGroup)>
|
|
||||||
</EuiErrorBoundary>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`LocationMap component renders named locations that have missing geo data 1`] = `
|
|
||||||
<EuiErrorBoundary>
|
|
||||||
<Styled(EuiFlexGroup)
|
|
||||||
gutterSize="none"
|
|
||||||
justifyContent="flexEnd"
|
|
||||||
wrap={true}
|
|
||||||
>
|
|
||||||
<Styled(EuiFlexItem)>
|
|
||||||
<LocationStatusTags
|
|
||||||
locations={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"geo": Object {
|
|
||||||
"location": undefined,
|
|
||||||
"name": "New York",
|
|
||||||
},
|
|
||||||
"summary": Object {
|
|
||||||
"down": 0,
|
|
||||||
"up": 4,
|
|
||||||
},
|
|
||||||
"timestamp": "2020-01-13T22:50:06.536Z",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Styled(EuiFlexItem)>
|
|
||||||
<EuiHideFor
|
|
||||||
sizes={
|
|
||||||
Array [
|
|
||||||
"xs",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<EuiFlexItem
|
|
||||||
grow={false}
|
|
||||||
>
|
|
||||||
<LocationMissingWarning />
|
|
||||||
<styled.div>
|
|
||||||
<EmbeddedMap
|
|
||||||
downPoints={Array []}
|
|
||||||
upPoints={Array []}
|
|
||||||
/>
|
|
||||||
</styled.div>
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiHideFor>
|
|
||||||
</Styled(EuiFlexGroup)>
|
|
||||||
</EuiErrorBoundary>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`LocationMap component shows warning if geo information is missing 1`] = `
|
|
||||||
<EuiErrorBoundary>
|
|
||||||
<Styled(EuiFlexGroup)
|
|
||||||
gutterSize="none"
|
|
||||||
justifyContent="flexEnd"
|
|
||||||
wrap={true}
|
|
||||||
>
|
|
||||||
<Styled(EuiFlexItem)>
|
|
||||||
<LocationStatusTags
|
|
||||||
locations={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"geo": Object {
|
|
||||||
"location": Object {
|
|
||||||
"lat": "52.487448",
|
|
||||||
"lon": " 13.394798",
|
|
||||||
},
|
|
||||||
"name": "Tokyo",
|
|
||||||
},
|
|
||||||
"summary": Object {
|
|
||||||
"down": 0,
|
|
||||||
"up": 4,
|
|
||||||
},
|
|
||||||
"timestamp": "2020-01-13T22:50:04.354Z",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"geo": Object {
|
|
||||||
"name": "Unnamed-location",
|
|
||||||
},
|
|
||||||
"summary": Object {
|
|
||||||
"down": 0,
|
|
||||||
"up": 4,
|
|
||||||
},
|
|
||||||
"timestamp": "2020-01-13T22:50:02.753Z",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Styled(EuiFlexItem)>
|
|
||||||
<EuiHideFor
|
|
||||||
sizes={
|
|
||||||
Array [
|
|
||||||
"xs",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<EuiFlexItem
|
|
||||||
grow={false}
|
|
||||||
>
|
|
||||||
<LocationMissingWarning />
|
|
||||||
<styled.div>
|
|
||||||
<EmbeddedMap
|
|
||||||
downPoints={Array []}
|
|
||||||
upPoints={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"lat": "52.487448",
|
|
||||||
"lon": " 13.394798",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</styled.div>
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiHideFor>
|
|
||||||
</Styled(EuiFlexGroup)>
|
|
||||||
</EuiErrorBoundary>
|
|
||||||
`;
|
|
|
@ -1,682 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`LocationStatusTags component renders properly against props 1`] = `
|
|
||||||
<Fragment>
|
|
||||||
<styled.div>
|
|
||||||
<span>
|
|
||||||
<styled.div
|
|
||||||
key="0"
|
|
||||||
>
|
|
||||||
<EuiBadge
|
|
||||||
color="#bd271e"
|
|
||||||
>
|
|
||||||
<EuiText
|
|
||||||
size="m"
|
|
||||||
>
|
|
||||||
<styled.div>
|
|
||||||
Berlin
|
|
||||||
</styled.div>
|
|
||||||
</EuiText>
|
|
||||||
</EuiBadge>
|
|
||||||
<styled.span>
|
|
||||||
<EuiText
|
|
||||||
color="subdued"
|
|
||||||
>
|
|
||||||
1 Mon ago
|
|
||||||
</EuiText>
|
|
||||||
</styled.span>
|
|
||||||
</styled.div>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<styled.div
|
|
||||||
key="0"
|
|
||||||
>
|
|
||||||
<EuiBadge
|
|
||||||
color="#d3dae6"
|
|
||||||
>
|
|
||||||
<EuiText
|
|
||||||
size="m"
|
|
||||||
>
|
|
||||||
<styled.div>
|
|
||||||
Berlin
|
|
||||||
</styled.div>
|
|
||||||
</EuiText>
|
|
||||||
</EuiBadge>
|
|
||||||
<styled.span>
|
|
||||||
<EuiText
|
|
||||||
color="subdued"
|
|
||||||
>
|
|
||||||
1 Mon ago
|
|
||||||
</EuiText>
|
|
||||||
</styled.span>
|
|
||||||
</styled.div>
|
|
||||||
<styled.div
|
|
||||||
key="1"
|
|
||||||
>
|
|
||||||
<EuiBadge
|
|
||||||
color="#d3dae6"
|
|
||||||
>
|
|
||||||
<EuiText
|
|
||||||
size="m"
|
|
||||||
>
|
|
||||||
<styled.div>
|
|
||||||
Islamabad
|
|
||||||
</styled.div>
|
|
||||||
</EuiText>
|
|
||||||
</EuiBadge>
|
|
||||||
<styled.span>
|
|
||||||
<EuiText
|
|
||||||
color="subdued"
|
|
||||||
>
|
|
||||||
1 Mon ago
|
|
||||||
</EuiText>
|
|
||||||
</styled.span>
|
|
||||||
</styled.div>
|
|
||||||
</span>
|
|
||||||
</styled.div>
|
|
||||||
</Fragment>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`LocationStatusTags component renders when all locations are down 1`] = `
|
|
||||||
.c3 {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c2 {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c1 {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c0 {
|
|
||||||
max-height: 229px;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width:1042px) {
|
|
||||||
.c1 {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="c0"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
<div
|
|
||||||
class="c1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge euiBadge--iconLeft"
|
|
||||||
style="background-color:#bd271e;color:#fff"
|
|
||||||
>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
5s ago
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="c1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge euiBadge--iconLeft"
|
|
||||||
style="background-color:#bd271e;color:#fff"
|
|
||||||
>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
5m ago
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
<span />
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`LocationStatusTags component renders when all locations are up 1`] = `
|
|
||||||
.c3 {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c2 {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c1 {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c0 {
|
|
||||||
max-height: 229px;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width:1042px) {
|
|
||||||
.c1 {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="c0"
|
|
||||||
>
|
|
||||||
<span />
|
|
||||||
<span>
|
|
||||||
<div
|
|
||||||
class="c1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge euiBadge--iconLeft"
|
|
||||||
style="background-color:#d3dae6;color:#000"
|
|
||||||
>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
5d ago
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="c1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge euiBadge--iconLeft"
|
|
||||||
style="background-color:#d3dae6;color:#000"
|
|
||||||
>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
5s ago
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`LocationStatusTags component renders when there are many location 1`] = `
|
|
||||||
Array [
|
|
||||||
.c3 {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c2 {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c1 {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c0 {
|
|
||||||
max-height: 229px;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width:1042px) {
|
|
||||||
.c1 {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="c0"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
<div
|
|
||||||
class="c1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge euiBadge--iconLeft"
|
|
||||||
style="background-color:#bd271e;color:#fff"
|
|
||||||
>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
5s ago
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="c1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge euiBadge--iconLeft"
|
|
||||||
style="background-color:#bd271e;color:#fff"
|
|
||||||
>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
5m ago
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="c1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge euiBadge--iconLeft"
|
|
||||||
style="background-color:#bd271e;color:#fff"
|
|
||||||
>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
5h ago
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="c1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge euiBadge--iconLeft"
|
|
||||||
style="background-color:#bd271e;color:#fff"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge__content"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge__text"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="euiText euiText--medium"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="c2"
|
|
||||||
>
|
|
||||||
Tokyo
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="c3"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="euiText euiText--medium"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="euiTextColor euiTextColor--subdued"
|
|
||||||
>
|
|
||||||
5d ago
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="c1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge euiBadge--iconLeft"
|
|
||||||
style="background-color:#bd271e;color:#fff"
|
|
||||||
>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
1 Mon ago
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="c1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge euiBadge--iconLeft"
|
|
||||||
style="background-color:#bd271e;color:#fff"
|
|
||||||
>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
5 Mon ago
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="c1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge euiBadge--iconLeft"
|
|
||||||
style="background-color:#bd271e;color:#fff"
|
|
||||||
>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
5 Yr ago
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="c1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge euiBadge--iconLeft"
|
|
||||||
style="background-color:#bd271e;color:#fff"
|
|
||||||
>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
5 Yr ago
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
<span />
|
|
||||||
</div>,
|
|
||||||
.c0 {
|
|
||||||
padding-left: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width:1042px) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="c0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="euiText euiText--medium"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="euiTextColor euiTextColor--subdued"
|
|
||||||
>
|
|
||||||
<h4>
|
|
||||||
1 Others ...
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>,
|
|
||||||
]
|
|
||||||
`;
|
|
|
@ -1,95 +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 styled from 'styled-components';
|
|
||||||
import { EuiFlexGroup, EuiFlexItem, EuiErrorBoundary, EuiHideFor } from '@elastic/eui';
|
|
||||||
import { LocationStatusTags } from './location_status_tags';
|
|
||||||
import { EmbeddedMap, LocationPoint } from './embeddables/embedded_map';
|
|
||||||
import { MonitorLocations, MonitorLocation } 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
|
|
||||||
const MapPanel = styled.div`
|
|
||||||
height: 240px;
|
|
||||||
width: 520px;
|
|
||||||
@media (min-width: 1300px) {
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
@media (max-width: 574px) {
|
|
||||||
height: 250px;
|
|
||||||
width: 100%;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const EuiFlexItemTags = styled(EuiFlexItem)`
|
|
||||||
padding-top: 5px;
|
|
||||||
@media (max-width: 1042px) {
|
|
||||||
flex-basis: 80% !important;
|
|
||||||
flex-grow: 0 !important;
|
|
||||||
order: 1;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const FlexGroup = styled(EuiFlexGroup)`
|
|
||||||
@media (max-width: 850px) {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface LocationMapProps {
|
|
||||||
monitorLocations: MonitorLocations;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const LocationMap = ({ monitorLocations }: LocationMapProps) => {
|
|
||||||
const upPoints: LocationPoint[] = [];
|
|
||||||
const downPoints: LocationPoint[] = [];
|
|
||||||
|
|
||||||
let isGeoInfoMissing = false;
|
|
||||||
|
|
||||||
if (monitorLocations?.locations) {
|
|
||||||
monitorLocations.locations.forEach((item: MonitorLocation) => {
|
|
||||||
if (item.geo?.name === UNNAMED_LOCATION || !item.geo?.location) {
|
|
||||||
isGeoInfoMissing = true;
|
|
||||||
} else if (
|
|
||||||
item.geo?.name !== UNNAMED_LOCATION &&
|
|
||||||
!!item.geo.location.lat &&
|
|
||||||
!!item.geo.location.lon
|
|
||||||
) {
|
|
||||||
// TypeScript doesn't infer that the above checks in this block's condition
|
|
||||||
// ensure that lat and lon are defined when we try to pass the location object directly,
|
|
||||||
// but if we destructure the values it does. Improvement to this block is welcome.
|
|
||||||
const { lat, lon } = item.geo.location;
|
|
||||||
if (item?.summary?.down === 0) {
|
|
||||||
upPoints.push({ lat, lon });
|
|
||||||
} else {
|
|
||||||
downPoints.push({ lat, lon });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<EuiErrorBoundary>
|
|
||||||
<FlexGroup wrap={true} gutterSize="none" justifyContent="flexEnd">
|
|
||||||
<EuiFlexItemTags>
|
|
||||||
<LocationStatusTags locations={monitorLocations?.locations || []} />
|
|
||||||
</EuiFlexItemTags>
|
|
||||||
<EuiHideFor sizes={['xs']}>
|
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
{isGeoInfoMissing && <LocationMissingWarning />}
|
|
||||||
<MapPanel>
|
|
||||||
<EmbeddedMap upPoints={upPoints} downPoints={downPoints} />
|
|
||||||
</MapPanel>
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiHideFor>
|
|
||||||
</FlexGroup>
|
|
||||||
</EuiErrorBoundary>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,130 +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, { useContext } from 'react';
|
|
||||||
import moment from 'moment';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { EuiBadge, EuiText } from '@elastic/eui';
|
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
|
||||||
import { UptimeThemeContext } from '../../../contexts';
|
|
||||||
import { MonitorLocation } from '../../../../common/runtime_types';
|
|
||||||
import { SHORT_TIMESPAN_LOCALE, SHORT_TS_LOCALE } from '../../../../common/constants';
|
|
||||||
|
|
||||||
const TimeStampSpan = styled.span`
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 4px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const TextStyle = styled.div`
|
|
||||||
font-weight: 600;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const BadgeItem = styled.div`
|
|
||||||
margin-bottom: 5px;
|
|
||||||
white-space: nowrap;
|
|
||||||
@media (max-width: 1042px) {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Set height so that it remains within panel, enough height to display 7 locations tags
|
|
||||||
const TagContainer = styled.div`
|
|
||||||
max-height: 229px;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-top: auto;
|
|
||||||
`;
|
|
||||||
|
|
||||||
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(UptimeThemeContext);
|
|
||||||
|
|
||||||
const upLocations: StatusTag[] = [];
|
|
||||||
const downLocations: StatusTag[] = [];
|
|
||||||
|
|
||||||
locations.forEach((item: any) => {
|
|
||||||
if (item.summary.down === 0) {
|
|
||||||
upLocations.push({ label: item.geo.name, timestamp: new Date(item.timestamp).valueOf() });
|
|
||||||
} else {
|
|
||||||
downLocations.push({ label: item.geo.name, timestamp: new Date(item.timestamp).valueOf() });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sort lexicographically by label
|
|
||||||
upLocations.sort((a, b) => {
|
|
||||||
return a.label > b.label ? 1 : b.label > a.label ? -1 : 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
const tagLabel = (item: StatusTag, ind: number, color: string) => {
|
|
||||||
return (
|
|
||||||
<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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const prevLocal: string = moment.locale() ?? 'en';
|
|
||||||
|
|
||||||
const renderTags = () => {
|
|
||||||
const shortLocale = moment.locale(SHORT_TS_LOCALE) === SHORT_TS_LOCALE;
|
|
||||||
if (!shortLocale) {
|
|
||||||
moment.defineLocale(SHORT_TS_LOCALE, SHORT_TIMESPAN_LOCALE);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tags = (
|
|
||||||
<TagContainer>
|
|
||||||
<span>{downLocations.map((item, ind) => tagLabel(item, ind, danger))}</span>
|
|
||||||
<span>{upLocations.map((item, ind) => tagLabel(item, ind, gray))}</span>
|
|
||||||
</TagContainer>
|
|
||||||
);
|
|
||||||
|
|
||||||
// Need to reset locale so it doesn't effect other parts of the app
|
|
||||||
moment.locale(prevLocal);
|
|
||||||
return tags;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{renderTags()}
|
|
||||||
{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>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,55 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`MonitorStatusBar component renders duration in ms, not us 1`] = `
|
|
||||||
<div
|
|
||||||
class="euiFlexGroup euiFlexGroup--directionColumn"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="euiText euiText--medium"
|
|
||||||
>
|
|
||||||
<h2>
|
|
||||||
Up in 2 Locations
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="euiText euiText--medium"
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
aria-label="Monitor URL link"
|
|
||||||
class="euiLink euiLink--primary"
|
|
||||||
href="https://www.example.com/"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
https://www.example.com/
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="euiFlexItem"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiTextColor euiTextColor--subdued euiTitle euiTitle--xsmall"
|
|
||||||
>
|
|
||||||
<h1
|
|
||||||
data-test-subj="monitor-page-title"
|
|
||||||
>
|
|
||||||
id1
|
|
||||||
</h1>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="euiSpacer euiSpacer--l"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
`;
|
|
|
@ -1,100 +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 moment from 'moment';
|
|
||||||
import { i18n } from '@kbn/i18n';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { EuiSpacer, EuiText, EuiBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
|
||||||
import { Tls } from '../../../../../common/runtime_types';
|
|
||||||
import { useCertStatus } from '../../../../hooks';
|
|
||||||
import { CERT_STATUS, CERTIFICATES_ROUTE } from '../../../../../common/constants';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
/**
|
|
||||||
* TLS information coming from monitor in ES heartbeat index
|
|
||||||
*/
|
|
||||||
tls: Tls | null | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const MonitorSSLCertificate = ({ tls }: Props) => {
|
|
||||||
const certStatus = useCertStatus(tls?.not_after);
|
|
||||||
|
|
||||||
const isExpiringSoon = certStatus === CERT_STATUS.EXPIRING_SOON;
|
|
||||||
|
|
||||||
const isExpired = certStatus === CERT_STATUS.EXPIRED;
|
|
||||||
|
|
||||||
const relativeDate = moment(tls?.not_after).fromNow();
|
|
||||||
|
|
||||||
return certStatus ? (
|
|
||||||
<>
|
|
||||||
<EuiText>
|
|
||||||
{i18n.translate('xpack.uptime.monitorStatusBar.sslCertificate.title', {
|
|
||||||
defaultMessage: 'Certificate:',
|
|
||||||
})}
|
|
||||||
</EuiText>
|
|
||||||
<EuiSpacer size="s" />
|
|
||||||
<EuiFlexGroup wrap>
|
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
<EuiText
|
|
||||||
className="eui-displayInline"
|
|
||||||
grow={false}
|
|
||||||
size="s"
|
|
||||||
aria-label={
|
|
||||||
isExpired
|
|
||||||
? i18n.translate(
|
|
||||||
'xpack.uptime.monitorStatusBar.sslCertificateExpired.label.ariaLabel',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Expired {validityDate}',
|
|
||||||
values: { validityDate: relativeDate },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
: i18n.translate(
|
|
||||||
'xpack.uptime.monitorStatusBar.sslCertificateExpiry.label.ariaLabel',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Expires {validityDate}',
|
|
||||||
values: { validityDate: relativeDate },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{isExpired ? (
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.uptime.monitorStatusBar.sslCertificateExpired.badgeContent"
|
|
||||||
defaultMessage="Expired {emphasizedText}"
|
|
||||||
values={{
|
|
||||||
emphasizedText: <EuiBadge color={'danger'}>{relativeDate}</EuiBadge>,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.uptime.monitorStatusBar.sslCertificateExpiry.badgeContent"
|
|
||||||
defaultMessage="Expires {emphasizedText}"
|
|
||||||
values={{
|
|
||||||
emphasizedText: (
|
|
||||||
<EuiBadge color={isExpiringSoon ? 'warning' : 'default'}>
|
|
||||||
{relativeDate}
|
|
||||||
</EuiBadge>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</EuiText>
|
|
||||||
</EuiFlexItem>
|
|
||||||
<EuiFlexItem>
|
|
||||||
<Link to={CERTIFICATES_ROUTE} className="eui-displayInline">
|
|
||||||
<EuiText style={{ whiteSpace: 'nowrap' }}>
|
|
||||||
{i18n.translate('xpack.uptime.monitorStatusBar.sslCertificate.overview', {
|
|
||||||
defaultMessage: 'Certificate overview',
|
|
||||||
})}
|
|
||||||
</EuiText>
|
|
||||||
</Link>
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiFlexGroup>
|
|
||||||
</>
|
|
||||||
) : null;
|
|
||||||
};
|
|
|
@ -1,61 +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 {
|
|
||||||
EuiLink,
|
|
||||||
EuiTitle,
|
|
||||||
EuiTextColor,
|
|
||||||
EuiSpacer,
|
|
||||||
EuiText,
|
|
||||||
EuiFlexGroup,
|
|
||||||
EuiFlexItem,
|
|
||||||
} from '@elastic/eui';
|
|
||||||
import { MonitorSSLCertificate } from './ssl_certificate';
|
|
||||||
import * as labels from './translations';
|
|
||||||
import { StatusByLocations } from './status_by_location';
|
|
||||||
import { Ping } from '../../../../../common/runtime_types';
|
|
||||||
import { MonitorLocations } from '../../../../../common/runtime_types';
|
|
||||||
|
|
||||||
interface MonitorStatusBarProps {
|
|
||||||
monitorId: string;
|
|
||||||
monitorStatus: Ping | null;
|
|
||||||
monitorLocations: MonitorLocations;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const MonitorStatusBarComponent: React.FC<MonitorStatusBarProps> = ({
|
|
||||||
monitorId,
|
|
||||||
monitorStatus,
|
|
||||||
monitorLocations,
|
|
||||||
}) => {
|
|
||||||
const full = monitorStatus?.url?.full ?? '';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<EuiFlexGroup direction="column" gutterSize="none" responsive={false}>
|
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
<StatusByLocations locations={monitorLocations?.locations ?? []} />
|
|
||||||
</EuiFlexItem>
|
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
<EuiText>
|
|
||||||
<EuiLink aria-label={labels.monitorUrlLinkAriaLabel} href={full} target="_blank">
|
|
||||||
{full}
|
|
||||||
</EuiLink>
|
|
||||||
</EuiText>
|
|
||||||
</EuiFlexItem>
|
|
||||||
<EuiFlexItem>
|
|
||||||
<EuiTitle size="xs">
|
|
||||||
<EuiTextColor color="subdued">
|
|
||||||
<h1 data-test-subj="monitor-page-title">{monitorId}</h1>
|
|
||||||
</EuiTextColor>
|
|
||||||
</EuiTitle>
|
|
||||||
</EuiFlexItem>
|
|
||||||
<EuiSpacer />
|
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
<MonitorSSLCertificate tls={monitorStatus?.tls} />
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiFlexGroup>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,50 +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 { i18n } from '@kbn/i18n';
|
|
||||||
|
|
||||||
export const healthStatusMessageAriaLabel = i18n.translate(
|
|
||||||
'xpack.uptime.monitorStatusBar.healthStatusMessageAriaLabel',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Monitor status',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const upLabel = i18n.translate('xpack.uptime.monitorStatusBar.healthStatusMessage.upLabel', {
|
|
||||||
defaultMessage: 'Up',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const downLabel = i18n.translate(
|
|
||||||
'xpack.uptime.monitorStatusBar.healthStatusMessage.downLabel',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Down',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const monitorUrlLinkAriaLabel = i18n.translate(
|
|
||||||
'xpack.uptime.monitorStatusBar.monitorUrlLinkAriaLabel',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Monitor URL link',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const durationTextAriaLabel = i18n.translate(
|
|
||||||
'xpack.uptime.monitorStatusBar.durationTextAriaLabel',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Monitor duration in milliseconds',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const timestampFromNowTextAriaLabel = i18n.translate(
|
|
||||||
'xpack.uptime.monitorStatusBar.timestampFromNowTextAriaLabel',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Time since last check',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const loadingMessage = i18n.translate('xpack.uptime.monitorStatusBar.loadingMessage', {
|
|
||||||
defaultMessage: 'Loading…',
|
|
||||||
});
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`MonitorStatusBar component renders 1`] = `
|
||||||
|
Array [
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="euiText euiText--medium"
|
||||||
|
>
|
||||||
|
<h2>
|
||||||
|
Up in 2 Locations
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
<div
|
||||||
|
class="euiSpacer euiSpacer--l"
|
||||||
|
/>,
|
||||||
|
.c0.c0.c0 {
|
||||||
|
width: 35%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c1.c1.c1 {
|
||||||
|
width: 65%;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
<dl
|
||||||
|
class="euiDescriptionList euiDescriptionList--column euiDescriptionList--reverse euiDescriptionList--compressed"
|
||||||
|
style="max-width:450px"
|
||||||
|
>
|
||||||
|
<dt
|
||||||
|
class="euiDescriptionList__title c0"
|
||||||
|
>
|
||||||
|
Overall availability
|
||||||
|
</dt>
|
||||||
|
<dd
|
||||||
|
class="euiDescriptionList__description c1"
|
||||||
|
data-test-subj="uptimeOverallAvailability"
|
||||||
|
>
|
||||||
|
0.00 %
|
||||||
|
</dd>
|
||||||
|
<dt
|
||||||
|
class="euiDescriptionList__title c0"
|
||||||
|
>
|
||||||
|
Url
|
||||||
|
</dt>
|
||||||
|
<dd
|
||||||
|
class="euiDescriptionList__description c1"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
aria-label="Monitor URL link"
|
||||||
|
class="euiLink euiLink--primary"
|
||||||
|
href=""
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
|
||||||
|
<div
|
||||||
|
data-euiicon-type="popout"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</dd>
|
||||||
|
<dt
|
||||||
|
class="euiDescriptionList__title c0"
|
||||||
|
>
|
||||||
|
Monitor ID
|
||||||
|
</dt>
|
||||||
|
<dd
|
||||||
|
class="euiDescriptionList__description c1"
|
||||||
|
data-test-subj="monitor-page-title"
|
||||||
|
/>
|
||||||
|
</dl>,
|
||||||
|
]
|
||||||
|
`;
|
|
@ -2,61 +2,91 @@
|
||||||
|
|
||||||
exports[`SSL Certificate component renders 1`] = `
|
exports[`SSL Certificate component renders 1`] = `
|
||||||
Array [
|
Array [
|
||||||
<div
|
.c0.c0.c0 {
|
||||||
class="euiText euiText--medium"
|
width: 35%;
|
||||||
|
}
|
||||||
|
|
||||||
|
<dt
|
||||||
|
class="euiDescriptionList__title c0"
|
||||||
>
|
>
|
||||||
Certificate:
|
TLS Certificate
|
||||||
</div>,
|
</dt>,
|
||||||
<div
|
<div
|
||||||
class="euiSpacer euiSpacer--s"
|
class="euiSpacer euiSpacer--s"
|
||||||
/>,
|
/>,
|
||||||
<div
|
.c0.c0.c0 {
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow euiFlexGroup--responsive euiFlexGroup--wrap"
|
width: 65%;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c1.c1.c1 {
|
||||||
|
margin: 0 0 0 4px;
|
||||||
|
display: inline-block;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
<dd
|
||||||
|
class="euiDescriptionList__description c0"
|
||||||
>
|
>
|
||||||
<div
|
<a
|
||||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
class="eui-displayInline"
|
||||||
|
href="/certificates"
|
||||||
>
|
>
|
||||||
<div
|
<span
|
||||||
aria-label="Expires in 2 months"
|
class="euiToolTipAnchor"
|
||||||
class="euiText euiText--small eui-displayInline euiText--constrainedWidth"
|
|
||||||
>
|
|
||||||
Expires
|
|
||||||
<span
|
|
||||||
class="euiBadge euiBadge--iconLeft"
|
|
||||||
style="background-color:#d3dae6;color:#000"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge__content"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="euiBadge__text"
|
|
||||||
>
|
|
||||||
in 2 months
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="euiFlexItem"
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
class="eui-displayInline"
|
|
||||||
href="/certificates"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="euiText euiText--medium"
|
class="euiText euiText--small"
|
||||||
style="white-space:nowrap"
|
|
||||||
>
|
>
|
||||||
Certificate overview
|
<div
|
||||||
|
color="success"
|
||||||
|
data-euiicon-type="lock"
|
||||||
|
/>
|
||||||
|
<h4
|
||||||
|
class="c1"
|
||||||
|
>
|
||||||
|
Expires in 2 months
|
||||||
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</span>
|
||||||
</div>
|
</a>
|
||||||
</div>,
|
</dd>,
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`SSL Certificate component renders null if invalid date 1`] = `null`;
|
exports[`SSL Certificate component renders null if invalid date 1`] = `
|
||||||
|
Array [
|
||||||
|
.c0.c0.c0 {
|
||||||
|
width: 35%;
|
||||||
|
}
|
||||||
|
|
||||||
|
<dt
|
||||||
|
class="euiDescriptionList__title c0"
|
||||||
|
>
|
||||||
|
TLS Certificate
|
||||||
|
</dt>,
|
||||||
|
<div
|
||||||
|
class="euiSpacer euiSpacer--s"
|
||||||
|
/>,
|
||||||
|
.c0.c0.c0 {
|
||||||
|
width: 65%;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
<dd
|
||||||
|
class="euiDescriptionList__description c0"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="eui-displayInline"
|
||||||
|
href="/certificates"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
--
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</dd>,
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`SSL Certificate component shallow renders 1`] = `
|
exports[`SSL Certificate component shallow renders 1`] = `
|
||||||
<ContextProvider
|
<ContextProvider
|
|
@ -6,10 +6,11 @@
|
||||||
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { renderWithIntl } from 'test_utils/enzyme_helpers';
|
import { MonitorStatusBar } from '../status_bar';
|
||||||
import { MonitorStatusBarComponent } from '../monitor_status_bar';
|
|
||||||
import { Ping } from '../../../../../common/runtime_types';
|
import { Ping } from '../../../../../common/runtime_types';
|
||||||
import * as redux from 'react-redux';
|
import * as redux from 'react-redux';
|
||||||
|
import { renderWithRouter } from '../../../../lib';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
|
|
||||||
describe('MonitorStatusBar component', () => {
|
describe('MonitorStatusBar component', () => {
|
||||||
let monitorStatus: Ping;
|
let monitorStatus: Ping;
|
||||||
|
@ -49,18 +50,21 @@ describe('MonitorStatusBar component', () => {
|
||||||
const spy = jest.spyOn(redux, 'useDispatch');
|
const spy = jest.spyOn(redux, 'useDispatch');
|
||||||
spy.mockReturnValue(jest.fn());
|
spy.mockReturnValue(jest.fn());
|
||||||
|
|
||||||
const spy1 = jest.spyOn(redux, 'useSelector');
|
jest.spyOn(redux, 'useSelector').mockImplementation((fn, d) => {
|
||||||
spy1.mockReturnValue(true);
|
if (fn.name === ' monitorStatusSelector') {
|
||||||
|
return monitorStatus;
|
||||||
|
} else {
|
||||||
|
return monitorLocations;
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders duration in ms, not us', () => {
|
it('renders', () => {
|
||||||
const component = renderWithIntl(
|
const history = createMemoryHistory({
|
||||||
<MonitorStatusBarComponent
|
initialEntries: ['/aWQx/'],
|
||||||
monitorStatus={monitorStatus}
|
});
|
||||||
monitorId="id1"
|
history.location.key = 'test';
|
||||||
monitorLocations={monitorLocations}
|
const component = renderWithRouter(<MonitorStatusBar />, history);
|
||||||
/>
|
|
||||||
);
|
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -6,9 +6,9 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { EuiBadge } from '@elastic/eui';
|
import { EuiIcon } from '@elastic/eui';
|
||||||
import { Tls } from '../../../../../common/runtime_types';
|
import { Tls } from '../../../../../common/runtime_types';
|
||||||
import { MonitorSSLCertificate } from '../monitor_status_bar';
|
import { MonitorSSLCertificate } from '../status_bar';
|
||||||
import * as redux from 'react-redux';
|
import * as redux from 'react-redux';
|
||||||
import { mountWithRouter, renderWithRouter, shallowWithRouter } from '../../../../lib';
|
import { mountWithRouter, renderWithRouter, shallowWithRouter } from '../../../../lib';
|
||||||
import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../common/constants';
|
import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../common/constants';
|
||||||
|
@ -58,14 +58,12 @@ describe('SSL Certificate component', () => {
|
||||||
};
|
};
|
||||||
const component = mountWithRouter(<MonitorSSLCertificate tls={monitorTls} />);
|
const component = mountWithRouter(<MonitorSSLCertificate tls={monitorTls} />);
|
||||||
|
|
||||||
const badgeComponent = component.find(EuiBadge);
|
const lockIcon = component.find(EuiIcon);
|
||||||
|
|
||||||
expect(badgeComponent.props().color).toBe('warning');
|
expect(lockIcon.props().color).toBe('warning');
|
||||||
|
|
||||||
const badgeComponentText = component.find('.euiBadge__text');
|
const componentText = component.find('h4');
|
||||||
expect(badgeComponentText.text()).toBe(moment(dateIn5Days).fromNow());
|
expect(componentText.text()).toBe('Expires soon ' + moment(dateIn5Days).fromNow());
|
||||||
|
|
||||||
expect(badgeComponent.find('span.euiBadge--warning')).toBeTruthy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not render the expiration date with a warning state if expiry date is greater than a month', () => {
|
it('does not render the expiration date with a warning state if expiry date is greater than a month', () => {
|
||||||
|
@ -75,12 +73,10 @@ describe('SSL Certificate component', () => {
|
||||||
};
|
};
|
||||||
const component = mountWithRouter(<MonitorSSLCertificate tls={monitorTls} />);
|
const component = mountWithRouter(<MonitorSSLCertificate tls={monitorTls} />);
|
||||||
|
|
||||||
const badgeComponent = component.find(EuiBadge);
|
const lockIcon = component.find(EuiIcon);
|
||||||
expect(badgeComponent.props().color).toBe('default');
|
expect(lockIcon.props().color).toBe('success');
|
||||||
|
|
||||||
const badgeComponentText = component.find('.euiBadge__text');
|
const componentText = component.find('h4');
|
||||||
expect(badgeComponentText.text()).toBe(moment(dateIn40Days).fromNow());
|
expect(componentText.text()).toBe('Expires ' + moment(dateIn40Days).fromNow());
|
||||||
|
|
||||||
expect(badgeComponent.find('span.euiBadge--warning')).toHaveLength(0);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -17,10 +17,16 @@ describe('StatusByLocation component', () => {
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
|
timestamp: '2020-01-13T22:50:06.536Z',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
|
timestamp: '2020-01-13T22:50:06.536Z',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const component = shallowWithIntl(<StatusByLocations locations={monitorLocations} />);
|
const component = shallowWithIntl(<StatusByLocations locations={monitorLocations} />);
|
||||||
|
@ -32,10 +38,16 @@ describe('StatusByLocation component', () => {
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
|
timestamp: '2020-01-13T22:50:06.536Z',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
|
timestamp: '2020-01-13T22:50:06.536Z',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const component = renderWithIntl(<StatusByLocations locations={monitorLocations} />);
|
const component = renderWithIntl(<StatusByLocations locations={monitorLocations} />);
|
||||||
|
@ -47,6 +59,9 @@ describe('StatusByLocation component', () => {
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
|
timestamp: '2020-01-13T22:50:06.536Z',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const component = renderWithIntl(<StatusByLocations locations={monitorLocations} />);
|
const component = renderWithIntl(<StatusByLocations locations={monitorLocations} />);
|
||||||
|
@ -58,6 +73,9 @@ describe('StatusByLocation component', () => {
|
||||||
{
|
{
|
||||||
summary: { up: 0, down: 4 },
|
summary: { up: 0, down: 4 },
|
||||||
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
|
timestamp: '2020-01-13T22:50:06.536Z',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const component = renderWithIntl(<StatusByLocations locations={monitorLocations} />);
|
const component = renderWithIntl(<StatusByLocations locations={monitorLocations} />);
|
||||||
|
@ -69,10 +87,16 @@ describe('StatusByLocation component', () => {
|
||||||
{
|
{
|
||||||
summary: { up: 0, down: 4 },
|
summary: { up: 0, down: 4 },
|
||||||
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
|
timestamp: '2020-01-13T22:50:06.536Z',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 0, down: 4 },
|
summary: { up: 0, down: 4 },
|
||||||
geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
|
timestamp: '2020-01-13T22:50:06.536Z',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const component = renderWithIntl(<StatusByLocations locations={monitorLocations} />);
|
const component = renderWithIntl(<StatusByLocations locations={monitorLocations} />);
|
||||||
|
@ -84,10 +108,16 @@ describe('StatusByLocation component', () => {
|
||||||
{
|
{
|
||||||
summary: { up: 0, down: 4 },
|
summary: { up: 0, down: 4 },
|
||||||
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
|
timestamp: '2020-01-13T22:50:06.536Z',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
|
timestamp: '2020-01-13T22:50:06.536Z',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const component = renderWithIntl(<StatusByLocations locations={monitorLocations} />);
|
const component = renderWithIntl(<StatusByLocations locations={monitorLocations} />);
|
|
@ -0,0 +1,381 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`AvailabilityReporting component renders correctly against snapshot 1`] = `
|
||||||
|
Array [
|
||||||
|
@media (max-width:1042px) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="euiSpacer euiSpacer--s"
|
||||||
|
/>,
|
||||||
|
.c0 {
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:1042px) {
|
||||||
|
.c0 {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="euiBasicTable"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<table
|
||||||
|
class="euiTable euiTable--compressed"
|
||||||
|
>
|
||||||
|
<caption
|
||||||
|
class="euiScreenReaderOnly euiTableCaption"
|
||||||
|
/>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
class="euiTableHeaderCell"
|
||||||
|
data-test-subj="tableHeaderCell_label_0"
|
||||||
|
role="columnheader"
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiTableCellContent"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiTableCellContent__text"
|
||||||
|
>
|
||||||
|
Location
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="euiTableHeaderCell"
|
||||||
|
data-test-subj="tableHeaderCell_availability_1"
|
||||||
|
role="columnheader"
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiTableCellContent euiTableCellContent--alignRight"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiTableCellContent__text"
|
||||||
|
>
|
||||||
|
Availability
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="euiTableHeaderCell"
|
||||||
|
data-test-subj="tableHeaderCell_timestamp_2"
|
||||||
|
role="columnheader"
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiTableCellContent euiTableCellContent--alignRight"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiTableCellContent__text"
|
||||||
|
>
|
||||||
|
Last check
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
|
class="euiTableRow"
|
||||||
|
>
|
||||||
|
<td
|
||||||
|
class="euiTableRowCell"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||||
|
>
|
||||||
|
Location
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="euiTableCellContent euiTableCellContent--truncateText euiTableCellContent--overflowingContent"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiBadge euiBadge--iconLeft"
|
||||||
|
style="background-color:#d3dae6;color:#000"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiBadge__content"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiBadge__text"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiText euiText--small"
|
||||||
|
>
|
||||||
|
<h4>
|
||||||
|
au-heartbeat
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class="euiTableRowCell"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||||
|
>
|
||||||
|
Availability
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="euiTableCellContent euiTableCellContent--alignRight euiTableCellContent--overflowingContent"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class=""
|
||||||
|
>
|
||||||
|
100.00 %
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class="euiTableRowCell"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||||
|
>
|
||||||
|
Last check
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="euiTableCellContent euiTableCellContent--alignRight"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiTableCellContent__text"
|
||||||
|
>
|
||||||
|
36m ago
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr
|
||||||
|
class="euiTableRow"
|
||||||
|
>
|
||||||
|
<td
|
||||||
|
class="euiTableRowCell"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||||
|
>
|
||||||
|
Location
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="euiTableCellContent euiTableCellContent--truncateText euiTableCellContent--overflowingContent"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiBadge euiBadge--iconLeft"
|
||||||
|
style="background-color:#d3dae6;color:#000"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiBadge__content"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiBadge__text"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiText euiText--small"
|
||||||
|
>
|
||||||
|
<h4>
|
||||||
|
nyc-heartbeat
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class="euiTableRowCell"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||||
|
>
|
||||||
|
Availability
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="euiTableCellContent euiTableCellContent--alignRight euiTableCellContent--overflowingContent"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class=""
|
||||||
|
>
|
||||||
|
100.00 %
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class="euiTableRowCell"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||||
|
>
|
||||||
|
Last check
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="euiTableCellContent euiTableCellContent--alignRight"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiTableCellContent__text"
|
||||||
|
>
|
||||||
|
36m ago
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr
|
||||||
|
class="euiTableRow"
|
||||||
|
>
|
||||||
|
<td
|
||||||
|
class="euiTableRowCell"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||||
|
>
|
||||||
|
Location
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="euiTableCellContent euiTableCellContent--truncateText euiTableCellContent--overflowingContent"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiBadge euiBadge--iconLeft"
|
||||||
|
style="background-color:#d3dae6;color:#000"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiBadge__content"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiBadge__text"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiText euiText--small"
|
||||||
|
>
|
||||||
|
<h4>
|
||||||
|
spa-heartbeat
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class="euiTableRowCell"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||||
|
>
|
||||||
|
Availability
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="euiTableCellContent euiTableCellContent--alignRight euiTableCellContent--overflowingContent"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class=""
|
||||||
|
>
|
||||||
|
100.00 %
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class="euiTableRowCell"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||||
|
>
|
||||||
|
Last check
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="euiTableCellContent euiTableCellContent--alignRight"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiTableCellContent__text"
|
||||||
|
>
|
||||||
|
36m ago
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`AvailabilityReporting component shallow renders correctly against snapshot 1`] = `
|
||||||
|
<Fragment>
|
||||||
|
<EuiSpacer
|
||||||
|
size="s"
|
||||||
|
/>
|
||||||
|
<EuiBasicTable
|
||||||
|
columns={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"field": "label",
|
||||||
|
"name": "Location",
|
||||||
|
"render": [Function],
|
||||||
|
"truncateText": true,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"align": "right",
|
||||||
|
"field": "availability",
|
||||||
|
"name": "Availability",
|
||||||
|
"render": [Function],
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"align": "right",
|
||||||
|
"field": "timestamp",
|
||||||
|
"name": "Last check",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
compressed={true}
|
||||||
|
items={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"availability": 100,
|
||||||
|
"color": "#d3dae6",
|
||||||
|
"label": "au-heartbeat",
|
||||||
|
"timestamp": "36m ago",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"availability": 100,
|
||||||
|
"color": "#d3dae6",
|
||||||
|
"label": "nyc-heartbeat",
|
||||||
|
"timestamp": "36m ago",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"availability": 100,
|
||||||
|
"color": "#d3dae6",
|
||||||
|
"label": "spa-heartbeat",
|
||||||
|
"timestamp": "36m ago",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
noItemsMessage="No items found"
|
||||||
|
onChange={[Function]}
|
||||||
|
responsive={false}
|
||||||
|
tableLayout="fixed"
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
`;
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,56 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`TagLabel component renders correctly against snapshot 1`] = `
|
||||||
|
.c0 {
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:1042px) {
|
||||||
|
.c0 {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="c0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiBadge euiBadge--iconLeft"
|
||||||
|
style="background-color:#fff;color:#000"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiBadge__content"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="euiBadge__text"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="euiText euiText--small"
|
||||||
|
>
|
||||||
|
<h4>
|
||||||
|
US-East
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`TagLabel component shallow render correctly against snapshot 1`] = `
|
||||||
|
<styled.div>
|
||||||
|
<EuiBadge
|
||||||
|
color="#fff"
|
||||||
|
>
|
||||||
|
<EuiText
|
||||||
|
size="s"
|
||||||
|
>
|
||||||
|
<h4>
|
||||||
|
US-East
|
||||||
|
</h4>
|
||||||
|
</EuiText>
|
||||||
|
</EuiBadge>
|
||||||
|
</styled.div>
|
||||||
|
`;
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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 { AvailabilityReporting } from '../availability_reporting';
|
||||||
|
import { StatusTag } from '../location_status_tags';
|
||||||
|
|
||||||
|
describe('AvailabilityReporting component', () => {
|
||||||
|
let allLocations: StatusTag[];
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
allLocations = [
|
||||||
|
{
|
||||||
|
label: 'au-heartbeat',
|
||||||
|
timestamp: '36m ago',
|
||||||
|
color: '#d3dae6',
|
||||||
|
availability: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'nyc-heartbeat',
|
||||||
|
timestamp: '36m ago',
|
||||||
|
color: '#d3dae6',
|
||||||
|
availability: 100,
|
||||||
|
},
|
||||||
|
{ label: 'spa-heartbeat', timestamp: '36m ago', color: '#d3dae6', availability: 100 },
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shallow renders correctly against snapshot', () => {
|
||||||
|
const component = shallowWithIntl(<AvailabilityReporting allLocations={allLocations} />);
|
||||||
|
expect(component).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders correctly against snapshot', () => {
|
||||||
|
const component = renderWithIntl(<AvailabilityReporting allLocations={allLocations} />);
|
||||||
|
expect(component).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -7,7 +7,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { renderWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
|
import { renderWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||||
import { MonitorLocation } from '../../../../../common/runtime_types/monitor';
|
import { MonitorLocation } from '../../../../../../common/runtime_types/monitor';
|
||||||
import { LocationStatusTags } from '../index';
|
import { LocationStatusTags } from '../index';
|
||||||
|
|
||||||
describe('LocationStatusTags component', () => {
|
describe('LocationStatusTags component', () => {
|
||||||
|
@ -19,16 +19,22 @@ describe('LocationStatusTags component', () => {
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 'w').toISOString(),
|
timestamp: moment().subtract('5', 'w').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 'w').toISOString(),
|
timestamp: moment().subtract('5', 'w').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 0, down: 2 },
|
summary: { up: 0, down: 2 },
|
||||||
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 'w').toISOString(),
|
timestamp: moment().subtract('5', 'w').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const component = shallowWithIntl(<LocationStatusTags locations={monitorLocations} />);
|
const component = shallowWithIntl(<LocationStatusTags locations={monitorLocations} />);
|
||||||
|
@ -41,41 +47,57 @@ describe('LocationStatusTags component', () => {
|
||||||
summary: { up: 0, down: 1 },
|
summary: { up: 0, down: 1 },
|
||||||
geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 's').toISOString(),
|
timestamp: moment().subtract('5', 's').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 0, down: 1 },
|
summary: { up: 0, down: 1 },
|
||||||
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 'm').toISOString(),
|
timestamp: moment().subtract('5', 'm').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 0, down: 1 },
|
summary: { up: 0, down: 1 },
|
||||||
geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 'h').toISOString(),
|
timestamp: moment().subtract('5', 'h').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 0, down: 1 },
|
summary: { up: 0, down: 1 },
|
||||||
geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 'd').toISOString(),
|
timestamp: moment().subtract('5', 'd').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 0, down: 1 },
|
summary: { up: 0, down: 1 },
|
||||||
geo: { name: 'New York', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'New York', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 'w').toISOString(),
|
timestamp: moment().subtract('5', 'w').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 0, down: 1 },
|
summary: { up: 0, down: 1 },
|
||||||
geo: { name: 'Toronto', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Toronto', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 'M').toISOString(),
|
timestamp: moment().subtract('5', 'M').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 0, down: 1 },
|
summary: { up: 0, down: 1 },
|
||||||
geo: { name: 'Sydney', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Sydney', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 'y').toISOString(),
|
timestamp: moment().subtract('5', 'y').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 0, down: 1 },
|
summary: { up: 0, down: 1 },
|
||||||
geo: { name: 'Paris', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Paris', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 'y').toISOString(),
|
timestamp: moment().subtract('5', 'y').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const component = renderWithIntl(<LocationStatusTags locations={monitorLocations} />);
|
const component = renderWithIntl(<LocationStatusTags locations={monitorLocations} />);
|
||||||
|
@ -88,11 +110,15 @@ describe('LocationStatusTags component', () => {
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 's').toISOString(),
|
timestamp: moment().subtract('5', 's').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 'd').toISOString(),
|
timestamp: moment().subtract('5', 'd').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const component = renderWithIntl(<LocationStatusTags locations={monitorLocations} />);
|
const component = renderWithIntl(<LocationStatusTags locations={monitorLocations} />);
|
||||||
|
@ -105,11 +131,15 @@ describe('LocationStatusTags component', () => {
|
||||||
summary: { up: 0, down: 2 },
|
summary: { up: 0, down: 2 },
|
||||||
geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 's').toISOString(),
|
timestamp: moment().subtract('5', 's').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 0, down: 2 },
|
summary: { up: 0, down: 2 },
|
||||||
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: moment().subtract('5', 'm').toISOString(),
|
timestamp: moment().subtract('5', 'm').toISOString(),
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const component = renderWithIntl(<LocationStatusTags locations={monitorLocations} />);
|
const component = renderWithIntl(<LocationStatusTags locations={monitorLocations} />);
|
|
@ -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 { TagLabel } from '../tag_label';
|
||||||
|
|
||||||
|
describe('TagLabel component', () => {
|
||||||
|
it('shallow render correctly against snapshot', () => {
|
||||||
|
const component = shallowWithIntl(<TagLabel color={'#fff'} label={'US-East'} />);
|
||||||
|
expect(component).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders correctly against snapshot', () => {
|
||||||
|
const component = renderWithIntl(<TagLabel color={'#fff'} label={'US-East'} />);
|
||||||
|
expect(component).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* 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 { EuiBasicTable, EuiSpacer } from '@elastic/eui';
|
||||||
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
import { Pagination } from '@elastic/eui/src/components/basic_table/pagination_bar';
|
||||||
|
import { StatusTag } from './location_status_tags';
|
||||||
|
import { TagLabel } from './tag_label';
|
||||||
|
import { AvailabilityLabel, LastCheckLabel, LocationLabel } from '../translations';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
allLocations: StatusTag[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatAvailabilityValue = (val: number) => {
|
||||||
|
const result = Math.round(val * 100) / 100;
|
||||||
|
return result.toFixed(2);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AvailabilityReporting: React.FC<Props> = ({ allLocations }) => {
|
||||||
|
const [pageIndex, setPageIndex] = useState(0);
|
||||||
|
|
||||||
|
const cols = [
|
||||||
|
{
|
||||||
|
field: 'label',
|
||||||
|
name: LocationLabel,
|
||||||
|
truncateText: true,
|
||||||
|
render: (val: string, item: StatusTag) => {
|
||||||
|
return <TagLabel color={item.color} label={item.label} />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'availability',
|
||||||
|
name: AvailabilityLabel,
|
||||||
|
align: 'right' as const,
|
||||||
|
render: (val: number) => {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.uptime.availabilityLabelText"
|
||||||
|
defaultMessage="{value} %"
|
||||||
|
values={{ value: formatAvailabilityValue(val) }}
|
||||||
|
description="A percentage value, like 23.5%"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: LastCheckLabel,
|
||||||
|
field: 'timestamp',
|
||||||
|
align: 'right' as const,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const pageSize = 5;
|
||||||
|
|
||||||
|
const pagination: Pagination = {
|
||||||
|
pageIndex,
|
||||||
|
pageSize,
|
||||||
|
totalItemCount: allLocations.length,
|
||||||
|
hidePerPageOptions: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTableChange = ({ page }: any) => {
|
||||||
|
setPageIndex(page.index);
|
||||||
|
};
|
||||||
|
|
||||||
|
const paginationProps = allLocations.length > pageSize ? { pagination } : {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EuiSpacer size="s" />
|
||||||
|
<EuiBasicTable
|
||||||
|
responsive={false}
|
||||||
|
compressed={true}
|
||||||
|
columns={cols}
|
||||||
|
items={allLocations.slice(pageIndex * pageSize, pageIndex * pageSize + pageSize)}
|
||||||
|
onChange={onTableChange}
|
||||||
|
{...paginationProps}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* 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 { AvailabilityReporting } from './availability_reporting';
|
||||||
|
export { LocationStatusTags } from './location_status_tags';
|
||||||
|
export { TagLabel } from './tag_label';
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* 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 moment from 'moment';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { UptimeThemeContext } from '../../../../contexts';
|
||||||
|
import { MonitorLocation } from '../../../../../common/runtime_types';
|
||||||
|
import { SHORT_TIMESPAN_LOCALE, SHORT_TS_LOCALE } from '../../../../../common/constants';
|
||||||
|
import { AvailabilityReporting } from '../index';
|
||||||
|
|
||||||
|
// Set height so that it remains within panel, enough height to display 7 locations tags
|
||||||
|
const TagContainer = styled.div`
|
||||||
|
max-height: 246px;
|
||||||
|
overflow: hidden;
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
locations: MonitorLocation[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatusTag {
|
||||||
|
label: string;
|
||||||
|
timestamp: string;
|
||||||
|
color: string;
|
||||||
|
availability: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LocationStatusTags = ({ locations }: Props) => {
|
||||||
|
const {
|
||||||
|
colors: { gray, danger },
|
||||||
|
} = useContext(UptimeThemeContext);
|
||||||
|
|
||||||
|
const allLocations: StatusTag[] = [];
|
||||||
|
const prevLocal: string = moment.locale() ?? 'en';
|
||||||
|
|
||||||
|
const shortLocale = moment.locale(SHORT_TS_LOCALE) === SHORT_TS_LOCALE;
|
||||||
|
if (!shortLocale) {
|
||||||
|
moment.defineLocale(SHORT_TS_LOCALE, SHORT_TIMESPAN_LOCALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
locations.forEach((item: MonitorLocation) => {
|
||||||
|
allLocations.push({
|
||||||
|
label: item.geo.name!,
|
||||||
|
timestamp: moment(new Date(item.timestamp).valueOf()).fromNow(),
|
||||||
|
color: item.summary.down === 0 ? gray : danger,
|
||||||
|
availability: (item.up_history / (item.up_history + item.down_history)) * 100,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Need to reset locale so it doesn't effect other parts of the app
|
||||||
|
moment.locale(prevLocal);
|
||||||
|
|
||||||
|
// Sort lexicographically by label
|
||||||
|
allLocations.sort((a, b) => {
|
||||||
|
return a.label > b.label ? 1 : b.label > a.label ? -1 : 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (allLocations.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TagContainer>
|
||||||
|
<AvailabilityReporting allLocations={allLocations} />
|
||||||
|
</TagContainer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* 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 styled from 'styled-components';
|
||||||
|
import { EuiBadge, EuiText } from '@elastic/eui';
|
||||||
|
|
||||||
|
const BadgeItem = styled.div`
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
@media (max-width: 1042px) {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
color: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TagLabel: React.FC<Props> = ({ color, label }) => {
|
||||||
|
return (
|
||||||
|
<BadgeItem>
|
||||||
|
<EuiBadge color={color}>
|
||||||
|
<EuiText size="s">
|
||||||
|
<h4>{label}</h4>
|
||||||
|
</EuiText>
|
||||||
|
</EuiBadge>
|
||||||
|
</BadgeItem>
|
||||||
|
);
|
||||||
|
};
|
|
@ -4,9 +4,9 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { MonitorStatusBarComponent } from './monitor_status_bar';
|
|
||||||
export { MonitorStatusDetailsComponent } from './status_details';
|
export { MonitorStatusDetailsComponent } from './status_details';
|
||||||
export { StatusByLocations } from './monitor_status_bar/status_by_location';
|
export { StatusByLocations } from './status_bar/status_by_location';
|
||||||
|
|
||||||
export { MonitorStatusDetails } from './status_details_container';
|
export { MonitorStatusDetails } from './status_details_container';
|
||||||
export { MonitorStatusBar } from './monitor_status_bar/status_bar_container';
|
export { MonitorStatusBar } from './status_bar/status_bar';
|
||||||
|
export { AvailabilityReporting } from './availability_reporting/availability_reporting';
|
|
@ -0,0 +1,280 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`LocationAvailability component doesnt shows warning if geo is provided 1`] = `
|
||||||
|
<EuiErrorBoundary>
|
||||||
|
<EuiFlexGroup
|
||||||
|
gutterSize="none"
|
||||||
|
responsive={false}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"flexGrow": 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<EuiFlexItem>
|
||||||
|
<EuiTitle
|
||||||
|
size="s"
|
||||||
|
>
|
||||||
|
<h3>
|
||||||
|
Monitoring from
|
||||||
|
</h3>
|
||||||
|
</EuiTitle>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
>
|
||||||
|
<ToggleViewBtn
|
||||||
|
onChange={[Function]}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
<EuiFlexGroup
|
||||||
|
gutterSize="none"
|
||||||
|
justifyContent="flexEnd"
|
||||||
|
wrap={true}
|
||||||
|
>
|
||||||
|
<Styled(EuiFlexItem)
|
||||||
|
grow={true}
|
||||||
|
>
|
||||||
|
<LocationStatusTags
|
||||||
|
locations={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"down_history": 0,
|
||||||
|
"geo": Object {
|
||||||
|
"location": Object {
|
||||||
|
"lat": "40.730610",
|
||||||
|
"lon": " -73.935242",
|
||||||
|
},
|
||||||
|
"name": "New York",
|
||||||
|
},
|
||||||
|
"summary": Object {
|
||||||
|
"down": 0,
|
||||||
|
"up": 4,
|
||||||
|
},
|
||||||
|
"timestamp": "2020-01-13T22:50:06.536Z",
|
||||||
|
"up_history": 4,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"down_history": 0,
|
||||||
|
"geo": Object {
|
||||||
|
"location": Object {
|
||||||
|
"lat": "52.487448",
|
||||||
|
"lon": " 13.394798",
|
||||||
|
},
|
||||||
|
"name": "Tokyo",
|
||||||
|
},
|
||||||
|
"summary": Object {
|
||||||
|
"down": 0,
|
||||||
|
"up": 4,
|
||||||
|
},
|
||||||
|
"timestamp": "2020-01-13T22:50:04.354Z",
|
||||||
|
"up_history": 4,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Styled(EuiFlexItem)>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
</EuiErrorBoundary>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`LocationAvailability component renders correctly against snapshot 1`] = `
|
||||||
|
<EuiErrorBoundary>
|
||||||
|
<EuiFlexGroup
|
||||||
|
gutterSize="none"
|
||||||
|
responsive={false}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"flexGrow": 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<EuiFlexItem>
|
||||||
|
<EuiTitle
|
||||||
|
size="s"
|
||||||
|
>
|
||||||
|
<h3>
|
||||||
|
Monitoring from
|
||||||
|
</h3>
|
||||||
|
</EuiTitle>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
>
|
||||||
|
<ToggleViewBtn
|
||||||
|
onChange={[Function]}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
<EuiFlexGroup
|
||||||
|
gutterSize="none"
|
||||||
|
justifyContent="flexEnd"
|
||||||
|
wrap={true}
|
||||||
|
>
|
||||||
|
<Styled(EuiFlexItem)
|
||||||
|
grow={true}
|
||||||
|
>
|
||||||
|
<LocationStatusTags
|
||||||
|
locations={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"down_history": 0,
|
||||||
|
"geo": Object {
|
||||||
|
"location": Object {
|
||||||
|
"lat": "40.730610",
|
||||||
|
"lon": " -73.935242",
|
||||||
|
},
|
||||||
|
"name": "New York",
|
||||||
|
},
|
||||||
|
"summary": Object {
|
||||||
|
"down": 0,
|
||||||
|
"up": 4,
|
||||||
|
},
|
||||||
|
"timestamp": "2020-01-13T22:50:06.536Z",
|
||||||
|
"up_history": 4,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"down_history": 0,
|
||||||
|
"geo": Object {
|
||||||
|
"location": Object {
|
||||||
|
"lat": "52.487448",
|
||||||
|
"lon": " 13.394798",
|
||||||
|
},
|
||||||
|
"name": "Tokyo",
|
||||||
|
},
|
||||||
|
"summary": Object {
|
||||||
|
"down": 0,
|
||||||
|
"up": 4,
|
||||||
|
},
|
||||||
|
"timestamp": "2020-01-13T22:50:04.354Z",
|
||||||
|
"up_history": 4,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"down_history": 0,
|
||||||
|
"geo": Object {
|
||||||
|
"name": "Unnamed-location",
|
||||||
|
},
|
||||||
|
"summary": Object {
|
||||||
|
"down": 0,
|
||||||
|
"up": 4,
|
||||||
|
},
|
||||||
|
"timestamp": "2020-01-13T22:50:02.753Z",
|
||||||
|
"up_history": 4,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Styled(EuiFlexItem)>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
</EuiErrorBoundary>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`LocationAvailability component renders named locations that have missing geo data 1`] = `
|
||||||
|
<EuiErrorBoundary>
|
||||||
|
<EuiFlexGroup
|
||||||
|
gutterSize="none"
|
||||||
|
responsive={false}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"flexGrow": 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<EuiFlexItem>
|
||||||
|
<EuiTitle
|
||||||
|
size="s"
|
||||||
|
>
|
||||||
|
<h3>
|
||||||
|
Monitoring from
|
||||||
|
</h3>
|
||||||
|
</EuiTitle>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
>
|
||||||
|
<ToggleViewBtn
|
||||||
|
onChange={[Function]}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
<EuiFlexGroup
|
||||||
|
gutterSize="none"
|
||||||
|
justifyContent="flexEnd"
|
||||||
|
wrap={true}
|
||||||
|
>
|
||||||
|
<Styled(EuiFlexItem)
|
||||||
|
grow={true}
|
||||||
|
>
|
||||||
|
<LocationStatusTags
|
||||||
|
locations={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"down_history": 0,
|
||||||
|
"geo": Object {
|
||||||
|
"location": undefined,
|
||||||
|
"name": "New York",
|
||||||
|
},
|
||||||
|
"summary": Object {
|
||||||
|
"down": 0,
|
||||||
|
"up": 4,
|
||||||
|
},
|
||||||
|
"timestamp": "2020-01-13T22:50:06.536Z",
|
||||||
|
"up_history": 4,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Styled(EuiFlexItem)>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
</EuiErrorBoundary>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`LocationAvailability component shows warning if geo information is missing 1`] = `
|
||||||
|
<EuiErrorBoundary>
|
||||||
|
<EuiFlexGroup
|
||||||
|
gutterSize="none"
|
||||||
|
responsive={false}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"flexGrow": 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<EuiFlexItem>
|
||||||
|
<LocationMissingWarning />
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
>
|
||||||
|
<ToggleViewBtn
|
||||||
|
onChange={[Function]}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
<EuiFlexGroup
|
||||||
|
gutterSize="none"
|
||||||
|
justifyContent="flexEnd"
|
||||||
|
wrap={true}
|
||||||
|
>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
>
|
||||||
|
<LocationMap
|
||||||
|
downPoints={Array []}
|
||||||
|
upPoints={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"location": Object {
|
||||||
|
"lat": "52.487448",
|
||||||
|
"lon": " 13.394798",
|
||||||
|
},
|
||||||
|
"name": "Tokyo",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
</EuiErrorBoundary>
|
||||||
|
`;
|
|
@ -6,59 +6,105 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
|
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||||
import { LocationMap } from '../location_map';
|
import { LocationAvailability } from '../location_availability';
|
||||||
import { MonitorLocations } from '../../../../../common/runtime_types';
|
import { MonitorLocations } from '../../../../../../common/runtime_types';
|
||||||
import { LocationMissingWarning } from '../location_missing';
|
import { LocationMissingWarning } from '../../location_map/location_missing';
|
||||||
|
|
||||||
|
class LocalStorageMock {
|
||||||
|
store: Record<string, string>;
|
||||||
|
constructor() {
|
||||||
|
this.store = { 'xpack.uptime.detailPage.selectedView': 'list' };
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.store = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
getItem(key: string) {
|
||||||
|
return this.store[key] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setItem(key: string, value: string) {
|
||||||
|
this.store[key] = value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
removeItem(key: string) {
|
||||||
|
delete this.store[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Note For shallow test, we need absolute time strings
|
// Note For shallow test, we need absolute time strings
|
||||||
describe('LocationMap component', () => {
|
describe('LocationAvailability component', () => {
|
||||||
let monitorLocations: MonitorLocations;
|
let monitorLocations: MonitorLocations;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
// @ts-ignore replacing a call to localStorage we use for monitor list size
|
||||||
|
global.localStorage = new LocalStorageMock();
|
||||||
|
|
||||||
|
// @ts-ignore replacing a call to localStorage we use for monitor list size
|
||||||
|
global.localStorage.setItem('xpack.uptime.detailPage.selectedView', 'list');
|
||||||
|
|
||||||
monitorLocations = {
|
monitorLocations = {
|
||||||
monitorId: 'wapo',
|
monitorId: 'wapo',
|
||||||
|
up_history: 12,
|
||||||
|
down_history: 0,
|
||||||
locations: [
|
locations: [
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'New York', location: { lat: '40.730610', lon: ' -73.935242' } },
|
geo: { name: 'New York', location: { lat: '40.730610', lon: ' -73.935242' } },
|
||||||
timestamp: '2020-01-13T22:50:06.536Z',
|
timestamp: '2020-01-13T22:50:06.536Z',
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: '2020-01-13T22:50:04.354Z',
|
timestamp: '2020-01-13T22:50:04.354Z',
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'Unnamed-location' },
|
geo: { name: 'Unnamed-location' },
|
||||||
timestamp: '2020-01-13T22:50:02.753Z',
|
timestamp: '2020-01-13T22:50:02.753Z',
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders correctly against snapshot', () => {
|
it('renders correctly against snapshot', () => {
|
||||||
const component = shallowWithIntl(<LocationMap monitorLocations={monitorLocations} />);
|
const component = shallowWithIntl(<LocationAvailability monitorLocations={monitorLocations} />);
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows warning if geo information is missing', () => {
|
it('shows warning if geo information is missing', () => {
|
||||||
|
// @ts-ignore replacing a call to localStorage we use for monitor list size
|
||||||
|
global.localStorage.setItem('xpack.uptime.detailPage.selectedView', 'map');
|
||||||
|
|
||||||
monitorLocations = {
|
monitorLocations = {
|
||||||
monitorId: 'wapo',
|
monitorId: 'wapo',
|
||||||
|
up_history: 8,
|
||||||
|
down_history: 0,
|
||||||
locations: [
|
locations: [
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: '2020-01-13T22:50:04.354Z',
|
timestamp: '2020-01-13T22:50:04.354Z',
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'Unnamed-location' },
|
geo: { name: 'Unnamed-location' },
|
||||||
timestamp: '2020-01-13T22:50:02.753Z',
|
timestamp: '2020-01-13T22:50:02.753Z',
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const component = shallowWithIntl(<LocationMap monitorLocations={monitorLocations} />);
|
const component = shallowWithIntl(<LocationAvailability monitorLocations={monitorLocations} />);
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
|
|
||||||
const warningComponent = component.find(LocationMissingWarning);
|
const warningComponent = component.find(LocationMissingWarning);
|
||||||
|
@ -68,20 +114,26 @@ describe('LocationMap component', () => {
|
||||||
it('doesnt shows warning if geo is provided', () => {
|
it('doesnt shows warning if geo is provided', () => {
|
||||||
monitorLocations = {
|
monitorLocations = {
|
||||||
monitorId: 'wapo',
|
monitorId: 'wapo',
|
||||||
|
up_history: 8,
|
||||||
|
down_history: 0,
|
||||||
locations: [
|
locations: [
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'New York', location: { lat: '40.730610', lon: ' -73.935242' } },
|
geo: { name: 'New York', location: { lat: '40.730610', lon: ' -73.935242' } },
|
||||||
timestamp: '2020-01-13T22:50:06.536Z',
|
timestamp: '2020-01-13T22:50:06.536Z',
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
|
geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||||
timestamp: '2020-01-13T22:50:04.354Z',
|
timestamp: '2020-01-13T22:50:04.354Z',
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const component = shallowWithIntl(<LocationMap monitorLocations={monitorLocations} />);
|
const component = shallowWithIntl(<LocationAvailability monitorLocations={monitorLocations} />);
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
|
|
||||||
const warningComponent = component.find(LocationMissingWarning);
|
const warningComponent = component.find(LocationMissingWarning);
|
||||||
|
@ -91,16 +143,20 @@ describe('LocationMap component', () => {
|
||||||
it('renders named locations that have missing geo data', () => {
|
it('renders named locations that have missing geo data', () => {
|
||||||
monitorLocations = {
|
monitorLocations = {
|
||||||
monitorId: 'wapo',
|
monitorId: 'wapo',
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
locations: [
|
locations: [
|
||||||
{
|
{
|
||||||
summary: { up: 4, down: 0 },
|
summary: { up: 4, down: 0 },
|
||||||
geo: { name: 'New York', location: undefined },
|
geo: { name: 'New York', location: undefined },
|
||||||
timestamp: '2020-01-13T22:50:06.536Z',
|
timestamp: '2020-01-13T22:50:06.536Z',
|
||||||
|
up_history: 4,
|
||||||
|
down_history: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const component = shallowWithIntl(<LocationMap monitorLocations={monitorLocations} />);
|
const component = shallowWithIntl(<LocationAvailability monitorLocations={monitorLocations} />);
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -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, { useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { EuiFlexGroup, EuiFlexItem, EuiErrorBoundary, EuiTitle } from '@elastic/eui';
|
||||||
|
import { LocationStatusTags } from '../availability_reporting';
|
||||||
|
import { LocationPoint } from '../location_map/embeddables/embedded_map';
|
||||||
|
import { MonitorLocations, MonitorLocation } from '../../../../../common/runtime_types';
|
||||||
|
import { UNNAMED_LOCATION } from '../../../../../common/constants';
|
||||||
|
import { LocationMissingWarning } from '../location_map/location_missing';
|
||||||
|
import { useSelectedView } from './use_selected_view';
|
||||||
|
import { LocationMap } from '../location_map';
|
||||||
|
import { MonitoringFrom } from '../translations';
|
||||||
|
import { ToggleViewBtn } from './toggle_view_btn';
|
||||||
|
|
||||||
|
const EuiFlexItemTags = styled(EuiFlexItem)`
|
||||||
|
width: 350px;
|
||||||
|
@media (max-width: 1042px) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface LocationMapProps {
|
||||||
|
monitorLocations: MonitorLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LocationAvailability = ({ monitorLocations }: LocationMapProps) => {
|
||||||
|
const upPoints: LocationPoint[] = [];
|
||||||
|
const downPoints: LocationPoint[] = [];
|
||||||
|
|
||||||
|
let isAnyGeoInfoMissing = false;
|
||||||
|
|
||||||
|
if (monitorLocations?.locations) {
|
||||||
|
monitorLocations.locations.forEach(({ geo, summary }: MonitorLocation) => {
|
||||||
|
if (geo?.name === UNNAMED_LOCATION || !geo?.location) {
|
||||||
|
isAnyGeoInfoMissing = true;
|
||||||
|
} else if (!!geo.location.lat && !!geo.location.lon) {
|
||||||
|
if (summary?.down === 0) {
|
||||||
|
upPoints.push(geo as LocationPoint);
|
||||||
|
} else {
|
||||||
|
downPoints.push(geo as LocationPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const { selectedView: initialView } = useSelectedView();
|
||||||
|
|
||||||
|
const [selectedView, setSelectedView] = useState(initialView);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiErrorBoundary>
|
||||||
|
<EuiFlexGroup responsive={false} gutterSize={'none'} style={{ flexGrow: 0 }}>
|
||||||
|
{selectedView === 'list' && (
|
||||||
|
<EuiFlexItem>
|
||||||
|
<EuiTitle size="s">
|
||||||
|
<h3>{MonitoringFrom}</h3>
|
||||||
|
</EuiTitle>
|
||||||
|
</EuiFlexItem>
|
||||||
|
)}
|
||||||
|
{selectedView === 'map' && (
|
||||||
|
<EuiFlexItem>{isAnyGeoInfoMissing && <LocationMissingWarning />}</EuiFlexItem>
|
||||||
|
)}
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<ToggleViewBtn
|
||||||
|
onChange={(val) => {
|
||||||
|
setSelectedView(val);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
|
||||||
|
<EuiFlexGroup wrap={true} gutterSize="none" justifyContent="flexEnd">
|
||||||
|
{selectedView === 'list' && (
|
||||||
|
<EuiFlexItemTags grow={true}>
|
||||||
|
<LocationStatusTags locations={monitorLocations?.locations || []} />
|
||||||
|
</EuiFlexItemTags>
|
||||||
|
)}
|
||||||
|
{selectedView === 'map' && (
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<LocationMap upPoints={upPoints} downPoints={downPoints} />
|
||||||
|
</EuiFlexItem>
|
||||||
|
)}
|
||||||
|
</EuiFlexGroup>
|
||||||
|
</EuiErrorBoundary>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* 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 React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { EuiButtonGroup } from '@elastic/eui';
|
||||||
|
import { useSelectedView } from './use_selected_view';
|
||||||
|
import { ChangeToListView, ChangeToMapView } from '../translations';
|
||||||
|
|
||||||
|
const ToggleViewButtons = styled.span`
|
||||||
|
margin-left: auto;
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onChange: (val: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ToggleViewBtn = ({ onChange }: Props) => {
|
||||||
|
const toggleButtons = [
|
||||||
|
{
|
||||||
|
id: `listBtn`,
|
||||||
|
label: ChangeToMapView,
|
||||||
|
name: 'listView',
|
||||||
|
iconType: 'list',
|
||||||
|
'data-test-subj': 'uptimeMonitorToggleListBtn',
|
||||||
|
'aria-label': ChangeToMapView,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: `mapBtn`,
|
||||||
|
label: ChangeToListView,
|
||||||
|
name: 'mapView',
|
||||||
|
iconType: 'mapMarker',
|
||||||
|
'data-test-subj': 'uptimeMonitorToggleMapBtn',
|
||||||
|
'aria-label': ChangeToListView,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { selectedView, setSelectedView } = useSelectedView();
|
||||||
|
|
||||||
|
const onChangeView = (optionId: string) => {
|
||||||
|
const currView = optionId === 'listBtn' ? 'list' : 'map';
|
||||||
|
setSelectedView(currView);
|
||||||
|
onChange(currView);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToggleViewButtons>
|
||||||
|
<EuiButtonGroup
|
||||||
|
options={toggleButtons}
|
||||||
|
idToSelectedMap={{ listBtn: selectedView === 'list', mapBtn: selectedView === 'map' }}
|
||||||
|
onChange={(id) => onChangeView(id)}
|
||||||
|
type="multi"
|
||||||
|
isIconOnly
|
||||||
|
style={{ marginLeft: 'auto' }}
|
||||||
|
/>
|
||||||
|
</ToggleViewButtons>
|
||||||
|
);
|
||||||
|
};
|
|
@ -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 { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
const localKey = 'xpack.uptime.detailPage.selectedView';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
selectedView: string;
|
||||||
|
setSelectedView: (val: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSelectedView = (): Props => {
|
||||||
|
const getSelectedView = localStorage.getItem(localKey) ?? 'list';
|
||||||
|
|
||||||
|
const [selectedView, setSelectedView] = useState(getSelectedView);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem(localKey, selectedView);
|
||||||
|
}, [selectedView]);
|
||||||
|
|
||||||
|
return { selectedView, setSelectedView };
|
||||||
|
};
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`LocationMap component renders correctly against snapshot 1`] = `
|
||||||
|
<styled.div>
|
||||||
|
<EmbeddedMap
|
||||||
|
downPoints={Array []}
|
||||||
|
upPoints={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"location": Object {
|
||||||
|
"lat": "40.730610",
|
||||||
|
"lon": " -73.935242",
|
||||||
|
},
|
||||||
|
"name": "New York",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"location": Object {
|
||||||
|
"lat": "52.487448",
|
||||||
|
"lon": " 13.394798",
|
||||||
|
},
|
||||||
|
"name": "Tokyo",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</styled.div>
|
||||||
|
`;
|
|
@ -4,6 +4,7 @@ exports[`LocationMissingWarning component renders correctly against snapshot 1`]
|
||||||
.c0 {
|
.c0 {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-bottom: 3px;
|
margin-bottom: 3px;
|
||||||
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
<div
|
<div
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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 { LocationPoint } from '../embeddables/embedded_map';
|
||||||
|
|
||||||
|
// Note For shallow test, we need absolute time strings
|
||||||
|
describe('LocationMap component', () => {
|
||||||
|
let upPoints: LocationPoint[];
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
upPoints = [
|
||||||
|
{
|
||||||
|
name: 'New York',
|
||||||
|
location: { lat: '40.730610', lon: ' -73.935242' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Tokyo',
|
||||||
|
location: { lat: '52.487448', lon: ' 13.394798' },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders correctly against snapshot', () => {
|
||||||
|
const component = shallowWithIntl(<LocationMap upPoints={upPoints} downPoints={[]} />);
|
||||||
|
expect(component).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -14,6 +14,7 @@ export const mockDownPointsLayer = {
|
||||||
__featureCollection: {
|
__featureCollection: {
|
||||||
features: [
|
features: [
|
||||||
{
|
{
|
||||||
|
id: 'Asia',
|
||||||
type: 'feature',
|
type: 'feature',
|
||||||
geometry: {
|
geometry: {
|
||||||
type: 'Point',
|
type: 'Point',
|
||||||
|
@ -21,6 +22,7 @@ export const mockDownPointsLayer = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'APJ',
|
||||||
type: 'feature',
|
type: 'feature',
|
||||||
geometry: {
|
geometry: {
|
||||||
type: 'Point',
|
type: 'Point',
|
||||||
|
@ -28,6 +30,7 @@ export const mockDownPointsLayer = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'Canada',
|
||||||
type: 'feature',
|
type: 'feature',
|
||||||
geometry: {
|
geometry: {
|
||||||
type: 'Point',
|
type: 'Point',
|
||||||
|
@ -79,6 +82,7 @@ export const mockUpPointsLayer = {
|
||||||
__featureCollection: {
|
__featureCollection: {
|
||||||
features: [
|
features: [
|
||||||
{
|
{
|
||||||
|
id: 'US-EAST',
|
||||||
type: 'feature',
|
type: 'feature',
|
||||||
geometry: {
|
geometry: {
|
||||||
type: 'Point',
|
type: 'Point',
|
||||||
|
@ -86,6 +90,7 @@ export const mockUpPointsLayer = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'US-WEST',
|
||||||
type: 'feature',
|
type: 'feature',
|
||||||
geometry: {
|
geometry: {
|
||||||
type: 'Point',
|
type: 'Point',
|
||||||
|
@ -93,6 +98,7 @@ export const mockUpPointsLayer = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'Europe',
|
||||||
type: 'feature',
|
type: 'feature',
|
||||||
geometry: {
|
geometry: {
|
||||||
type: 'Point',
|
type: 'Point',
|
|
@ -7,7 +7,7 @@
|
||||||
import { getLayerList } from '../map_config';
|
import { getLayerList } from '../map_config';
|
||||||
import { mockLayerList } from './__mocks__/mock';
|
import { mockLayerList } from './__mocks__/mock';
|
||||||
import { LocationPoint } from '../embedded_map';
|
import { LocationPoint } from '../embedded_map';
|
||||||
import { UptimeAppColors } from '../../../../../uptime_app';
|
import { UptimeAppColors } from '../../../../../../uptime_app';
|
||||||
|
|
||||||
jest.mock('uuid', () => {
|
jest.mock('uuid', () => {
|
||||||
return {
|
return {
|
||||||
|
@ -22,14 +22,14 @@ describe('map_config', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
upPoints = [
|
upPoints = [
|
||||||
{ lat: '52.487239', lon: '13.399262' },
|
{ name: 'US-EAST', location: { lat: '52.487239', lon: '13.399262' } },
|
||||||
{ lat: '55.487239', lon: '13.399262' },
|
{ location: { lat: '55.487239', lon: '13.399262' }, name: 'US-WEST' },
|
||||||
{ lat: '54.487239', lon: '14.399262' },
|
{ location: { lat: '54.487239', lon: '14.399262' }, name: 'Europe' },
|
||||||
];
|
];
|
||||||
downPoints = [
|
downPoints = [
|
||||||
{ lat: '52.487239', lon: '13.399262' },
|
{ location: { lat: '52.487239', lon: '13.399262' }, name: 'Asia' },
|
||||||
{ lat: '55.487239', lon: '13.399262' },
|
{ location: { lat: '55.487239', lon: '13.399262' }, name: 'APJ' },
|
||||||
{ lat: '54.487239', lon: '14.399262' },
|
{ location: { lat: '54.487239', lon: '14.399262' }, name: 'Canada' },
|
||||||
];
|
];
|
||||||
colors = {
|
colors = {
|
||||||
danger: '#BC261E',
|
danger: '#BC261E',
|
|
@ -7,28 +7,35 @@
|
||||||
import React, { useEffect, useState, useContext, useRef } from 'react';
|
import React, { useEffect, useState, useContext, useRef } from 'react';
|
||||||
import uuid from 'uuid';
|
import uuid from 'uuid';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { createPortalNode, InPortal, OutPortal } from 'react-reverse-portal';
|
||||||
import {
|
import {
|
||||||
MapEmbeddable,
|
MapEmbeddable,
|
||||||
MapEmbeddableInput,
|
MapEmbeddableInput,
|
||||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||||
} from '../../../../../../maps/public/embeddable';
|
} from '../../../../../../../maps/public/embeddable';
|
||||||
import * as i18n from './translations';
|
import * as i18n from './translations';
|
||||||
import { Location } from '../../../../../common/runtime_types';
|
import { GeoPoint } from '../../../../../../common/runtime_types';
|
||||||
import { getLayerList } from './map_config';
|
import { getLayerList } from './map_config';
|
||||||
import { UptimeThemeContext, UptimeStartupPluginsContext } from '../../../../contexts';
|
import { UptimeThemeContext, UptimeStartupPluginsContext } from '../../../../../contexts';
|
||||||
|
|
||||||
|
import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../../maps/public';
|
||||||
|
import { MapToolTipComponent } from './map_tool_tip';
|
||||||
import {
|
import {
|
||||||
isErrorEmbeddable,
|
isErrorEmbeddable,
|
||||||
ViewMode,
|
ViewMode,
|
||||||
ErrorEmbeddable,
|
ErrorEmbeddable,
|
||||||
} from '../../../../../../../../src/plugins/embeddable/public';
|
} from '../../../../../../../../../src/plugins/embeddable/public';
|
||||||
import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../maps/public';
|
import {
|
||||||
|
RenderTooltipContentParams,
|
||||||
|
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||||
|
} from '../../../../../../../maps/public/classes/tooltips/tooltip_property';
|
||||||
|
|
||||||
export interface EmbeddedMapProps {
|
export interface EmbeddedMapProps {
|
||||||
upPoints: LocationPoint[];
|
upPoints: LocationPoint[];
|
||||||
downPoints: LocationPoint[];
|
downPoints: LocationPoint[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LocationPoint = Required<Location>;
|
export type LocationPoint = Required<GeoPoint>;
|
||||||
|
|
||||||
const EmbeddedPanel = styled.div`
|
const EmbeddedPanel = styled.div`
|
||||||
z-index: auto;
|
z-index: auto;
|
||||||
|
@ -58,6 +65,8 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp
|
||||||
}
|
}
|
||||||
const factory: any = embeddablePlugin.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE);
|
const factory: any = embeddablePlugin.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE);
|
||||||
|
|
||||||
|
const portalNode = React.useMemo(() => createPortalNode(), []);
|
||||||
|
|
||||||
const input: MapEmbeddableInput = {
|
const input: MapEmbeddableInput = {
|
||||||
id: uuid.v4(),
|
id: uuid.v4(),
|
||||||
filters: [],
|
filters: [],
|
||||||
|
@ -77,12 +86,38 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp
|
||||||
zoom: 0,
|
zoom: 0,
|
||||||
},
|
},
|
||||||
disableInteractive: true,
|
disableInteractive: true,
|
||||||
disableTooltipControl: true,
|
|
||||||
hideToolbarOverlay: true,
|
hideToolbarOverlay: true,
|
||||||
hideLayerControl: true,
|
hideLayerControl: true,
|
||||||
hideViewControl: true,
|
hideViewControl: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderTooltipContent = ({
|
||||||
|
addFilters,
|
||||||
|
closeTooltip,
|
||||||
|
features,
|
||||||
|
isLocked,
|
||||||
|
getLayerName,
|
||||||
|
loadFeatureProperties,
|
||||||
|
loadFeatureGeometry,
|
||||||
|
}: RenderTooltipContentParams) => {
|
||||||
|
const props = {
|
||||||
|
addFilters,
|
||||||
|
closeTooltip,
|
||||||
|
isLocked,
|
||||||
|
getLayerName,
|
||||||
|
loadFeatureProperties,
|
||||||
|
loadFeatureGeometry,
|
||||||
|
};
|
||||||
|
const relevantFeatures = features.filter(
|
||||||
|
(item: any) => item.layerId === 'up_points' || item.layerId === 'down_points'
|
||||||
|
);
|
||||||
|
if (relevantFeatures.length > 0) {
|
||||||
|
return <OutPortal {...props} node={portalNode} features={relevantFeatures} />;
|
||||||
|
}
|
||||||
|
closeTooltip();
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function setupEmbeddable() {
|
async function setupEmbeddable() {
|
||||||
if (!factory) {
|
if (!factory) {
|
||||||
|
@ -94,11 +129,13 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp
|
||||||
});
|
});
|
||||||
|
|
||||||
if (embeddableObject && !isErrorEmbeddable(embeddableObject)) {
|
if (embeddableObject && !isErrorEmbeddable(embeddableObject)) {
|
||||||
embeddableObject.setLayerList(getLayerList(upPoints, downPoints, colors));
|
embeddableObject.setRenderTooltipContent(renderTooltipContent);
|
||||||
|
await embeddableObject.setLayerList(getLayerList(upPoints, downPoints, colors));
|
||||||
}
|
}
|
||||||
|
|
||||||
setEmbeddable(embeddableObject);
|
setEmbeddable(embeddableObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupEmbeddable();
|
setupEmbeddable();
|
||||||
|
|
||||||
// we want this effect to execute exactly once after the component mounts
|
// we want this effect to execute exactly once after the component mounts
|
||||||
|
@ -126,6 +163,9 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp
|
||||||
className="embPanel__content"
|
className="embPanel__content"
|
||||||
ref={embeddableRoot}
|
ref={embeddableRoot}
|
||||||
/>
|
/>
|
||||||
|
<InPortal node={portalNode}>
|
||||||
|
<MapToolTipComponent />
|
||||||
|
</InPortal>
|
||||||
</EmbeddedPanel>
|
</EmbeddedPanel>
|
||||||
);
|
);
|
||||||
});
|
});
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import lowPolyLayerFeatures from './low_poly_layer.json';
|
import lowPolyLayerFeatures from './low_poly_layer.json';
|
||||||
import { LocationPoint } from './embedded_map';
|
import { LocationPoint } from './embedded_map';
|
||||||
import { UptimeAppColors } from '../../../../uptime_app';
|
import { UptimeAppColors } from '../../../../../uptime_app';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns `Source/Destination Point-to-point` Map LayerList configuration, with a source,
|
* Returns `Source/Destination Point-to-point` Map LayerList configuration, with a source,
|
||||||
|
@ -70,9 +70,10 @@ export const getLowPolyLayer = () => {
|
||||||
export const getDownPointsLayer = (downPoints: LocationPoint[], dangerColor: string) => {
|
export const getDownPointsLayer = (downPoints: LocationPoint[], dangerColor: string) => {
|
||||||
const features = downPoints?.map((point) => ({
|
const features = downPoints?.map((point) => ({
|
||||||
type: 'feature',
|
type: 'feature',
|
||||||
|
id: point.name,
|
||||||
geometry: {
|
geometry: {
|
||||||
type: 'Point',
|
type: 'Point',
|
||||||
coordinates: [+point.lon, +point.lat],
|
coordinates: [+point.location.lon, +point.location.lat],
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
return {
|
return {
|
||||||
|
@ -122,9 +123,10 @@ export const getDownPointsLayer = (downPoints: LocationPoint[], dangerColor: str
|
||||||
export const getUpPointsLayer = (upPoints: LocationPoint[]) => {
|
export const getUpPointsLayer = (upPoints: LocationPoint[]) => {
|
||||||
const features = upPoints?.map((point) => ({
|
const features = upPoints?.map((point) => ({
|
||||||
type: 'feature',
|
type: 'feature',
|
||||||
|
id: point.name,
|
||||||
geometry: {
|
geometry: {
|
||||||
type: 'Point',
|
type: 'Point',
|
||||||
coordinates: [+point.lon, +point.lat],
|
coordinates: [+point.location.lon, +point.location.lat],
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
return {
|
return {
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* 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 moment from 'moment';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import {
|
||||||
|
EuiDescriptionList,
|
||||||
|
EuiDescriptionListDescription,
|
||||||
|
EuiDescriptionListTitle,
|
||||||
|
EuiOutsideClickDetector,
|
||||||
|
EuiPopoverTitle,
|
||||||
|
} from '@elastic/eui';
|
||||||
|
import { TagLabel } from '../../availability_reporting';
|
||||||
|
import { UptimeThemeContext } from '../../../../../contexts';
|
||||||
|
import { AppState } from '../../../../../state';
|
||||||
|
import { monitorLocationsSelector } from '../../../../../state/selectors';
|
||||||
|
import { useMonitorId } from '../../../../../hooks';
|
||||||
|
import { MonitorLocation } from '../../../../../../common/runtime_types/monitor';
|
||||||
|
import { formatAvailabilityValue } from '../../availability_reporting/availability_reporting';
|
||||||
|
import { LastCheckLabel } from '../../translations';
|
||||||
|
import {
|
||||||
|
RenderTooltipContentParams,
|
||||||
|
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||||
|
} from '../../../../../../../maps/public/classes/tooltips/tooltip_property';
|
||||||
|
|
||||||
|
type MapToolTipProps = Partial<RenderTooltipContentParams>;
|
||||||
|
|
||||||
|
export const MapToolTipComponent = ({ closeTooltip, features = [] }: MapToolTipProps) => {
|
||||||
|
const { id: featureId, layerId } = features[0] ?? {};
|
||||||
|
const locationName = featureId?.toString();
|
||||||
|
const {
|
||||||
|
colors: { gray, danger },
|
||||||
|
} = useContext(UptimeThemeContext);
|
||||||
|
|
||||||
|
const monitorId = useMonitorId();
|
||||||
|
|
||||||
|
const monitorLocations = useSelector((state: AppState) =>
|
||||||
|
monitorLocationsSelector(state, monitorId)
|
||||||
|
);
|
||||||
|
if (!locationName || !monitorLocations?.locations) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
timestamp,
|
||||||
|
up_history: ups,
|
||||||
|
down_history: downs,
|
||||||
|
}: MonitorLocation = monitorLocations.locations!.find(
|
||||||
|
({ geo }: MonitorLocation) => geo.name === locationName
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const availability = (ups / (ups + downs)) * 100;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiOutsideClickDetector
|
||||||
|
onOutsideClick={() => {
|
||||||
|
if (closeTooltip != null) {
|
||||||
|
closeTooltip();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<>
|
||||||
|
<EuiPopoverTitle>
|
||||||
|
{layerId === 'up_points' ? (
|
||||||
|
<TagLabel label={locationName} color={gray} />
|
||||||
|
) : (
|
||||||
|
<TagLabel label={locationName} color={danger} />
|
||||||
|
)}
|
||||||
|
</EuiPopoverTitle>
|
||||||
|
<EuiDescriptionList type="column" textStyle="reverse" compressed={true}>
|
||||||
|
<EuiDescriptionListTitle>Availability</EuiDescriptionListTitle>
|
||||||
|
<EuiDescriptionListDescription>
|
||||||
|
{i18n.translate('xpack.uptime.mapToolTip.AvailabilityStat.title', {
|
||||||
|
defaultMessage: '{value} %',
|
||||||
|
values: { value: formatAvailabilityValue(availability) },
|
||||||
|
description: 'A percentage value like 23.5%',
|
||||||
|
})}
|
||||||
|
</EuiDescriptionListDescription>
|
||||||
|
<EuiDescriptionListTitle>{LastCheckLabel}</EuiDescriptionListTitle>
|
||||||
|
<EuiDescriptionListDescription>
|
||||||
|
{moment(timestamp).fromNow()}
|
||||||
|
</EuiDescriptionListDescription>
|
||||||
|
</EuiDescriptionList>
|
||||||
|
</>
|
||||||
|
</EuiOutsideClickDetector>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MapToolTip = React.memo(MapToolTipComponent);
|
|
@ -5,4 +5,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './location_map';
|
export * from './location_map';
|
||||||
export * from './location_status_tags';
|
export * from '../availability_reporting/location_status_tags';
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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 styled from 'styled-components';
|
||||||
|
import { EmbeddedMap, LocationPoint } from './embeddables/embedded_map';
|
||||||
|
|
||||||
|
// 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
|
||||||
|
const MapPanel = styled.div`
|
||||||
|
height: 240px;
|
||||||
|
width: 520px;
|
||||||
|
margin-right: 65px;
|
||||||
|
@media (max-width: 574px) {
|
||||||
|
height: 250px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
upPoints: LocationPoint[];
|
||||||
|
downPoints: LocationPoint[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LocationMap = ({ upPoints, downPoints }: Props) => {
|
||||||
|
return (
|
||||||
|
<MapPanel>
|
||||||
|
<EmbeddedMap upPoints={upPoints} downPoints={downPoints} />
|
||||||
|
</MapPanel>
|
||||||
|
);
|
||||||
|
};
|
|
@ -16,11 +16,12 @@ import {
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
import { LocationLink } from '../../common/location_link';
|
import { LocationLink } from '../../../common/location_link';
|
||||||
|
|
||||||
const EuiPopoverRight = styled(EuiFlexItem)`
|
const EuiPopoverRight = styled(EuiFlexItem)`
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-bottom: 3px;
|
margin-bottom: 3px;
|
||||||
|
margin-right: 5px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const LocationMissingWarning = () => {
|
export const LocationMissingWarning = () => {
|
|
@ -5,5 +5,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { MonitorSSLCertificate } from './ssl_certificate';
|
export { MonitorSSLCertificate } from './ssl_certificate';
|
||||||
export { MonitorStatusBarComponent } from './status_bar';
|
export { MonitorStatusBar } from './status_bar';
|
||||||
export { MonitorStatusBar } from './status_bar_container';
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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 { Link } from 'react-router-dom';
|
||||||
|
import { EuiSpacer } from '@elastic/eui';
|
||||||
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
import { Tls } from '../../../../../common/runtime_types';
|
||||||
|
import { CERTIFICATES_ROUTE } from '../../../../../common/constants';
|
||||||
|
import { MonListDescription, MonListTitle } from './status_bar';
|
||||||
|
import { CertStatusColumn } from '../../../overview/monitor_list/cert_status_column';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/**
|
||||||
|
* TLS information coming from monitor in ES heartbeat index
|
||||||
|
*/
|
||||||
|
tls: Tls | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MonitorSSLCertificate = ({ tls }: Props) => {
|
||||||
|
return tls?.not_after ? (
|
||||||
|
<>
|
||||||
|
<MonListTitle>
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.uptime.monitorStatusBar.sslCertificate.title"
|
||||||
|
defaultMessage="TLS Certificate"
|
||||||
|
/>
|
||||||
|
</MonListTitle>
|
||||||
|
|
||||||
|
<EuiSpacer size="s" />
|
||||||
|
<MonListDescription>
|
||||||
|
<Link to={CERTIFICATES_ROUTE} className="eui-displayInline">
|
||||||
|
<CertStatusColumn cert={tls} boldStyle={true} />
|
||||||
|
</Link>
|
||||||
|
</MonListDescription>
|
||||||
|
</>
|
||||||
|
) : null;
|
||||||
|
};
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* 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 styled from 'styled-components';
|
||||||
|
import {
|
||||||
|
EuiLink,
|
||||||
|
EuiIcon,
|
||||||
|
EuiSpacer,
|
||||||
|
EuiDescriptionList,
|
||||||
|
EuiDescriptionListTitle,
|
||||||
|
EuiDescriptionListDescription,
|
||||||
|
} from '@elastic/eui';
|
||||||
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
import { MonitorSSLCertificate } from './ssl_certificate';
|
||||||
|
import * as labels from '../translations';
|
||||||
|
import { StatusByLocations } from './status_by_location';
|
||||||
|
import { useStatusBar } from './use_status_bar';
|
||||||
|
import { MonitorIDLabel, OverallAvailability } from '../translations';
|
||||||
|
import { URL_LABEL } from '../../../common/translations';
|
||||||
|
import { MonitorLocations } from '../../../../../common/runtime_types/monitor';
|
||||||
|
import { formatAvailabilityValue } from '../availability_reporting/availability_reporting';
|
||||||
|
|
||||||
|
export const MonListTitle = styled(EuiDescriptionListTitle)`
|
||||||
|
&&& {
|
||||||
|
width: 35%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const MonListDescription = styled(EuiDescriptionListDescription)`
|
||||||
|
&&& {
|
||||||
|
width: 65%;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const MonitorStatusBar: React.FC = () => {
|
||||||
|
const { monitorId, monitorStatus, monitorLocations = {} } = useStatusBar();
|
||||||
|
|
||||||
|
const { locations, up_history: ups, down_history: downs } = monitorLocations as MonitorLocations;
|
||||||
|
|
||||||
|
const full = monitorStatus?.url?.full ?? '';
|
||||||
|
|
||||||
|
const availability = (ups === 0 && downs === 0) || !ups ? 0 : (ups / (ups + downs)) * 100;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<StatusByLocations locations={locations ?? []} />
|
||||||
|
</div>
|
||||||
|
<EuiSpacer />
|
||||||
|
<EuiDescriptionList
|
||||||
|
type="column"
|
||||||
|
compressed={true}
|
||||||
|
textStyle="reverse"
|
||||||
|
style={{ maxWidth: '450px' }}
|
||||||
|
>
|
||||||
|
<MonListTitle>{OverallAvailability}</MonListTitle>
|
||||||
|
<MonListDescription data-test-subj="uptimeOverallAvailability">
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.uptime.availabilityLabelText"
|
||||||
|
defaultMessage="{value} %"
|
||||||
|
values={{ value: formatAvailabilityValue(availability) }}
|
||||||
|
description="A percentage value, like 23.5 %"
|
||||||
|
/>
|
||||||
|
</MonListDescription>
|
||||||
|
<MonListTitle>{URL_LABEL}</MonListTitle>
|
||||||
|
<MonListDescription>
|
||||||
|
<EuiLink aria-label={labels.monitorUrlLinkAriaLabel} href={full} target="_blank">
|
||||||
|
{full} <EuiIcon type={'popout'} size="s" />
|
||||||
|
</EuiLink>
|
||||||
|
</MonListDescription>
|
||||||
|
<MonListTitle>{MonitorIDLabel}</MonListTitle>
|
||||||
|
<MonListDescription data-test-subj="monitor-page-title">{monitorId}</MonListDescription>
|
||||||
|
<MonitorSSLCertificate tls={monitorStatus?.tls} />
|
||||||
|
</EuiDescriptionList>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -4,24 +4,33 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useContext, useEffect } from 'react';
|
import { useContext, useEffect } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { monitorLocationsSelector, monitorStatusSelector } from '../../../../state/selectors';
|
|
||||||
import { MonitorStatusBarComponent } from './index';
|
|
||||||
import { getMonitorStatusAction } from '../../../../state/actions';
|
|
||||||
import { useGetUrlParams } from '../../../../hooks';
|
|
||||||
import { UptimeRefreshContext } from '../../../../contexts';
|
import { UptimeRefreshContext } from '../../../../contexts';
|
||||||
import { MonitorIdParam } from '../../../../../common/types';
|
import { useGetUrlParams, useMonitorId } from '../../../../hooks';
|
||||||
|
import { monitorLocationsSelector, monitorStatusSelector } from '../../../../state/selectors';
|
||||||
import { AppState } from '../../../../state';
|
import { AppState } from '../../../../state';
|
||||||
|
import { getMonitorStatusAction } from '../../../../state/actions';
|
||||||
|
import { Ping } from '../../../../../common/runtime_types/ping';
|
||||||
|
import { MonitorLocations } from '../../../../../common/runtime_types/monitor';
|
||||||
|
|
||||||
export const MonitorStatusBar: React.FC<MonitorIdParam> = ({ monitorId }) => {
|
interface MonitorStatusBarProps {
|
||||||
|
monitorId: string;
|
||||||
|
monitorStatus: Ping | null;
|
||||||
|
monitorLocations?: MonitorLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useStatusBar = (): MonitorStatusBarProps => {
|
||||||
const { lastRefresh } = useContext(UptimeRefreshContext);
|
const { lastRefresh } = useContext(UptimeRefreshContext);
|
||||||
|
|
||||||
const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = useGetUrlParams();
|
const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = useGetUrlParams();
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const monitorId = useMonitorId();
|
||||||
|
|
||||||
const monitorStatus = useSelector(monitorStatusSelector);
|
const monitorStatus = useSelector(monitorStatusSelector);
|
||||||
|
|
||||||
const monitorLocations = useSelector((state: AppState) =>
|
const monitorLocations = useSelector((state: AppState) =>
|
||||||
monitorLocationsSelector(state, monitorId)
|
monitorLocationsSelector(state, monitorId)
|
||||||
);
|
);
|
||||||
|
@ -30,11 +39,5 @@ export const MonitorStatusBar: React.FC<MonitorIdParam> = ({ monitorId }) => {
|
||||||
dispatch(getMonitorStatusAction({ dateStart, dateEnd, monitorId }));
|
dispatch(getMonitorStatusAction({ dateStart, dateEnd, monitorId }));
|
||||||
}, [monitorId, dateStart, dateEnd, lastRefresh, dispatch]);
|
}, [monitorId, dateStart, dateEnd, lastRefresh, dispatch]);
|
||||||
|
|
||||||
return (
|
return { monitorStatus, monitorLocations, monitorId };
|
||||||
<MonitorStatusBarComponent
|
|
||||||
monitorId={monitorId}
|
|
||||||
monitorStatus={monitorStatus}
|
|
||||||
monitorLocations={monitorLocations!}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
|
@ -7,31 +7,24 @@
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
|
import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { LocationMap } from '../location_map';
|
import { LocationAvailability } from './location_availability/location_availability';
|
||||||
import { UptimeRefreshContext } from '../../../contexts';
|
import { UptimeRefreshContext } from '../../../contexts';
|
||||||
import { MonitorLocations } from '../../../../common/runtime_types';
|
import { MonitorLocations } from '../../../../common/runtime_types';
|
||||||
import { MonitorStatusBar } from './monitor_status_bar';
|
import { MonitorStatusBar } from './status_bar';
|
||||||
|
|
||||||
interface MonitorStatusDetailsProps {
|
interface MonitorStatusDetailsProps {
|
||||||
monitorId: string;
|
|
||||||
monitorLocations: MonitorLocations;
|
monitorLocations: MonitorLocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
const WrapFlexItem = styled(EuiFlexItem)`
|
const WrapFlexItem = styled(EuiFlexItem)`
|
||||||
&&& {
|
&&& {
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 800px) {
|
||||||
width: 100%;
|
flex-basis: 100%;
|
||||||
}
|
|
||||||
@media (max-width: 1042px) {
|
|
||||||
flex-basis: 520px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const MonitorStatusDetailsComponent = ({
|
export const MonitorStatusDetailsComponent = ({ monitorLocations }: MonitorStatusDetailsProps) => {
|
||||||
monitorId,
|
|
||||||
monitorLocations,
|
|
||||||
}: MonitorStatusDetailsProps) => {
|
|
||||||
const { refreshApp } = useContext(UptimeRefreshContext);
|
const { refreshApp } = useContext(UptimeRefreshContext);
|
||||||
|
|
||||||
const [isTabActive] = useState(document.visibilityState);
|
const [isTabActive] = useState(document.visibilityState);
|
||||||
|
@ -56,12 +49,12 @@ export const MonitorStatusDetailsComponent = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiPanel>
|
<EuiPanel>
|
||||||
<EuiFlexGroup gutterSize="l" wrap responsive={true}>
|
<EuiFlexGroup gutterSize="l" wrap={true} responsive={true}>
|
||||||
<EuiFlexItem grow={true}>
|
<EuiFlexItem grow={1}>
|
||||||
<MonitorStatusBar monitorId={monitorId} />
|
<MonitorStatusBar />
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
<WrapFlexItem grow={false}>
|
<WrapFlexItem grow={1}>
|
||||||
<LocationMap monitorLocations={monitorLocations} />
|
<LocationAvailability monitorLocations={monitorLocations} />
|
||||||
</WrapFlexItem>
|
</WrapFlexItem>
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
</EuiPanel>
|
</EuiPanel>
|
|
@ -28,7 +28,5 @@ export const MonitorStatusDetails: React.FC<MonitorIdParam> = ({ monitorId }) =>
|
||||||
dispatch(getMonitorLocationsAction({ dateStart, dateEnd, monitorId }));
|
dispatch(getMonitorLocationsAction({ dateStart, dateEnd, monitorId }));
|
||||||
}, [monitorId, dateStart, dateEnd, lastRefresh, dispatch]);
|
}, [monitorId, dateStart, dateEnd, lastRefresh, dispatch]);
|
||||||
|
|
||||||
return (
|
return <MonitorStatusDetailsComponent monitorLocations={monitorLocations!} />;
|
||||||
<MonitorStatusDetailsComponent monitorId={monitorId} monitorLocations={monitorLocations!} />
|
|
||||||
);
|
|
||||||
};
|
};
|
|
@ -48,3 +48,56 @@ export const timestampFromNowTextAriaLabel = i18n.translate(
|
||||||
export const loadingMessage = i18n.translate('xpack.uptime.monitorStatusBar.loadingMessage', {
|
export const loadingMessage = i18n.translate('xpack.uptime.monitorStatusBar.loadingMessage', {
|
||||||
defaultMessage: 'Loading…',
|
defaultMessage: 'Loading…',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const MonitorIDLabel = i18n.translate('xpack.uptime.monitorStatusBar.monitor.id', {
|
||||||
|
defaultMessage: 'Monitor ID',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const OverallAvailability = i18n.translate(
|
||||||
|
'xpack.uptime.monitorStatusBar.monitor.availability',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Overall availability',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const MonitoringFrom = i18n.translate(
|
||||||
|
'xpack.uptime.monitorStatusBar.monitor.monitoringFrom',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Monitoring from',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ChangeToMapView = i18n.translate(
|
||||||
|
'xpack.uptime.monitorStatusBar.monitor.monitoringFrom.listToMap',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Change to map view to check availability by location.',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ChangeToListView = i18n.translate(
|
||||||
|
'xpack.uptime.monitorStatusBar.monitor.monitoringFrom.MapToList',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Change to list view to check availability by location.',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const LocationLabel = i18n.translate(
|
||||||
|
'xpack.uptime.monitorStatusBar.monitor.availabilityReport.location',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Location',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const AvailabilityLabel = i18n.translate(
|
||||||
|
'xpack.uptime.monitorStatusBar.monitor.availabilityReport.availability',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Availability',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const LastCheckLabel = i18n.translate(
|
||||||
|
'xpack.uptime.monitorStatusBar.monitor.availabilityReport.lastCheck',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Last check',
|
||||||
|
}
|
||||||
|
);
|
|
@ -8,13 +8,14 @@ import React from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { EuiIcon, EuiText, EuiToolTip } from '@elastic/eui';
|
import { EuiIcon, EuiText, EuiToolTip } from '@elastic/eui';
|
||||||
import { Cert } from '../../../../common/runtime_types';
|
import { Cert, Tls } from '../../../../common/runtime_types';
|
||||||
import { useCertStatus } from '../../../hooks';
|
import { useCertStatus } from '../../../hooks';
|
||||||
import { EXPIRED, EXPIRES_SOON } from '../../certificates/translations';
|
import { EXPIRED, EXPIRES, EXPIRES_SOON } from '../../certificates/translations';
|
||||||
import { CERT_STATUS } from '../../../../common/constants';
|
import { CERT_STATUS } from '../../../../common/constants';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
cert: Cert;
|
cert: Cert | Tls;
|
||||||
|
boldStyle?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Span = styled.span`
|
const Span = styled.span`
|
||||||
|
@ -22,7 +23,15 @@ const Span = styled.span`
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CertStatusColumn: React.FC<Props> = ({ cert }) => {
|
const H4Text = styled.h4`
|
||||||
|
&&& {
|
||||||
|
margin: 0 0 0 4px;
|
||||||
|
display: inline-block;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CertStatusColumn: React.FC<Props> = ({ cert, boldStyle = false }) => {
|
||||||
const certStatus = useCertStatus(cert?.not_after);
|
const certStatus = useCertStatus(cert?.not_after);
|
||||||
|
|
||||||
const relativeDate = moment(cert?.not_after).fromNow();
|
const relativeDate = moment(cert?.not_after).fromNow();
|
||||||
|
@ -32,9 +41,15 @@ export const CertStatusColumn: React.FC<Props> = ({ cert }) => {
|
||||||
<EuiToolTip content={moment(cert?.not_after).format('L LT')}>
|
<EuiToolTip content={moment(cert?.not_after).format('L LT')}>
|
||||||
<EuiText size="s">
|
<EuiText size="s">
|
||||||
<EuiIcon color={color} type="lock" size="s" />
|
<EuiIcon color={color} type="lock" size="s" />
|
||||||
<Span>
|
{boldStyle ? (
|
||||||
{text} {relativeDate}
|
<H4Text>
|
||||||
</Span>
|
{text} {relativeDate}
|
||||||
|
</H4Text>
|
||||||
|
) : (
|
||||||
|
<Span>
|
||||||
|
{text} {relativeDate}
|
||||||
|
</Span>
|
||||||
|
)}
|
||||||
</EuiText>
|
</EuiText>
|
||||||
</EuiToolTip>
|
</EuiToolTip>
|
||||||
);
|
);
|
||||||
|
@ -47,5 +62,5 @@ export const CertStatusColumn: React.FC<Props> = ({ cert }) => {
|
||||||
return <CertStatus color="danger" text={EXPIRED} />;
|
return <CertStatus color="danger" text={EXPIRED} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return certStatus ? <CertStatus color="success" text={'Expires'} /> : <span>--</span>;
|
return certStatus ? <CertStatus color="success" text={EXPIRES} /> : <span>--</span>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -30,6 +30,7 @@ import { MonitorListProps } from './monitor_list_container';
|
||||||
import { MonitorList } from '../../../state/reducers/monitor_list';
|
import { MonitorList } from '../../../state/reducers/monitor_list';
|
||||||
import { CertStatusColumn } from './cert_status_column';
|
import { CertStatusColumn } from './cert_status_column';
|
||||||
import { MonitorListHeader } from './monitor_list_header';
|
import { MonitorListHeader } from './monitor_list_header';
|
||||||
|
import { URL_LABEL } from '../../common/translations';
|
||||||
|
|
||||||
interface Props extends MonitorListProps {
|
interface Props extends MonitorListProps {
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
|
@ -106,7 +107,7 @@ export const MonitorListComponent: React.FC<Props> = ({
|
||||||
{
|
{
|
||||||
align: 'left' as const,
|
align: 'left' as const,
|
||||||
field: 'state.url.full',
|
field: 'state.url.full',
|
||||||
name: labels.URL,
|
name: URL_LABEL,
|
||||||
render: (url: string, summary: MonitorSummary) => (
|
render: (url: string, summary: MonitorSummary) => (
|
||||||
<TruncatedEuiLink href={url} target="_blank" color="text">
|
<TruncatedEuiLink href={url} target="_blank" color="text">
|
||||||
{url} <EuiIcon size="s" type="popout" color="subbdued" />
|
{url} <EuiIcon size="s" type="popout" color="subbdued" />
|
||||||
|
|
|
@ -62,10 +62,6 @@ export const NO_DATA_MESSAGE = i18n.translate('xpack.uptime.monitorList.noItemMe
|
||||||
description: 'This message is shown if the monitors table is rendered but has no items.',
|
description: 'This message is shown if the monitors table is rendered but has no items.',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const URL = i18n.translate('xpack.uptime.monitorList.table.url.name', {
|
|
||||||
defaultMessage: 'Url',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const UP = i18n.translate('xpack.uptime.monitorList.statusColumn.upLabel', {
|
export const UP = i18n.translate('xpack.uptime.monitorList.statusColumn.upLabel', {
|
||||||
defaultMessage: 'Up',
|
defaultMessage: 'Up',
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,21 +2,25 @@
|
||||||
|
|
||||||
exports[`PageHeader shallow renders extra links: page_header_with_extra_links 1`] = `
|
exports[`PageHeader shallow renders extra links: page_header_with_extra_links 1`] = `
|
||||||
Array [
|
Array [
|
||||||
@media only screen and (max-width:1024px) and (min-width:868px) {
|
.c0 {
|
||||||
.c0.c0.c0 .euiSuperDatePicker__flexWrapper {
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width:1024px) and (min-width:868px) {
|
||||||
|
.c1.c1.c1 .euiSuperDatePicker__flexWrapper {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width:880px) {
|
@media only screen and (max-width:880px) {
|
||||||
.c0.c0.c0 {
|
.c1.c1.c1 {
|
||||||
-webkit-box-flex: 1;
|
-webkit-box-flex: 1;
|
||||||
-webkit-flex-grow: 1;
|
-webkit-flex-grow: 1;
|
||||||
-ms-flex-positive: 1;
|
-ms-flex-positive: 1;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c0.c0.c0 .euiSuperDatePicker__flexWrapper {
|
.c1.c1.c1 .euiSuperDatePicker__flexWrapper {
|
||||||
width: calc(100% + 8px);
|
width: calc(100% + 8px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +32,7 @@ Array [
|
||||||
class="euiFlexItem"
|
class="euiFlexItem"
|
||||||
>
|
>
|
||||||
<h1
|
<h1
|
||||||
class="euiTitle euiTitle--medium"
|
class="c0 euiTitle euiTitle--medium"
|
||||||
>
|
>
|
||||||
TestingHeading
|
TestingHeading
|
||||||
</h1>
|
</h1>
|
||||||
|
@ -126,7 +130,7 @@ Array [
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexItem euiFlexItem--flexGrowZero c0"
|
class="euiFlexItem euiFlexItem--flexGrowZero c1"
|
||||||
style="flex-basis:485px"
|
style="flex-basis:485px"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -238,21 +242,25 @@ Array [
|
||||||
|
|
||||||
exports[`PageHeader shallow renders with the date picker: page_header_with_date_picker 1`] = `
|
exports[`PageHeader shallow renders with the date picker: page_header_with_date_picker 1`] = `
|
||||||
Array [
|
Array [
|
||||||
@media only screen and (max-width:1024px) and (min-width:868px) {
|
.c0 {
|
||||||
.c0.c0.c0 .euiSuperDatePicker__flexWrapper {
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width:1024px) and (min-width:868px) {
|
||||||
|
.c1.c1.c1 .euiSuperDatePicker__flexWrapper {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width:880px) {
|
@media only screen and (max-width:880px) {
|
||||||
.c0.c0.c0 {
|
.c1.c1.c1 {
|
||||||
-webkit-box-flex: 1;
|
-webkit-box-flex: 1;
|
||||||
-webkit-flex-grow: 1;
|
-webkit-flex-grow: 1;
|
||||||
-ms-flex-positive: 1;
|
-ms-flex-positive: 1;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c0.c0.c0 .euiSuperDatePicker__flexWrapper {
|
.c1.c1.c1 .euiSuperDatePicker__flexWrapper {
|
||||||
width: calc(100% + 8px);
|
width: calc(100% + 8px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,7 +272,7 @@ Array [
|
||||||
class="euiFlexItem"
|
class="euiFlexItem"
|
||||||
>
|
>
|
||||||
<h1
|
<h1
|
||||||
class="euiTitle euiTitle--medium"
|
class="c0 euiTitle euiTitle--medium"
|
||||||
>
|
>
|
||||||
TestingHeading
|
TestingHeading
|
||||||
</h1>
|
</h1>
|
||||||
|
@ -273,7 +281,7 @@ Array [
|
||||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="euiFlexItem euiFlexItem--flexGrowZero c0"
|
class="euiFlexItem euiFlexItem--flexGrowZero c1"
|
||||||
style="flex-basis:485px"
|
style="flex-basis:485px"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -385,14 +393,18 @@ Array [
|
||||||
|
|
||||||
exports[`PageHeader shallow renders without the date picker: page_header_no_date_picker 1`] = `
|
exports[`PageHeader shallow renders without the date picker: page_header_no_date_picker 1`] = `
|
||||||
Array [
|
Array [
|
||||||
<div
|
.c0 {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow euiFlexGroup--wrap"
|
class="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow euiFlexGroup--wrap"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="euiFlexItem"
|
class="euiFlexItem"
|
||||||
>
|
>
|
||||||
<h1
|
<h1
|
||||||
class="euiTitle euiTitle--medium"
|
class="c0 euiTitle euiTitle--medium"
|
||||||
>
|
>
|
||||||
TestingHeading
|
TestingHeading
|
||||||
</h1>
|
</h1>
|
||||||
|
|
|
@ -19,6 +19,7 @@ interface PageHeaderProps {
|
||||||
extraLinks?: boolean;
|
extraLinks?: boolean;
|
||||||
datePicker?: boolean;
|
datePicker?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SETTINGS_LINK_TEXT = i18n.translate('xpack.uptime.page_header.settingsLink', {
|
const SETTINGS_LINK_TEXT = i18n.translate('xpack.uptime.page_header.settingsLink', {
|
||||||
defaultMessage: 'Settings',
|
defaultMessage: 'Settings',
|
||||||
});
|
});
|
||||||
|
@ -43,6 +44,10 @@ const StyledPicker = styled(EuiFlexItem)`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const H1Text = styled.h1`
|
||||||
|
white-space: nowrap;
|
||||||
|
`;
|
||||||
|
|
||||||
export const PageHeader = React.memo(
|
export const PageHeader = React.memo(
|
||||||
({ headingText, extraLinks = false, datePicker = true }: PageHeaderProps) => {
|
({ headingText, extraLinks = false, datePicker = true }: PageHeaderProps) => {
|
||||||
const DatePickerComponent = () =>
|
const DatePickerComponent = () =>
|
||||||
|
@ -89,7 +94,7 @@ export const PageHeader = React.memo(
|
||||||
>
|
>
|
||||||
<EuiFlexItem grow={true}>
|
<EuiFlexItem grow={true}>
|
||||||
<EuiTitle>
|
<EuiTitle>
|
||||||
<h1>{headingText}</h1>
|
<H1Text>{headingText}</H1Text>
|
||||||
</EuiTitle>
|
</EuiTitle>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
<EuiFlexItem grow={false}>{extraLinkComponents}</EuiFlexItem>
|
<EuiFlexItem grow={false}>{extraLinkComponents}</EuiFlexItem>
|
||||||
|
|
|
@ -32,7 +32,7 @@ export const getMonitorLocations: UMElasticsearchQueryFn<
|
||||||
bool: {
|
bool: {
|
||||||
filter: [
|
filter: [
|
||||||
{
|
{
|
||||||
match: {
|
term: {
|
||||||
'monitor.id': monitorId,
|
'monitor.id': monitorId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -70,6 +70,18 @@ export const getMonitorLocations: UMElasticsearchQueryFn<
|
||||||
_source: ['monitor', 'summary', 'observer', '@timestamp'],
|
_source: ['monitor', 'summary', 'observer', '@timestamp'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
down_history: {
|
||||||
|
sum: {
|
||||||
|
field: 'summary.down',
|
||||||
|
missing: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
up_history: {
|
||||||
|
sum: {
|
||||||
|
field: 'summary.up',
|
||||||
|
missing: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -99,10 +111,17 @@ export const getMonitorLocations: UMElasticsearchQueryFn<
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let totalUps = 0;
|
||||||
|
let totalDowns = 0;
|
||||||
|
|
||||||
const monLocs: MonitorLocation[] = [];
|
const monLocs: MonitorLocation[] = [];
|
||||||
locations.forEach((loc: any) => {
|
locations.forEach(({ most_recent: mostRecent, up_history, down_history }: any) => {
|
||||||
const mostRecentLocation = loc.most_recent.hits.hits[0]._source;
|
const mostRecentLocation = mostRecent.hits.hits[0]._source;
|
||||||
|
totalUps += up_history.value;
|
||||||
|
totalDowns += down_history.value;
|
||||||
const location: MonitorLocation = {
|
const location: MonitorLocation = {
|
||||||
|
up_history: up_history.value ?? 0,
|
||||||
|
down_history: down_history.value ?? 0,
|
||||||
summary: mostRecentLocation?.summary,
|
summary: mostRecentLocation?.summary,
|
||||||
geo: getGeo(mostRecentLocation?.observer?.geo),
|
geo: getGeo(mostRecentLocation?.observer?.geo),
|
||||||
timestamp: mostRecentLocation['@timestamp'],
|
timestamp: mostRecentLocation['@timestamp'],
|
||||||
|
@ -113,5 +132,7 @@ export const getMonitorLocations: UMElasticsearchQueryFn<
|
||||||
return {
|
return {
|
||||||
monitorId,
|
monitorId,
|
||||||
locations: monLocs,
|
locations: monLocs,
|
||||||
|
up_history: totalUps,
|
||||||
|
down_history: totalDowns,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -65,7 +65,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||||
|
|
||||||
it('detail page', async () => {
|
it('detail page', async () => {
|
||||||
await uptimeService.navigation.goToMonitor(A11Y_TEST_MONITOR_ID);
|
await uptimeService.navigation.goToMonitor(A11Y_TEST_MONITOR_ID);
|
||||||
await uptimeService.monitor.locationMapIsRendered();
|
await uptimeService.monitor.displayOverallAvailability('0.00 %');
|
||||||
await a11y.testAppSnapshot();
|
await a11y.testAppSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -11,41 +11,58 @@ import { FtrProviderContext } from '../../ftr_provider_context';
|
||||||
export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||||
const { uptime: uptimePage } = getPageObjects(['uptime']);
|
const { uptime: uptimePage } = getPageObjects(['uptime']);
|
||||||
const uptime = getService('uptime');
|
const uptime = getService('uptime');
|
||||||
|
const es = getService('legacyEs');
|
||||||
|
|
||||||
const monitor = () => uptime.monitor;
|
const monitor = () => uptime.monitor;
|
||||||
|
const MONITOR_ID = 'location-testing-id';
|
||||||
|
|
||||||
|
const LessAvailMonitor = 'less-availability-monitor';
|
||||||
|
|
||||||
|
const addMonitorWithNoLocation = async () => {
|
||||||
|
/**
|
||||||
|
* This mogrify function will strip the documents of their location
|
||||||
|
* data (but preserve their location name), which is necessary for
|
||||||
|
* this test to work as desired.
|
||||||
|
* @param d current document
|
||||||
|
*/
|
||||||
|
const mogrifyNoLocation = (d: any) => {
|
||||||
|
if (d.observer?.geo?.location) {
|
||||||
|
d.observer.geo.location = undefined;
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
};
|
||||||
|
await makeChecksWithStatus(es, MONITOR_ID, 5, 2, 10000, {}, 'up', mogrifyNoLocation);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addLessAvailMonitor = async () => {
|
||||||
|
await makeChecksWithStatus(es, LessAvailMonitor, 5, 2, 10000, {}, 'up');
|
||||||
|
await makeChecksWithStatus(es, LessAvailMonitor, 5, 2, 10000, {}, 'down');
|
||||||
|
};
|
||||||
|
|
||||||
describe('Observer location', () => {
|
describe('Observer location', () => {
|
||||||
const start = moment().subtract('15', 'm').toISOString();
|
const start = moment().subtract('15', 'm').toISOString();
|
||||||
const end = moment().toISOString();
|
const end = moment().toISOString();
|
||||||
|
|
||||||
const MONITOR_ID = 'location-testing-id';
|
before(async () => {
|
||||||
|
await addMonitorWithNoLocation();
|
||||||
|
await addLessAvailMonitor();
|
||||||
|
await uptime.navigation.goToUptime();
|
||||||
|
await uptimePage.goToRoot(true);
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
/**
|
await addMonitorWithNoLocation();
|
||||||
* This mogrify function will strip the documents of their location
|
await addLessAvailMonitor();
|
||||||
* data (but preserve their location name), which is necessary for
|
if (!(await uptime.navigation.isOnDetailsPage()))
|
||||||
* this test to work as desired.
|
await uptimePage.loadDataAndGoToMonitorPage(start, end, MONITOR_ID);
|
||||||
* @param d current document
|
});
|
||||||
*/
|
|
||||||
const mogrifyNoLocation = (d: any) => {
|
|
||||||
if (d.observer?.geo?.location) {
|
|
||||||
d.observer.geo.location = undefined;
|
|
||||||
}
|
|
||||||
return d;
|
|
||||||
};
|
|
||||||
await makeChecksWithStatus(
|
|
||||||
getService('legacyEs'),
|
|
||||||
MONITOR_ID,
|
|
||||||
5,
|
|
||||||
2,
|
|
||||||
10000,
|
|
||||||
{},
|
|
||||||
'up',
|
|
||||||
mogrifyNoLocation
|
|
||||||
);
|
|
||||||
await uptime.navigation.goToUptime();
|
|
||||||
|
|
||||||
await uptimePage.loadDataAndGoToMonitorPage(start, end, MONITOR_ID);
|
it('displays the overall availability', async () => {
|
||||||
|
await monitor().displayOverallAvailability('100.00 %');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can change the view to map', async () => {
|
||||||
|
await monitor().toggleToMapView();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders the location panel and canvas', async () => {
|
it('renders the location panel and canvas', async () => {
|
||||||
|
@ -55,5 +72,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||||
it('renders the location missing popover when monitor has location name, but no geo data', async () => {
|
it('renders the location missing popover when monitor has location name, but no geo data', async () => {
|
||||||
await monitor().locationMissingExists();
|
await monitor().locationMissingExists();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('displays less monitor availability', async () => {
|
||||||
|
await uptime.navigation.goToHomeViaBreadCrumb();
|
||||||
|
await uptimePage.loadDataAndGoToMonitorPage(start, end, LessAvailMonitor);
|
||||||
|
await monitor().displayOverallAvailability('50.00 %');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -58,13 +58,13 @@ export function UptimeCommonProvider({ getService }: FtrProviderContext) {
|
||||||
'[data-test-subj="xpack.uptime.filterBar.filterStatusUp"]'
|
'[data-test-subj="xpack.uptime.filterBar.filterStatusUp"]'
|
||||||
);
|
);
|
||||||
if (await upFilter.elementHasClass('euiFilterButton-hasActiveFilters')) {
|
if (await upFilter.elementHasClass('euiFilterButton-hasActiveFilters')) {
|
||||||
this.setStatusFilterUp();
|
await this.setStatusFilterUp();
|
||||||
}
|
}
|
||||||
const downFilter = await find.byCssSelector(
|
const downFilter = await find.byCssSelector(
|
||||||
'[data-test-subj="xpack.uptime.filterBar.filterStatusDown"]'
|
'[data-test-subj="xpack.uptime.filterBar.filterStatusDown"]'
|
||||||
);
|
);
|
||||||
if (await downFilter.elementHasClass('euiFilterButton-hasActiveFilters')) {
|
if (await downFilter.elementHasClass('euiFilterButton-hasActiveFilters')) {
|
||||||
this.setStatusFilterDown();
|
await this.setStatusFilterDown();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async selectFilterItem(filterType: string, option: string) {
|
async selectFilterItem(filterType: string, option: string) {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import expect from '@kbn/expect/expect.js';
|
||||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||||
|
|
||||||
export function UptimeMonitorProvider({ getService }: FtrProviderContext) {
|
export function UptimeMonitorProvider({ getService }: FtrProviderContext) {
|
||||||
|
@ -17,6 +18,13 @@ export function UptimeMonitorProvider({ getService }: FtrProviderContext) {
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
async displayOverallAvailability(availabilityVal: string) {
|
||||||
|
return retry.tryForTime(60 * 1000, async () => {
|
||||||
|
await testSubjects.existOrFail('uptimeOverallAvailability');
|
||||||
|
const availability = await testSubjects.getVisibleText('uptimeOverallAvailability');
|
||||||
|
expect(availability).to.be(availabilityVal);
|
||||||
|
});
|
||||||
|
},
|
||||||
async locationMapIsRendered() {
|
async locationMapIsRendered() {
|
||||||
return retry.tryForTime(15000, async () => {
|
return retry.tryForTime(15000, async () => {
|
||||||
await testSubjects.existOrFail('xpack.uptime.locationMap.embeddedPanel', {
|
await testSubjects.existOrFail('xpack.uptime.locationMap.embeddedPanel', {
|
||||||
|
@ -45,5 +53,8 @@ export function UptimeMonitorProvider({ getService }: FtrProviderContext) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
async toggleToMapView() {
|
||||||
|
await testSubjects.click('uptimeMonitorToggleMapBtn');
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ export function UptimeNavigationProvider({ getService, getPageObjects }: FtrProv
|
||||||
},
|
},
|
||||||
|
|
||||||
goToMonitor: async (monitorId: string) => {
|
goToMonitor: async (monitorId: string) => {
|
||||||
|
// only go to monitor page if not already there
|
||||||
if (!(await testSubjects.exists('uptimeMonitorPage', { timeout: 0 }))) {
|
if (!(await testSubjects.exists('uptimeMonitorPage', { timeout: 0 }))) {
|
||||||
await testSubjects.click(`monitor-page-link-${monitorId}`);
|
await testSubjects.click(`monitor-page-link-${monitorId}`);
|
||||||
await testSubjects.existOrFail('uptimeMonitorPage', {
|
await testSubjects.existOrFail('uptimeMonitorPage', {
|
||||||
|
@ -80,5 +81,13 @@ export function UptimeNavigationProvider({ getService, getPageObjects }: FtrProv
|
||||||
await PageObjects.timePicker.setAbsoluteRange(dateStart, dateEnd);
|
await PageObjects.timePicker.setAbsoluteRange(dateStart, dateEnd);
|
||||||
await this.goToMonitor(monitorId);
|
await this.goToMonitor(monitorId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async isOnDetailsPage() {
|
||||||
|
return await testSubjects.exists('uptimeMonitorPage', { timeout: 0 });
|
||||||
|
},
|
||||||
|
|
||||||
|
async goToHomeViaBreadCrumb() {
|
||||||
|
await testSubjects.click('breadcrumb first');
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue