diff --git a/x-pack/plugins/apm/public/components/app/transaction_link/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_link/index.tsx
new file mode 100644
index 0000000000000..c6394f09b0d3c
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/app/transaction_link/index.tsx
@@ -0,0 +1,67 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { EuiEmptyPrompt } from '@elastic/eui';
+import React from 'react';
+import { Redirect, RouteComponentProps } from 'react-router-dom';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
+import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher';
+import { useUrlParams } from '../../../context/url_params_context/use_url_params';
+import { getRedirectToTransactionDetailPageUrl } from '../TraceLink/get_redirect_to_transaction_detail_page_url';
+
+const CentralizedContainer = euiStyled.div`
+ height: 100%;
+ display: flex;
+`;
+
+export function TransactionLink({
+ match,
+}: RouteComponentProps<{ transactionId: string }>) {
+ const { transactionId } = match.params;
+ const { urlParams } = useUrlParams();
+ const { rangeFrom, rangeTo } = urlParams;
+
+ const { data = { transaction: null }, status } = useFetcher(
+ (callApmApi) => {
+ if (transactionId) {
+ return callApmApi({
+ endpoint: 'GET /api/apm/transactions/{transactionId}',
+ params: {
+ path: {
+ transactionId,
+ },
+ },
+ });
+ }
+ },
+ [transactionId]
+ );
+ if (transactionId && status === FETCH_STATUS.SUCCESS) {
+ if (data.transaction) {
+ return (
+
+ );
+ }
+
+ return ;
+ }
+
+ return (
+
+ Fetching transaction...}
+ />
+
+ );
+}
diff --git a/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx b/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx
index 3d530f4614d82..36580d38e660d 100644
--- a/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx
+++ b/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx
@@ -20,6 +20,7 @@ import { AnomalyDetection } from '../app/Settings/anomaly_detection';
import { ApmIndices } from '../app/Settings/ApmIndices';
import { CustomizeUI } from '../app/Settings/CustomizeUI';
import { TraceLink } from '../app/TraceLink';
+import { TransactionLink } from '../app/transaction_link';
import { TransactionDetails } from '../app/transaction_details';
import { enableServiceOverview } from '../../../common/ui_settings_keys';
import { redirectTo } from './redirect_to';
@@ -498,6 +499,12 @@ export const apmRouteConfig: APMRouteDefinition[] = [
component: TraceLink,
breadcrumb: null,
},
+ {
+ exact: true,
+ path: '/link-to/transaction/:transactionId',
+ component: TransactionLink,
+ breadcrumb: null,
+ },
];
function RedirectToDefaultServiceRouteView(
diff --git a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
index b4323ae7f51e2..6987ef0757734 100644
--- a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
@@ -21,11 +21,11 @@ export function getTransaction({
setup,
}: {
transactionId: string;
- traceId: string;
- setup: Setup & SetupTimeRange;
+ traceId?: string;
+ setup: Setup | (Setup & SetupTimeRange);
}) {
return withApmSpan('get_transaction', async () => {
- const { start, end, apmEventClient } = setup;
+ const { apmEventClient } = setup;
const resp = await apmEventClient.search({
apm: {
@@ -37,8 +37,8 @@ export function getTransaction({
bool: {
filter: asMutableArray([
{ term: { [TRANSACTION_ID]: transactionId } },
- { term: { [TRACE_ID]: traceId } },
- ...rangeQuery(start, end),
+ ...(traceId ? [{ term: { [TRACE_ID]: traceId } }] : []),
+ ...('start' in setup ? rangeQuery(setup.start, setup.end) : []),
]),
},
},
diff --git a/x-pack/plugins/apm/server/routes/traces.ts b/x-pack/plugins/apm/server/routes/traces.ts
index dd392982b02fd..7fce04644f220 100644
--- a/x-pack/plugins/apm/server/routes/traces.ts
+++ b/x-pack/plugins/apm/server/routes/traces.ts
@@ -14,6 +14,7 @@ import { environmentRt, kueryRt, rangeRt } from './default_api_types';
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
import { getRootTransactionByTraceId } from '../lib/transactions/get_transaction_by_trace';
import { createApmServerRouteRepository } from './create_apm_server_route_repository';
+import { getTransaction } from '../lib/transactions/get_transaction';
const tracesRoute = createApmServerRoute({
endpoint: 'GET /api/apm/traces',
@@ -70,7 +71,24 @@ const rootTransactionByTraceIdRoute = createApmServerRoute({
},
});
+const transactionByIdRoute = createApmServerRoute({
+ endpoint: 'GET /api/apm/transactions/{transactionId}',
+ params: t.type({
+ path: t.type({
+ transactionId: t.string,
+ }),
+ }),
+ options: { tags: ['access:apm'] },
+ handler: async (resources) => {
+ const { params } = resources;
+ const { transactionId } = params.path;
+ const setup = await setupRequest(resources);
+ return { transaction: await getTransaction({ transactionId, setup }) };
+ },
+});
+
export const traceRouteRepository = createApmServerRouteRepository()
.add(tracesByIdRoute)
.add(tracesRoute)
- .add(rootTransactionByTraceIdRoute);
+ .add(rootTransactionByTraceIdRoute)
+ .add(transactionByIdRoute);