+ {section.map(item => (
+
+ {item.title && {item.title}}
+ {item.subtitle && (
+ {item.subtitle}
+ )}
+
+ {item.actions.map(action => (
+
+ ))}
+
+
+ ))}
+ {isLastSection &&
}
+
+ );
+ })}
+
);
};
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx
index 2bfa5cf1274fa..e9f89034f58ee 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx
@@ -36,7 +36,7 @@ describe('TransactionActionMenu component', () => {
Transactions.transactionWithMinimalData
);
- expect(queryByText('Show trace logs')).not.toBeNull();
+ expect(queryByText('Trace logs')).not.toBeNull();
});
it('should not render the pod links when there is no pod id', async () => {
@@ -44,8 +44,8 @@ describe('TransactionActionMenu component', () => {
Transactions.transactionWithMinimalData
);
- expect(queryByText('Show pod logs')).toBeNull();
- expect(queryByText('Show pod metrics')).toBeNull();
+ expect(queryByText('Pod logs')).toBeNull();
+ expect(queryByText('Pod metrics')).toBeNull();
});
it('should render the pod links when there is a pod id', async () => {
@@ -53,8 +53,8 @@ describe('TransactionActionMenu component', () => {
Transactions.transactionWithKubernetesData
);
- expect(queryByText('Show pod logs')).not.toBeNull();
- expect(queryByText('Show pod metrics')).not.toBeNull();
+ expect(queryByText('Pod logs')).not.toBeNull();
+ expect(queryByText('Pod metrics')).not.toBeNull();
});
it('should not render the container links when there is no container id', async () => {
@@ -62,8 +62,8 @@ describe('TransactionActionMenu component', () => {
Transactions.transactionWithMinimalData
);
- expect(queryByText('Show container logs')).toBeNull();
- expect(queryByText('Show container metrics')).toBeNull();
+ expect(queryByText('Container logs')).toBeNull();
+ expect(queryByText('Container metrics')).toBeNull();
});
it('should render the container links when there is a container id', async () => {
@@ -71,8 +71,8 @@ describe('TransactionActionMenu component', () => {
Transactions.transactionWithContainerData
);
- expect(queryByText('Show container logs')).not.toBeNull();
- expect(queryByText('Show container metrics')).not.toBeNull();
+ expect(queryByText('Container logs')).not.toBeNull();
+ expect(queryByText('Container metrics')).not.toBeNull();
});
it('should not render the host links when there is no hostname', async () => {
@@ -80,8 +80,8 @@ describe('TransactionActionMenu component', () => {
Transactions.transactionWithMinimalData
);
- expect(queryByText('Show host logs')).toBeNull();
- expect(queryByText('Show host metrics')).toBeNull();
+ expect(queryByText('Host logs')).toBeNull();
+ expect(queryByText('Host metrics')).toBeNull();
});
it('should render the host links when there is a hostname', async () => {
@@ -89,8 +89,8 @@ describe('TransactionActionMenu component', () => {
Transactions.transactionWithHostData
);
- expect(queryByText('Show host logs')).not.toBeNull();
- expect(queryByText('Show host metrics')).not.toBeNull();
+ expect(queryByText('Host logs')).not.toBeNull();
+ expect(queryByText('Host metrics')).not.toBeNull();
});
it('should not render the uptime link if there is no url available', async () => {
@@ -98,7 +98,7 @@ describe('TransactionActionMenu component', () => {
Transactions.transactionWithMinimalData
);
- expect(queryByText('View monitor status')).toBeNull();
+ expect(queryByText('Status')).toBeNull();
});
it('should not render the uptime link if there is no domain available', async () => {
@@ -106,7 +106,7 @@ describe('TransactionActionMenu component', () => {
Transactions.transactionWithUrlWithoutDomain
);
- expect(queryByText('View monitor status')).toBeNull();
+ expect(queryByText('Status')).toBeNull();
});
it('should render the uptime link if there is a url with a domain', async () => {
@@ -114,7 +114,7 @@ describe('TransactionActionMenu component', () => {
Transactions.transactionWithUrlAndDomain
);
- expect(queryByText('View monitor status')).not.toBeNull();
+ expect(queryByText('Status')).not.toBeNull();
});
it('should match the snapshot', async () => {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts
new file mode 100644
index 0000000000000..52c2d27eabb82
--- /dev/null
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts
@@ -0,0 +1,204 @@
+/*
+ * 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 { Location } from 'history';
+import { getSections } from '../sections';
+import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
+import { AppMountContextBasePath } from '../../../../context/ApmPluginContext';
+
+describe('Transaction action menu', () => {
+ const basePath = ({
+ prepend: jest.fn()
+ } as unknown) as AppMountContextBasePath;
+ const date = '2020-02-06T11:00:00.000Z';
+ const timestamp = { us: new Date(date).getTime() };
+
+ it('shows required sections only', () => {
+ const transaction = ({
+ timestamp,
+ trace: { id: '123' },
+ transaction: { id: '123' },
+ '@timestamp': date
+ } as unknown) as Transaction;
+ expect(
+ getSections({
+ transaction,
+ basePath,
+ location: ({} as unknown) as Location,
+ urlParams: {}
+ })
+ ).toEqual([
+ [
+ {
+ key: 'traceDetails',
+ title: 'Trace details',
+ subtitle: 'View trace logs to get further details.',
+ actions: [
+ {
+ key: 'traceLogs',
+ label: 'Trace logs',
+ href:
+ '#/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123',
+ condition: true
+ }
+ ]
+ }
+ ],
+ [
+ {
+ key: 'kibana',
+ actions: [
+ {
+ key: 'sampleDocument',
+ label: 'View sample document',
+ href:
+ '#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))',
+ condition: true
+ }
+ ]
+ }
+ ]
+ ]);
+ });
+
+ it('shows pod and required sections only', () => {
+ const transaction = ({
+ kubernetes: { pod: { uid: '123' } },
+ timestamp,
+ trace: { id: '123' },
+ transaction: { id: '123' },
+ '@timestamp': date
+ } as unknown) as Transaction;
+ expect(
+ getSections({
+ transaction,
+ basePath,
+ location: ({} as unknown) as Location,
+ urlParams: {}
+ })
+ ).toEqual([
+ [
+ {
+ key: 'podDetails',
+ title: 'Pod details',
+ subtitle:
+ 'View logs and metrics for this pod to get further details.',
+ actions: [
+ {
+ key: 'podLogs',
+ label: 'Pod logs',
+ href: '#/link-to/pod-logs/123?time=1580986800',
+ condition: true
+ },
+ {
+ key: 'podMetrics',
+ label: 'Pod metrics',
+ href:
+ '#/link-to/pod-detail/123?from=1580986500000&to=1580987100000',
+ condition: true
+ }
+ ]
+ },
+ {
+ key: 'traceDetails',
+ title: 'Trace details',
+ subtitle: 'View trace logs to get further details.',
+ actions: [
+ {
+ key: 'traceLogs',
+ label: 'Trace logs',
+ href:
+ '#/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123',
+ condition: true
+ }
+ ]
+ }
+ ],
+ [
+ {
+ key: 'kibana',
+ actions: [
+ {
+ key: 'sampleDocument',
+ label: 'View sample document',
+ href:
+ '#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))',
+ condition: true
+ }
+ ]
+ }
+ ]
+ ]);
+ });
+
+ it('shows host and required sections only', () => {
+ const transaction = ({
+ host: { hostname: 'foo' },
+ timestamp,
+ trace: { id: '123' },
+ transaction: { id: '123' },
+ '@timestamp': date
+ } as unknown) as Transaction;
+ expect(
+ getSections({
+ transaction,
+ basePath,
+ location: ({} as unknown) as Location,
+ urlParams: {}
+ })
+ ).toEqual([
+ [
+ {
+ key: 'hostDetails',
+ title: 'Host details',
+ subtitle: 'View host logs and metrics to get further details.',
+ actions: [
+ {
+ key: 'hostLogs',
+ label: 'Host logs',
+ href: '#/link-to/host-logs/foo?time=1580986800',
+ condition: true
+ },
+ {
+ key: 'hostMetrics',
+ label: 'Host metrics',
+ href:
+ '#/link-to/host-detail/foo?from=1580986500000&to=1580987100000',
+ condition: true
+ }
+ ]
+ },
+ {
+ key: 'traceDetails',
+ title: 'Trace details',
+ subtitle: 'View trace logs to get further details.',
+ actions: [
+ {
+ key: 'traceLogs',
+ label: 'Trace logs',
+ href:
+ '#/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123',
+ condition: true
+ }
+ ]
+ }
+ ],
+ [
+ {
+ key: 'kibana',
+ actions: [
+ {
+ key: 'sampleDocument',
+ label: 'View sample document',
+ href:
+ '#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))',
+ condition: true
+ }
+ ]
+ }
+ ]
+ ]);
+ });
+});
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts
new file mode 100644
index 0000000000000..77445a2600960
--- /dev/null
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts
@@ -0,0 +1,299 @@
+/*
+ * 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';
+import { Location } from 'history';
+import { pick, isEmpty } from 'lodash';
+import moment from 'moment';
+import url from 'url';
+import { Transaction } from '../../../../typings/es_schemas/ui/Transaction';
+import { IUrlParams } from '../../../context/UrlParamsContext/types';
+import { getDiscoverHref } from '../Links/DiscoverLinks/DiscoverLink';
+import { getDiscoverQuery } from '../Links/DiscoverLinks/DiscoverTransactionLink';
+import { getInfraHref } from '../Links/InfraLink';
+import { fromQuery } from '../Links/url_helpers';
+import { AppMountContextBasePath } from '../../../context/ApmPluginContext';
+
+function getInfraMetricsQuery(transaction: Transaction) {
+ const timestamp = new Date(transaction['@timestamp']).getTime();
+ const fiveMinutes = moment.duration(5, 'minutes').asMilliseconds();
+
+ return {
+ from: timestamp - fiveMinutes,
+ to: timestamp + fiveMinutes
+ };
+}
+
+interface Action {
+ key: string;
+ label: string;
+ href: string;
+ condition: boolean;
+}
+
+interface Section {
+ key: string;
+ title?: string;
+ subtitle?: string;
+ actions: Action[];
+}
+
+type SectionRecord = Record