mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Adds integration links for Infrastructure UI.
This commit is contained in:
parent
adb433530e
commit
b43386173c
11 changed files with 329 additions and 16 deletions
|
@ -1,9 +1,34 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`IntegrationLink component renders a disabled link when href is undefined 1`] = `
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<EuiToolTip
|
||||
content="Required data for this integration was not found."
|
||||
delay="regular"
|
||||
position="top"
|
||||
>
|
||||
<EuiIcon
|
||||
type="apmApp"
|
||||
/>
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText
|
||||
color="subdued"
|
||||
>
|
||||
click for bar
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`IntegrationLink component renders without errors 1`] = `
|
||||
<EuiLink
|
||||
aria-label="foo"
|
||||
color="subdued"
|
||||
color="primary"
|
||||
href="/app/foo?kuery=localhost"
|
||||
type="button"
|
||||
>
|
||||
|
|
|
@ -21,4 +21,16 @@ describe('IntegrationLink component', () => {
|
|||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it('renders a disabled link when href is undefined', () => {
|
||||
const component = shallowWithIntl(
|
||||
<IntegrationLink
|
||||
ariaLabel="foo"
|
||||
href={undefined}
|
||||
iconType="apmApp"
|
||||
message="click for bar"
|
||||
tooltipContent="info for bar"
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiToolTip } from '@elastic/eui';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiText, EuiToolTip } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
interface IntegrationLinkProps {
|
||||
ariaLabel: string;
|
||||
href: string;
|
||||
href: string | undefined;
|
||||
iconType: 'apmApp' | 'infraApp' | 'loggingApp';
|
||||
message: string;
|
||||
tooltipContent: string;
|
||||
|
@ -21,15 +22,31 @@ export const IntegrationLink = ({
|
|||
iconType,
|
||||
message,
|
||||
tooltipContent,
|
||||
}: IntegrationLinkProps) => (
|
||||
<EuiLink aria-label={ariaLabel} color="subdued" href={href}>
|
||||
}: IntegrationLinkProps) =>
|
||||
typeof href === 'undefined' ? (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip content={tooltipContent} position="top">
|
||||
<EuiToolTip
|
||||
content={i18n.translate('xpack.uptime.integrationLink.missingDataMessage', {
|
||||
defaultMessage: 'Required data for this integration was not found.',
|
||||
})}
|
||||
>
|
||||
<EuiIcon type={iconType} />
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>{message}</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText color="subdued">{message}</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiLink>
|
||||
);
|
||||
) : (
|
||||
<EuiLink aria-label={ariaLabel} href={href}>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip content={tooltipContent} position="top">
|
||||
<EuiIcon type={iconType} />
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>{message}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiLink>
|
||||
);
|
||||
|
|
|
@ -10,7 +10,12 @@ import { i18n } from '@kbn/i18n';
|
|||
import { get } from 'lodash';
|
||||
import { LatestMonitor } from '../../../common/graphql/types';
|
||||
import { IntegrationLink } from './integration_link';
|
||||
import { getApmHref } from '../../lib/helper';
|
||||
import {
|
||||
getApmHref,
|
||||
getInfraContainerHref,
|
||||
getInfraIpHref,
|
||||
getInfraKubernetesHref,
|
||||
} from '../../lib/helper';
|
||||
|
||||
interface MonitorListActionsPopoverProps {
|
||||
basePath: string;
|
||||
|
@ -28,7 +33,10 @@ export const MonitorListActionsPopover = ({
|
|||
}: MonitorListActionsPopoverProps) => {
|
||||
const popoverId = `${monitor.id.key}_popover`;
|
||||
const [popoverIsVisible, setPopoverIsVisible] = useState<boolean>(false);
|
||||
const domain = get(ping, 'url.domain', '');
|
||||
const domain = get<string>(ping, 'url.domain', '');
|
||||
const podUid = get<string | undefined>(ping, 'kubernetes.pod.uid', undefined);
|
||||
const containerId = get<string | undefined>(ping, 'container.id', undefined);
|
||||
const ip = get<string | undefined>(ping, 'monitor.ip');
|
||||
return (
|
||||
<EuiPopover
|
||||
button={
|
||||
|
@ -51,7 +59,7 @@ export const MonitorListActionsPopover = ({
|
|||
id={popoverId}
|
||||
isOpen={popoverIsVisible}
|
||||
>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexGroup direction="column">
|
||||
<EuiFlexItem>
|
||||
<IntegrationLink
|
||||
ariaLabel={i18n.translate('xpack.uptime.apmIntegrationAction.description', {
|
||||
|
@ -79,6 +87,89 @@ export const MonitorListActionsPopover = ({
|
|||
)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<IntegrationLink
|
||||
ariaLabel={i18n.translate(
|
||||
'xpack.uptime.monitorList.infraIntegrationAction.ip.ariaLabel',
|
||||
{
|
||||
defaultMessage: `Check Infrastructure UI for this montor's ip address`,
|
||||
description: 'This value is shown as the aria label value for screen readers.',
|
||||
}
|
||||
)}
|
||||
href={getInfraIpHref(monitor, basePath)}
|
||||
iconType="infraApp"
|
||||
message={i18n.translate('xpack.uptime.monitorList.infraIntegrationAction.ip.message', {
|
||||
defaultMessage: 'Show host metrics',
|
||||
description: `A message explaining that this link will take the user to the Infrastructure UI, filtered for this monitor's IP Address`,
|
||||
})}
|
||||
tooltipContent={i18n.translate(
|
||||
'xpack.uptime.monitorList.infraIntegrationAction.ip.tooltip',
|
||||
{
|
||||
defaultMessage: 'Check Infrastructure UI for the IP "{ip}"',
|
||||
values: {
|
||||
ip,
|
||||
},
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<IntegrationLink
|
||||
ariaLabel={i18n.translate(
|
||||
'xpack.uptime.monitorList.infraIntegrationAction.kubernetes.description',
|
||||
{
|
||||
defaultMessage: `Check Infrastructure UI for this monitor's pod UID`,
|
||||
description: 'This value is shown as the aria label value for screen readers.',
|
||||
}
|
||||
)}
|
||||
href={getInfraKubernetesHref(monitor, basePath)}
|
||||
iconType="infraApp"
|
||||
message={i18n.translate(
|
||||
'xpack.uptime.monitorList.infraIntegrationAction.kubernetes.message',
|
||||
{
|
||||
defaultMessage: 'Show pod metrics',
|
||||
description:
|
||||
'A message explaining that this link will take the user to the Infrastructure UI filtered for the monitor Pod UID.',
|
||||
}
|
||||
)}
|
||||
tooltipContent={i18n.translate(
|
||||
'xpack.uptime.monitorList.infraIntegrationAction.kubernetes.tooltip',
|
||||
{
|
||||
defaultMessage: 'Check Infrastructure UI for pod UID "{podUid}".',
|
||||
values: {
|
||||
podUid,
|
||||
},
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<IntegrationLink
|
||||
ariaLabel={i18n.translate(
|
||||
'xpack.uptime.monitorList.infraIntegrationAction.docker.description',
|
||||
{
|
||||
defaultMessage: `Check Infrastructure UI for this monitor's container ID`,
|
||||
}
|
||||
)}
|
||||
href={getInfraContainerHref(monitor, basePath)}
|
||||
iconType="infraApp"
|
||||
message={i18n.translate(
|
||||
'xpack.uptime.monitorList.infraIntegrationAction.container.message',
|
||||
{
|
||||
defaultMessage: 'Show container metrics',
|
||||
}
|
||||
)}
|
||||
tooltipContent={i18n.translate(
|
||||
'xpack.uptime.monitorList.infraIntegrationAction.docker.tooltip',
|
||||
{
|
||||
defaultMessage: 'Check Infrastructure UI for container ID "{containerId}"',
|
||||
values: {
|
||||
containerId,
|
||||
},
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPopover>
|
||||
);
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
*/
|
||||
|
||||
export { convertMicrosecondsToMilliseconds } from './convert_measurements';
|
||||
export { getApmHref } from './observability_integration';
|
||||
export * from './observability_integration';
|
||||
export { UptimeUrlParams, getSupportedUrlParams } from './url_params';
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`getInfraHref getInfraContainerHref creates a link for valid parameters 1`] = `"/foo/app/infra#/link-to/container-detail/test-container-id"`;
|
||||
|
||||
exports[`getInfraHref getInfraContainerHref does not specify a base path when none is available 1`] = `"/app/infra#/link-to/container-detail/test-container-id"`;
|
||||
|
||||
exports[`getInfraHref getInfraIpHref creates a link for valid parameters 1`] = `"/bar/app/infra#/infrastructure/inventory?waffleFilter=(expression:'host.ip%20%3A%20151.101.202.217',kind:kuery)"`;
|
||||
|
||||
exports[`getInfraHref getInfraIpHref does not specify a base path when none is available 1`] = `"/app/infra#/infrastructure/inventory?waffleFilter=(expression:'host.ip%20%3A%20151.101.202.217',kind:kuery)"`;
|
||||
|
||||
exports[`getInfraHref getInfraKubernetesHref creates a link for valid parameters 1`] = `"/foo/app/infra#/link-to/pod-detail/test-pod-uid"`;
|
||||
|
||||
exports[`getInfraHref getInfraKubernetesHref does not specify a base path when none is available 1`] = `"/app/infra#/link-to/pod-detail/test-pod-uid"`;
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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 { getInfraContainerHref, getInfraKubernetesHref, getInfraIpHref } from '../get_infra_href';
|
||||
import { LatestMonitor } from '../../../../../common/graphql/types';
|
||||
|
||||
describe('getInfraHref', () => {
|
||||
let monitor: LatestMonitor;
|
||||
beforeEach(() => {
|
||||
monitor = {
|
||||
id: {
|
||||
key: 'foo',
|
||||
url: 'http://bar.com/',
|
||||
},
|
||||
ping: {
|
||||
timestamp: '1557405354000',
|
||||
container: {
|
||||
id: 'test-container-id',
|
||||
},
|
||||
kubernetes: {
|
||||
pod: {
|
||||
uid: 'test-pod-uid',
|
||||
},
|
||||
},
|
||||
monitor: {
|
||||
ip: '151.101.202.217',
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('getInfraContainerHref creates a link for valid parameters', () => {
|
||||
expect.assertions(2);
|
||||
const result = getInfraContainerHref(monitor, 'foo');
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('getInfraContainerHref does not specify a base path when none is available', () => {
|
||||
expect.assertions(1);
|
||||
expect(getInfraContainerHref(monitor, '')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('getInfraContainerHref returns undefined when no container id is present', () => {
|
||||
expect.assertions(1);
|
||||
delete monitor.ping;
|
||||
expect(getInfraContainerHref(monitor, 'foo')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('getInfraKubernetesHref creates a link for valid parameters', () => {
|
||||
expect.assertions(2);
|
||||
const result = getInfraKubernetesHref(monitor, 'foo');
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('getInfraKubernetesHref does not specify a base path when none is available', () => {
|
||||
expect.assertions(1);
|
||||
expect(getInfraKubernetesHref(monitor, '')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('getInfraKubernetesHref returns undefined when no pod data is present', () => {
|
||||
expect.assertions(1);
|
||||
delete monitor.ping;
|
||||
expect(getInfraKubernetesHref(monitor, 'foo')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('getInfraIpHref creates a link for valid parameters', () => {
|
||||
expect.assertions(2);
|
||||
const result = getInfraIpHref(monitor, 'bar');
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('getInfraIpHref does not specify a base path when none is available', () => {
|
||||
expect.assertions(1);
|
||||
expect(getInfraIpHref(monitor, '')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('getInfraIpHref returns undefined when ip is present', () => {
|
||||
expect.assertions(1);
|
||||
delete monitor.ping;
|
||||
expect(getInfraIpHref(monitor, 'foo')).toBeUndefined();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const addBasePath = (basePath: string, url: string) =>
|
||||
`${basePath.length > 0 ? `/${basePath}` : ''}${url}`;
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { addBasePath } from './add_base_path';
|
||||
import { LatestMonitor } from '../../../../common/graphql/types';
|
||||
|
||||
export const getApmHref = (
|
||||
|
@ -13,6 +14,9 @@ export const getApmHref = (
|
|||
dateRangeStart: string,
|
||||
dateRangeEnd: string
|
||||
) =>
|
||||
`${basePath && basePath.length ? `/${basePath}` : ''}/app/apm#/services?kuery=${encodeURI(
|
||||
`url.domain: "${get(monitor, 'ping.url.domain')}"`
|
||||
)}&rangeFrom=${dateRangeStart}&rangeTo=${dateRangeEnd}`;
|
||||
addBasePath(
|
||||
basePath,
|
||||
`/app/apm#/services?kuery=${encodeURI(
|
||||
`url.domain: "${get(monitor, 'ping.url.domain')}"`
|
||||
)}&rangeFrom=${dateRangeStart}&rangeTo=${dateRangeEnd}`
|
||||
);
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { LatestMonitor } from '../../../../common/graphql/types';
|
||||
import { addBasePath } from './add_base_path';
|
||||
|
||||
/**
|
||||
* Builds URLs to the designated features by extracting values from the provided
|
||||
* monitor object on a given path. Then returns the result of a provided function
|
||||
* to place the value in its rightful place on the URI string.
|
||||
* @param monitor the data object
|
||||
* @param path the location on the object of the desired data
|
||||
* @param getHref a function that returns the full URL
|
||||
*/
|
||||
const buildHref = (
|
||||
monitor: LatestMonitor,
|
||||
path: string,
|
||||
getHref: (value: string) => string
|
||||
): string | undefined => {
|
||||
const queryValue = get<string | undefined>(monitor, path);
|
||||
if (queryValue === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return getHref(queryValue);
|
||||
};
|
||||
|
||||
export const getInfraContainerHref = (
|
||||
monitor: LatestMonitor,
|
||||
basePath: string
|
||||
): string | undefined =>
|
||||
buildHref(monitor, 'ping.container.id', containerId =>
|
||||
addBasePath(basePath, `/app/infra#/link-to/container-detail/${encodeURIComponent(containerId)}`)
|
||||
);
|
||||
|
||||
export const getInfraKubernetesHref = (
|
||||
monitor: LatestMonitor,
|
||||
basePath: string
|
||||
): string | undefined =>
|
||||
buildHref(monitor, 'ping.kubernetes.pod.uid', uid =>
|
||||
addBasePath(basePath, `/app/infra#/link-to/pod-detail/${encodeURIComponent(uid)}`)
|
||||
);
|
||||
|
||||
export const getInfraIpHref = (monitor: LatestMonitor, basePath: string) =>
|
||||
buildHref(monitor, 'ping.monitor.ip', ip => {
|
||||
const expression = encodeURIComponent(`host.ip : ${ip}`);
|
||||
return addBasePath(
|
||||
basePath,
|
||||
`/app/infra#/infrastructure/inventory?waffleFilter=(expression:'${expression}',kind:kuery)`
|
||||
);
|
||||
});
|
|
@ -5,3 +5,4 @@
|
|||
*/
|
||||
|
||||
export { getApmHref } from './get_apm_href';
|
||||
export { getInfraContainerHref, getInfraIpHref, getInfraKubernetesHref } from './get_infra_href';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue