From 67010bd41debd24514a9746d2798194f14c7d107 Mon Sep 17 00:00:00 2001 From: Jenny Date: Wed, 2 Apr 2025 17:07:42 +0200 Subject: [PATCH 01/16] Encode URL path in the reuqest formatter --- .../src/format_request.test.ts | 9 +++++++++ .../src/format_request.ts | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/platform/packages/shared/kbn-server-route-repository-utils/src/format_request.test.ts b/src/platform/packages/shared/kbn-server-route-repository-utils/src/format_request.test.ts index 5c128cf57fefc..bd59eb8f64ea7 100644 --- a/src/platform/packages/shared/kbn-server-route-repository-utils/src/format_request.test.ts +++ b/src/platform/packages/shared/kbn-server-route-repository-utils/src/format_request.test.ts @@ -11,6 +11,15 @@ import { formatRequest } from './format_request'; describe('formatRequest', () => { const version = 1; + it('should encode the path if the optional or required param is provided', () => { + const pathParams = { param: 'test/Param/>?%/' }; + const resultOptionalEnd = formatRequest(`GET /api/endpoint/{param?} ${version}`, pathParams); + expect(resultOptionalEnd.pathname).toBe('/api/endpoint/test%2FParam%2F%3E%3F%25%2F'); + const resultRequiredEnd = formatRequest(`GET /api/endpoint/{param} ${version}`, pathParams); + expect(resultRequiredEnd.pathname).toBe('/api/endpoint/test%2FParam%2F%3E%3F%25%2F'); + const resultRequiredMid = formatRequest(`GET /api/{param}/endpoint/ ${version}`, pathParams); + expect(resultRequiredMid.pathname).toBe('/api/test%2FParam%2F%3E%3F%25%2F/endpoint/'); + }); it('should return the correct path if the optional or required param is provided', () => { const pathParams = { param: 'testParam' }; const resultOptionalEnd = formatRequest(`GET /api/endpoint/{param?} ${version}`, pathParams); diff --git a/src/platform/packages/shared/kbn-server-route-repository-utils/src/format_request.ts b/src/platform/packages/shared/kbn-server-route-repository-utils/src/format_request.ts index 291ba67cf70fd..5c0c08341ee76 100644 --- a/src/platform/packages/shared/kbn-server-route-repository-utils/src/format_request.ts +++ b/src/platform/packages/shared/kbn-server-route-repository-utils/src/format_request.ts @@ -21,8 +21,8 @@ export function formatRequest(endpoint: string, pathParams: Record const pathname = Object.keys(pathParams).reduce((acc, paramName) => { return acc - .replace(`{${paramName}}`, pathParams[paramName]) - .replace(`{${paramName}?}`, pathParams[paramName]); + .replace(`{${paramName}}`, encodeURIComponent(pathParams[paramName])) + .replace(`{${paramName}?}`, encodeURIComponent(pathParams[paramName])); }, rawPathname); if ((pathname.match(optionalReg) ?? [])?.length > 0) { From 6ed7d2e6ac4a94d7563de29c5ac54b946994360c Mon Sep 17 00:00:00 2001 From: Jenny Date: Thu, 3 Apr 2025 19:57:40 +0200 Subject: [PATCH 02/16] Refactoring: Change TransactionDetailLink to apm router link --- .../src/scenarios/simple_trace.ts | 3 +- .../plugins/apm/common/environment_rt.ts | 1 + .../error_sampler/error_sample_detail.tsx | 18 ++++++++++ .../top_erroneous_transactions/index.tsx | 13 +++++++ .../maybe_view_trace_link.tsx | 34 ++++++++++++++++--- .../waterfall/flyout_top_level_properties.tsx | 15 ++++++++ .../span_flyout/sticky_span_properties.tsx | 15 ++++++++ .../shared/links/apm/error_detail_link.tsx | 7 +++- .../shared/links/apm/metric_overview_link.tsx | 4 +-- .../shared/links/apm/service_map_link.tsx | 4 ++- .../apm/service_node_metric_overview_link.tsx | 4 ++- .../links/apm/service_node_overview_link.tsx | 2 +- .../service_transactions_overview_link.tsx | 2 +- .../apm/transaction_detail_link/index.tsx | 15 ++++++-- .../links/apm/transaction_overview_link.tsx | 19 ++++++++--- .../shared/transactions_table/get_columns.tsx | 11 ++++++ .../shared/transactions_table/index.tsx | 20 ++++++++++- .../locators/apm/service_overview_locator.ts | 2 +- .../observability_integration/get_apm_href.ts | 4 ++- 19 files changed, 170 insertions(+), 23 deletions(-) diff --git a/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/simple_trace.ts b/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/simple_trace.ts index e98fcb32cf3e0..603bbd7f903da 100644 --- a/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/simple_trace.ts +++ b/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/simple_trace.ts @@ -25,9 +25,10 @@ const scenario: Scenario = async (runOptions) => { const successfulTimestamps = range.interval('1m').rate(180); const failedTimestamps = range.interval('1m').rate(180); + // TODO Keep using service name that needs encoding or add new scenario const instances = [...Array(numServices).keys()].map((index) => apm - .service({ name: `synth-node-${index}`, environment: ENVIRONMENT, agentName: 'nodejs' }) + .service({ name: `synth%/node-${index}`, environment: ENVIRONMENT, agentName: 'nodejs' }) .instance('instance') ); const instanceSpans = (instance: Instance) => { diff --git a/x-pack/solutions/observability/plugins/apm/common/environment_rt.ts b/x-pack/solutions/observability/plugins/apm/common/environment_rt.ts index 530b6e7930134..87d65a0847c30 100644 --- a/x-pack/solutions/observability/plugins/apm/common/environment_rt.ts +++ b/x-pack/solutions/observability/plugins/apm/common/environment_rt.ts @@ -11,6 +11,7 @@ import { ENVIRONMENT_ALL, ENVIRONMENT_NOT_DEFINED } from './environment_filter_v export const environmentStringRt = t.union([ t.literal(ENVIRONMENT_NOT_DEFINED.value), t.literal(ENVIRONMENT_ALL.value), + t.string, nonEmptyStringRt, ]); diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx index 4d147e78237cb..026f931869805 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx @@ -176,6 +176,23 @@ export function ErrorSampleDetails({ }, }); + const transactionViewLink = transaction + ? router.link('/services/{serviceName}/transactions/view', { + path: { serviceName: transaction.service.name }, + query: { + ...query, + traceId: transaction.trace.id, + transactionId: transaction.transaction.id, + transactionName: transaction.transaction.name, + transactionType: transaction.transaction.type, + comparisonEnabled: !!comparisonEnabled, + showCriticalPath: false, + offset, + kuery, + }, + }) + : undefined; + return ( @@ -265,6 +282,7 @@ export function ErrorSampleDetails({ serviceName={transaction.service.name} offset={offset} comparisonEnabled={comparisonEnabled} + href={transactionViewLink} > {transaction.transaction.name} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/top_erroneous_transactions/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/top_erroneous_transactions/index.tsx index 32fe2c2e103dc..e4b83129a040c 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/top_erroneous_transactions/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/top_erroneous_transactions/index.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import type { EuiBasicTableColumn } from '@elastic/eui'; import { EuiBasicTable, EuiTitle, RIGHT_ALIGNMENT, EuiSpacer } from '@elastic/eui'; import type { ValuesType } from 'utility-types'; +import { useApmRouter } from '../../../../hooks/use_apm_router'; import type { APIReturnType } from '../../../../services/rest/create_call_apm_api'; import { SparkPlot } from '../../../shared/charts/spark_plot'; import { ChartType, getTimeSeriesColor } from '../../../shared/charts/helper/get_timeseries_color'; @@ -37,6 +38,7 @@ export function TopErroneousTransactions({ serviceName }: Props) { query, path: { groupId }, } = useApmParams('/services/{serviceName}/errors/{groupId}'); + const { link } = useApmRouter(); const { rangeFrom, rangeTo, environment, kuery, offset, comparisonEnabled } = query; @@ -91,6 +93,17 @@ export function TopErroneousTransactions({ serviceName }: Props) { transactionType={transactionType ?? ''} comparisonEnabled={comparisonEnabled} offset={offset} + href={link('/services/{serviceName}/transactions/view', { + path: { serviceName }, + query: { + ...query, + transactionName, + transactionType: transactionType ?? '', + comparisonEnabled, + showCriticalPath: false, + offset, + }, + })} > {transactionName} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx index a7257663dd413..5e0226fa570df 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx @@ -8,6 +8,7 @@ import { EuiButton, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { useApmRouter } from '../../../../hooks/use_apm_router'; import { getNextEnvironmentUrlParam } from '../../../../../common/environment_filter_values'; import type { Transaction as ITransaction } from '../../../../../typings/es_schemas/ui/transaction'; import { TransactionDetailLink } from '../../../shared/links/apm/transaction_detail_link'; @@ -53,6 +54,8 @@ export function MaybeViewTraceLink({ '/dependencies/operation' ); + const { link } = useApmRouter(); + const latencyAggregationType = ('latencyAggregationType' in query && query.latencyAggregationType) || LatencyAggregationType.avg; @@ -77,6 +80,31 @@ export function MaybeViewTraceLink({ const rootTransaction = rootWaterfallTransaction.doc; const isRoot = transaction.transaction.id === rootWaterfallTransaction.id; + const nextEnvironment = getNextEnvironmentUrlParam({ + requestedEnvironment: rootTransaction.service.environment, + currentEnvironmentUrlParam: environment, + }); + + const transactionViewLink = rootTransaction + ? link('/services/{serviceName}/transactions/view', { + path: { serviceName: rootTransaction.service.name }, + query: { + ...query, + traceId: rootTransaction.trace.id, + transactionId: rootTransaction.transaction.id, + transactionName: rootTransaction.transaction.name, + transactionType: rootTransaction.transaction.type, + comparisonEnabled: !!comparisonEnabled, + showCriticalPath: false, + offset, + environment: getNextEnvironmentUrlParam({ + requestedEnvironment: rootTransaction.service.environment, + currentEnvironmentUrlParam: environment, + }), + serviceGroup: '', + }, + }) + : undefined; // the user is already viewing the full trace, so don't link to it if (isRoot) { @@ -92,11 +120,6 @@ export function MaybeViewTraceLink({ // the user is viewing a zoomed in version of the trace. Link to the full trace } else { - const nextEnvironment = getNextEnvironmentUrlParam({ - requestedEnvironment: rootTransaction.service.environment, - currentEnvironmentUrlParam: environment, - }); - return ( diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/flyout_top_level_properties.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/flyout_top_level_properties.tsx index 2c76d5dc73664..edfae7d95d6ad 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/flyout_top_level_properties.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/flyout_top_level_properties.tsx @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; +import { useApmRouter } from '../../../../../../hooks/use_apm_router'; import { SERVICE_NAME, TRANSACTION_NAME } from '../../../../../../../common/es_fields/apm'; import { getNextEnvironmentUrlParam } from '../../../../../../../common/environment_filter_values'; import { LatencyAggregationType } from '../../../../../../../common/latency_aggregation_types'; @@ -27,6 +28,7 @@ export function FlyoutTopLevelProperties({ transaction }: Props) { '/traces/explorer', '/dependencies/operation' ); + const { link } = useApmRouter(); const latencyAggregationType = ('latencyAggregationType' in query && query.latencyAggregationType) || @@ -39,6 +41,18 @@ export function FlyoutTopLevelProperties({ transaction }: Props) { return null; } + const transactionViewLink = transaction + ? link('/services/{serviceName}/transactions/view', { + path: { serviceName: transaction?.service.name }, + query: { + ...query, + serviceGroup, + latencyAggregationType, + transactionName: transaction.transaction.name, + }, + }) + : undefined; + const nextEnvironment = getNextEnvironmentUrlParam({ requestedEnvironment: transaction.service.environment, currentEnvironmentUrlParam: query.environment, @@ -85,6 +99,7 @@ export function FlyoutTopLevelProperties({ transaction }: Props) { latencyAggregationType={latencyAggregationType} comparisonEnabled={comparisonEnabled} offset={offset} + href={transactionViewLink} > {transaction.transaction.name} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/sticky_span_properties.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/sticky_span_properties.tsx index 441b0235914ae..877a263364836 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/sticky_span_properties.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/sticky_span_properties.tsx @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { METRIC_TYPE, useUiTracker } from '@kbn/observability-shared-plugin/public'; +import { useApmRouter } from '../../../../../../../hooks/use_apm_router'; import { SERVICE_NAME, SPAN_DESTINATION_SERVICE_RESOURCE, @@ -37,6 +38,8 @@ export function StickySpanProperties({ span, transaction }: Props) { '/traces/explorer', '/dependencies/operation' ); + const router = useApmRouter(); + const { environment, comparisonEnabled, offset } = query; const latencyAggregationType = @@ -54,6 +57,17 @@ export function StickySpanProperties({ span, transaction }: Props) { const spanName = span.span.name; const dependencyName = span.span.destination?.service.resource; + const transactionViewLink = transaction + ? router.link('/services/{serviceName}/transactions/view', { + path: { serviceName: transaction?.service.name }, + query: { + ...query, + serviceGroup, + latencyAggregationType, + transactionName: transaction.transaction.name, + }, + }) + : undefined; const transactionStickyProperties = transaction ? [ @@ -91,6 +105,7 @@ export function StickySpanProperties({ span, transaction }: Props) { latencyAggregationType={latencyAggregationType} comparisonEnabled={comparisonEnabled} offset={offset} + href={transactionViewLink} > {transaction.transaction.name} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx index 74ef4682f4cc1..91ef41a49dcdb 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx @@ -15,7 +15,12 @@ interface Props extends APMLinkExtendProps { } function ErrorDetailLink({ serviceName, errorGroupId, ...rest }: Props) { - return ; + return ( + + ); } export { ErrorDetailLink }; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx index e77b77537ce52..ab3d9f0c864c5 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx @@ -19,7 +19,7 @@ const persistedFilters: Array = [ export function useMetricOverviewHref(serviceName: string) { return useAPMHref({ - path: `/services/${serviceName}/metrics`, + path: `/services/${encodeURIComponent(serviceName)}/metrics`, persistedFilters, }); } @@ -29,5 +29,5 @@ interface Props extends APMLinkExtendProps { } export function MetricOverviewLink({ serviceName, ...rest }: Props) { - return ; + return ; } diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_map_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_map_link.tsx index f9958e588ba73..66bad47e90414 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_map_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_map_link.tsx @@ -11,7 +11,9 @@ import type { APMLinkExtendProps } from './apm_link'; import { useAPMHref } from './apm_link'; export function useServiceMapHref(serviceName?: string) { - const path = serviceName ? `/services/${serviceName}/service-map` : '/service-map'; + const path = serviceName + ? `/services/${encodeURIComponent(serviceName)}/service-map` + : '/service-map'; return useAPMHref({ path }); } diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx index 7b99e981e9bb1..d0f3e7b334931 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx @@ -31,7 +31,9 @@ export function useServiceNodeMetricOverviewHref({ serviceNodeName: string; }) { return useAPMHref({ - path: `/services/${serviceName}/metrics/${encodeURIComponent(serviceNodeName)}`, + path: `/services/${encodeURIComponent(serviceName)}/metrics/${encodeURIComponent( + serviceNodeName + )}`, persistedFilters, }); } diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_overview_link.tsx index 7ea28dfbebeac..93a52a9b470ff 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_overview_link.tsx @@ -17,7 +17,7 @@ const persistedFilters: Array = [ export function useServiceNodeOverviewHref(serviceName: string) { return useAPMHref({ - path: `/services/${serviceName}/nodes`, + path: `/services/${encodeURIComponent(serviceName)}/nodes`, persistedFilters, }); } diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx index edf62a082bc56..6dbbb6e47535d 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx @@ -34,7 +34,7 @@ export function useServiceOrTransactionsOverviewHref({ }: Props) { const query = { environment, transactionType }; return useAPMHref({ - path: `/services/${serviceName}`, + path: `/services/${encodeURIComponent(serviceName)}`, persistedFilters, query: removeUndefinedProps(query), }); diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx index 78904a0d8c389..d9299ba3be101 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx @@ -31,6 +31,7 @@ interface Props extends APMLinkExtendProps { transactionName: string; transactionType?: string; latencyAggregationType?: string; + href?: string; environment?: string; comparisonEnabled?: boolean; offset?: string; @@ -50,6 +51,7 @@ export function TransactionDetailLink({ comparisonEnabled, offset = '1d', overflowCount = 0, + href, ...rest }: Props) { const { urlParams } = useLegacyUrlParams(); @@ -59,9 +61,10 @@ export function TransactionDetailLink({ urlComparisonEnabled: comparisonEnabled, }); const location = useLocation(); - const href = getLegacyApmHref({ + + const hrefLegacy = getLegacyApmHref({ basePath: core.http.basePath, - path: `/services/${serviceName}/transactions/view`, + path: `/services/${encodeURIComponent(serviceName)}/transactions/view`, query: { traceId, transactionId, @@ -79,7 +82,13 @@ export function TransactionDetailLink({ return ( } + content={ + + } /> ); } diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx index 93e0382794b76..cb185dd6bc378 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx @@ -8,6 +8,8 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; import { useLocation } from 'react-router-dom'; +import { useAnyOfApmParams } from '../../../../hooks/use_apm_params'; +import { useApmRouter } from '../../../../hooks/use_apm_router'; import { removeUndefinedProps } from '../../../../context/url_params_context/helpers'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import type { APMLinkExtendProps } from './apm_link'; @@ -19,6 +21,7 @@ interface Props extends APMLinkExtendProps { transactionType?: string; } +// TODO remove legacy and refactor export function useTransactionsOverviewHref({ serviceName, latencyAggregationType, @@ -44,10 +47,18 @@ export function TransactionOverviewLink({ transactionType, ...rest }: Props) { - const href = useTransactionsOverviewHref({ - serviceName, - latencyAggregationType, - transactionType, + const { query } = useAnyOfApmParams( + '/services/{serviceName}/transactions', + '/services/{serviceName}/overview', + '/mobile-services/{serviceName}/transactions', + '/mobile-services/{serviceName}/overview' + ); + const apmRouter = useApmRouter(); + + const href = apmRouter.link('/services/{serviceName}/transactions', { + path: { serviceName }, + query, }); + return ; } diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/get_columns.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/get_columns.tsx index 452f8910a6a68..0c71cd2703086 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/get_columns.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/get_columns.tsx @@ -138,6 +138,17 @@ export function getColumns({ comparisonEnabled={comparisonEnabled} offset={offset} overflowCount={transactionOverflowCount} + href={link('/services/{serviceName}/transactions/view', { + path: { serviceName }, + query: { + ...query, + transactionName: name, + transactionType: type, + comparisonEnabled: !!comparisonEnabled, + showCriticalPath: false, + offset, + }, + })} > {name} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/index.tsx index a84c45f076bcb..774da199379b8 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/index.tsx @@ -82,7 +82,8 @@ export function TransactionsTable({ '/services/{serviceName}/transactions', '/services/{serviceName}/overview', '/mobile-services/{serviceName}/transactions', - '/mobile-services/{serviceName}/overview' + '/mobile-services/{serviceName}/overview', + '/services/{serviceName}/transactions/view' ); const latencyAggregationType = getLatencyAggregationType(latencyAggregationTypeFromQuery); @@ -159,6 +160,22 @@ export function TransactionsTable({ }; }, [isTableSearchBarEnabled, mainStatistics.maxCountExceeded, setSearchQueryDebounced]); + const transactionViewLink = link('/services/{serviceName}/transactions/view', { + path: { serviceName }, + query: { + environment, + transactionName: '', + showCriticalPath: false, + transactionType, + latencyAggregationType, + rangeFrom: start, + rangeTo: end, + kuery, + serviceGroup: '', + comparisonEnabled: !!comparisonEnabled, + }, + }); + useEffect(() => { return setScreenContext?.({ data: [ @@ -196,6 +213,7 @@ export function TransactionsTable({ serviceName={serviceName} latencyAggregationType={latencyAggregationType} transactionType={transactionType} + href={transactionViewLink} > {i18n.translate('xpack.apm.transactionsTable.linkText', { defaultMessage: 'View transactions', diff --git a/x-pack/solutions/observability/plugins/observability_shared/common/locators/apm/service_overview_locator.ts b/x-pack/solutions/observability/plugins/observability_shared/common/locators/apm/service_overview_locator.ts index e216640f31b4f..e7f478a788e55 100644 --- a/x-pack/solutions/observability/plugins/observability_shared/common/locators/apm/service_overview_locator.ts +++ b/x-pack/solutions/observability/plugins/observability_shared/common/locators/apm/service_overview_locator.ts @@ -30,7 +30,7 @@ export class ServiceOverviewLocatorDefinition implements LocatorDefinition Date: Fri, 4 Apr 2025 15:29:14 +0200 Subject: [PATCH 03/16] Refactor MetricOverviewLink to use APM router link --- .../src/scenarios/simple_trace.ts | 2 +- .../get_columns.tsx | 8 ++++-- .../index.tsx | 2 ++ .../agent_instances_details/index.tsx | 27 ++++++++++++++++--- .../shared/links/apm/metric_overview_link.tsx | 18 ++++++++++--- 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/simple_trace.ts b/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/simple_trace.ts index 603bbd7f903da..304778626c001 100644 --- a/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/simple_trace.ts +++ b/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/simple_trace.ts @@ -28,7 +28,7 @@ const scenario: Scenario = async (runOptions) => { // TODO Keep using service name that needs encoding or add new scenario const instances = [...Array(numServices).keys()].map((index) => apm - .service({ name: `synth%/node-${index}`, environment: ENVIRONMENT, agentName: 'nodejs' }) + .service({ name: `synth/node-${index}`, environment: ENVIRONMENT, agentName: 'nodejs' }) .instance('instance') ); const instanceSpans = (instance: Instance) => { diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx index 85da44144453a..715fa91fcbbdc 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx @@ -11,6 +11,7 @@ import { i18n } from '@kbn/i18n'; import type { ReactNode } from 'react'; import React from 'react'; import { ActionMenu } from '@kbn/observability-shared-plugin/public'; +import type { TypeOf } from '@kbn/typed-react-router-config'; import { isTimeComparison } from '../../../shared/time_comparison/get_comparison_options'; import type { LatencyAggregationType } from '../../../../../common/latency_aggregation_types'; import { getServiceNodeName, SERVICE_NODE_NAME_MISSING } from '../../../../../common/service_nodes'; @@ -26,6 +27,7 @@ import { getLatencyColumnLabel } from '../../../shared/transactions_table/get_la import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip'; import { InstanceActionsMenu } from './instance_actions_menu'; import { ChartType, getTimeSeriesColor } from '../../../shared/charts/helper/get_timeseries_color'; +import type { ApmRoutes } from '../../../routing/apm_route_config'; type ServiceInstanceMainStatistics = APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics'>; @@ -46,6 +48,7 @@ export function getColumns({ itemIdToOpenActionMenuRowMap, offset, shouldShowSparkPlots = true, + query, }: { serviceName: string; kuery: string; @@ -59,6 +62,7 @@ export function getColumns({ toggleRowActionMenu: (selectedServiceNodeName: string) => void; itemIdToOpenActionMenuRowMap: Record; shouldShowSparkPlots?: boolean; + query: Omit['query'], 'kuery'>; }): Array> { return [ { @@ -75,12 +79,12 @@ export function getColumns({ const link = ( ({ + query={{ ...query, kuery: isMissingServiceNodeName ? `NOT (service.node.name:*)` : `service.node.name:"${item.serviceNodeName}"`, - })} + }} > {text} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx index 073c39dfa4186..ee81e265c5b2b 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx @@ -63,6 +63,7 @@ export function ServiceOverviewInstancesTable({ isNotInitiated, }: Props) { const { + query, query: { kuery, latencyAggregationType, comparisonEnabled, offset }, } = useApmParams('/services/{serviceName}'); @@ -125,6 +126,7 @@ export function ServiceOverviewInstancesTable({ itemIdToOpenActionMenuRowMap, shouldShowSparkPlots, offset, + query, }); const pagination = { diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_explorer/agent_instances/agent_instances_details/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_explorer/agent_instances/agent_instances_details/index.tsx index 3f2e67ea07071..8de982b34bb05 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_explorer/agent_instances/agent_instances_details/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_explorer/agent_instances/agent_instances_details/index.tsx @@ -11,6 +11,9 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; import type { ValuesType } from 'utility-types'; +import type { TypeOf } from '@kbn/typed-react-router-config'; +import { ENVIRONMENT_NOT_DEFINED } from '../../../../../../../common/environment_filter_values'; +import { useAnyOfApmParams } from '../../../../../../hooks/use_apm_params'; import { MetricOverviewLink } from '../../../../../shared/links/apm/metric_overview_link'; import { AgentExplorerFieldName } from '../../../../../../../common/agent_explorer'; import { isOpenTelemetryAgentName } from '../../../../../../../common/agent_name'; @@ -26,6 +29,7 @@ import { ItemsBadge } from '../../../../../shared/item_badge'; import { PopoverTooltip } from '../../../../../shared/popover_tooltip'; import { TimestampTooltip } from '../../../../../shared/timestamp_tooltip'; import { TruncateWithTooltip } from '../../../../../shared/truncate_with_tooltip'; +import type { ApmRoutes } from '../../../../../routing/apm_route_config'; type AgentExplorerInstance = ValuesType< APIReturnType<'GET /internal/apm/services/{serviceName}/agent_instances'>['items'] @@ -41,6 +45,7 @@ enum AgentExplorerInstanceFieldName { export function getInstanceColumns( serviceName: string, agentName: AgentName, + query: Omit['query'], 'kuery'>, agentDocsPageUrl?: string ): Array> { return [ @@ -103,10 +108,10 @@ export function getInstanceColumns( {serviceNode ? ( ({ + query={{ ...query, kuery: `service.node.name:"${displayedName}"`, - })} + }} > {displayedName} @@ -178,11 +183,27 @@ export function AgentInstancesDetails({ items, isLoading, }: Props) { + const { + query, + query: { environment, rangeFrom, rangeTo, serviceGroup }, + } = useAnyOfApmParams('/services/{serviceName}/overview', '/services/{serviceName}/metrics'); return ( <> = [ 'host', @@ -26,8 +30,16 @@ export function useMetricOverviewHref(serviceName: string) { interface Props extends APMLinkExtendProps { serviceName: string; + query: TypeOf['query']; } -export function MetricOverviewLink({ serviceName, ...rest }: Props) { - return ; +export function MetricOverviewLink({ serviceName, query, ...rest }: Props) { + const { link } = useApmRouter(); + const metricsOverviewLink = link('/services/{serviceName}/metrics', { + path: { + serviceName, + }, + query, + }); + return ; } From 8ae36af4d433110cd54bc9c0fb99e21a7b248416 Mon Sep 17 00:00:00 2001 From: Jenny Date: Fri, 4 Apr 2025 15:33:27 +0200 Subject: [PATCH 04/16] Refactor ErrorDetailLink to use APM router link --- .../error_group_list/index.tsx | 3 ++- .../shared/errors_table/get_columns.tsx | 2 +- .../shared/links/apm/error_detail_link.tsx | 23 ++++++++++++------- .../components/shared/links/url_helpers.ts | 2 +- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx index 258a50b2c3405..c800ef60c8936 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx @@ -152,6 +152,7 @@ export function ErrorGroupList({ {groupId.slice(0, 5) || NOT_AVAILABLE_LABEL} @@ -195,7 +196,7 @@ export function ErrorGroupList({ return ( - + {item.name || NOT_AVAILABLE_LABEL} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/errors_table/get_columns.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/errors_table/get_columns.tsx index 56f424ef46b31..6334b263273f0 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/errors_table/get_columns.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/errors_table/get_columns.tsx @@ -88,7 +88,7 @@ export function getColumns({ + {name} } diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx index 91ef41a49dcdb..53be2644dfcf5 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx @@ -6,21 +6,28 @@ */ import React from 'react'; +import type { TypeOf } from '@kbn/typed-react-router-config'; +import { EuiLink } from '@elastic/eui'; +import { useApmRouter } from '../../../../hooks/use_apm_router'; import type { APMLinkExtendProps } from './apm_link'; -import { LegacyAPMLink } from './apm_link'; +import type { ApmRoutes } from '../../../routing/apm_route_config'; interface Props extends APMLinkExtendProps { serviceName: string; errorGroupId: string; + query: TypeOf['query']; } -function ErrorDetailLink({ serviceName, errorGroupId, ...rest }: Props) { - return ( - - ); +function ErrorDetailLink({ serviceName, errorGroupId, query, ...rest }: Props) { + const { link } = useApmRouter(); + const errorDetailsLink = link('/services/{serviceName}/errors/{groupId}', { + path: { + serviceName, + groupId: errorGroupId, + }, + query, + }); + return ; } export { ErrorDetailLink }; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/url_helpers.ts b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/url_helpers.ts index 06e3b31792858..1067123481494 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/url_helpers.ts +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/url_helpers.ts @@ -66,7 +66,7 @@ export interface APMQueryParams { waterfallItemId?: string; spanId?: string; page?: string | number; - pageSize?: string; + pageSize?: string | number; sortDirection?: string; sortField?: string; kuery?: string; From 4c9bc9057b1206718e6b4800e1236bbcac4877ad Mon Sep 17 00:00:00 2001 From: Jenny Date: Fri, 4 Apr 2025 15:33:54 +0200 Subject: [PATCH 05/16] Refactor ServiceMapLink to use APM router link --- .../shared/links/apm/service_map_link.tsx | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_map_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_map_link.tsx index 66bad47e90414..d03aeea8e1e7b 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_map_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_map_link.tsx @@ -5,23 +5,29 @@ * 2.0. */ -import { EuiLink } from '@elastic/eui'; import React from 'react'; +import type { TypeOf } from '@kbn/typed-react-router-config'; +import { EuiLink } from '@elastic/eui'; +import { useApmRouter } from '../../../../hooks/use_apm_router'; +import type { ApmRoutes } from '../../../routing/apm_route_config'; import type { APMLinkExtendProps } from './apm_link'; -import { useAPMHref } from './apm_link'; - -export function useServiceMapHref(serviceName?: string) { - const path = serviceName - ? `/services/${encodeURIComponent(serviceName)}/service-map` - : '/service-map'; - return useAPMHref({ path }); -} interface ServiceMapLinkProps extends APMLinkExtendProps { serviceName?: string; + query: + | TypeOf['query'] + | TypeOf['query']; } -export function ServiceMapLink({ serviceName, ...rest }: ServiceMapLinkProps) { - const href = useServiceMapHref(serviceName); +export function ServiceMapLink({ serviceName, query, ...rest }: ServiceMapLinkProps) { + const { link } = useApmRouter(); + const href = serviceName + ? link('/services/{serviceName}/service-map', { + path: { + serviceName, + }, + query, + }) + : link('/service-map', { query }); return ; } From ebe5f6279ed36d696aec139e75b005a9f9e7ce49 Mon Sep 17 00:00:00 2001 From: Jenny Date: Fri, 4 Apr 2025 16:53:50 +0200 Subject: [PATCH 06/16] Remove LegacyAPMLink and refactor the places using it to use apm router link --- .../apm/dev_docs/routing_and_linking.md | 2 +- .../components/app/service_map/controls.tsx | 2 +- .../service_page/service_page.tsx | 21 ++++++-- .../schema/migrated/card_footer_content.tsx | 6 +-- .../anomaly_detection_setup_link.tsx | 2 +- .../app_root/apm_header_action_menu/index.tsx | 2 +- .../links/apm/agent_configuration_links.tsx | 2 +- .../shared/links/apm/apm_link.test.tsx | 54 ------------------- .../apm/{apm_link.tsx => apm_link_hooks.ts} | 14 ----- .../shared/links/apm/error_detail_link.tsx | 2 +- .../components/shared/links/apm/home_link.tsx | 9 ++-- .../shared/links/apm/metric_overview_link.tsx | 4 +- .../links/apm/service_inventory_link.tsx | 2 +- .../shared/links/apm/service_map_link.tsx | 2 +- .../apm/service_node_metric_overview_link.tsx | 4 +- .../links/apm/service_node_overview_link.tsx | 2 +- .../service_transactions_overview_link.tsx | 4 +- .../shared/links/apm/trace_overview_link.tsx | 2 +- .../apm/transaction_detail_link/index.tsx | 4 +- .../links/apm/transaction_overview_link.tsx | 4 +- .../components/shared/ml_callout/index.tsx | 2 +- .../custom_link_toolbar.tsx | 31 +++++++++-- 22 files changed, 72 insertions(+), 105 deletions(-) delete mode 100644 x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link.test.tsx rename x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/{apm_link.tsx => apm_link_hooks.ts} (82%) diff --git a/x-pack/solutions/observability/plugins/apm/dev_docs/routing_and_linking.md b/x-pack/solutions/observability/plugins/apm/dev_docs/routing_and_linking.md index 19087d12dbc9a..946e1a05569fa 100644 --- a/x-pack/solutions/observability/plugins/apm/dev_docs/routing_and_linking.md +++ b/x-pack/solutions/observability/plugins/apm/dev_docs/routing_and_linking.md @@ -72,7 +72,7 @@ const serviceOverviewLink = apmRouter.link('/services/:serviceName', { If you're not in React context, you can also import `apmRouter` directly and call its `link` function - but you have to prepend the basePath manually in that case. -We also have the [`getLegacyApmHref` function and `LegacyAPMLink` component](../public/components/shared/links/apm/apm_link.tsx), but we should consider them deprecated, in favor of `router.link`. Other components inside that directory contain other functions and components that provide the same functionality for linking to more specific sections inside the APM plugin. +We also have the [`getLegacyApmHref` and `useAPMHref` functions](../public/components/shared/links/apm/apm_link_hooks.ts), but we should consider them deprecated, in favor of `router.link`. Other components inside that directory contain other functions and components that provide the same functionality for linking to more specific sections inside the APM plugin. ### Cross-app linking diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/service_map/controls.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/service_map/controls.tsx index 4d9d5e86a4074..5a8404767d56a 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/service_map/controls.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/service_map/controls.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import React, { useContext, useEffect, useState } from 'react'; import styled from '@emotion/styled'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; -import { getLegacyApmHref } from '../../shared/links/apm/apm_link'; +import { getLegacyApmHref } from '../../shared/links/apm/apm_link_hooks'; import { useLegacyUrlParams } from '../../../context/url_params_context/use_url_params'; import type { APMQueryParams } from '../../shared/links/url_helpers'; import { CytoscapeContext } from './cytoscape'; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx index ed65abfe0a31c..e83907404c27a 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx @@ -5,11 +5,12 @@ * 2.0. */ -import { EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiButton, EuiCallOut } from '@elastic/eui'; +import { EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiButton, EuiCallOut, EuiLink } from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; import { isString } from 'lodash'; import { EuiButtonEmpty } from '@elastic/eui'; +import { useApmRouter } from '../../../../../../hooks/use_apm_router'; import type { AgentConfigurationIntake } from '../../../../../../../common/agent_configuration/configuration_types'; import { omitAllOption, @@ -18,7 +19,6 @@ import { } from '../../../../../../../common/agent_configuration/all_option'; import { useFetcher, FETCH_STATUS } from '../../../../../../hooks/use_fetcher'; import { FormRowSelect } from './form_row_select'; -import { LegacyAPMLink } from '../../../../../shared/links/apm/apm_link'; import { FormRowSuggestionsSelect } from './form_row_suggestions_select'; import { SERVICE_NAME } from '../../../../../../../common/es_fields/apm'; import { isOpenTelemetryAgentName, isEDOTAgentName } from '../../../../../../../common/agent_name'; @@ -45,6 +45,8 @@ export function ServicePage({ newConfig, setNewConfig, onClickNext }: Props) { { preservePreviousData: false } ); + const { link } = useApmRouter(); + const environments = environmentsData?.environments ?? []; const { status: agentNameStatus } = useFetcher( @@ -162,13 +164,22 @@ export function ServicePage({ newConfig, setNewConfig, onClickNext }: Props) { {/* Cancel button */} - - + + {i18n.translate('xpack.apm.agentConfig.servicePage.cancelButton', { defaultMessage: 'Cancel', })} - + {/* Next button */} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/settings/schema/migrated/card_footer_content.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/settings/schema/migrated/card_footer_content.tsx index 599d468a3713e..95a3abc13f9b1 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/settings/schema/migrated/card_footer_content.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/settings/schema/migrated/card_footer_content.tsx @@ -9,7 +9,7 @@ import { EuiButton, EuiSpacer, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; -import { LegacyAPMLink } from '../../../../shared/links/apm/apm_link'; +import { HomeLink } from '../../../../shared/links/apm/home_link'; import { useFleetCloudAgentPolicyHref } from '../../../../shared/links/kibana'; export function CardFooterContent() { @@ -33,12 +33,12 @@ export function CardFooterContent() { defaultMessage="or simply return to the {serviceInventoryLink}." values={{ serviceInventoryLink: ( - + {i18n.translate( 'xpack.apm.settings.schema.success.returnText.serviceInventoryLink', { defaultMessage: 'Service Inventory' } )} - + ), }} /> diff --git a/x-pack/solutions/observability/plugins/apm/public/components/routing/app_root/apm_header_action_menu/anomaly_detection_setup_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/routing/app_root/apm_header_action_menu/anomaly_detection_setup_link.tsx index e382b0ea4ae40..5fe2488cd8fb3 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/routing/app_root/apm_header_action_menu/anomaly_detection_setup_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/routing/app_root/apm_header_action_menu/anomaly_detection_setup_link.tsx @@ -18,7 +18,7 @@ import { import { useAnomalyDetectionJobsContext } from '../../../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { useApmParams } from '../../../../hooks/use_apm_params'; -import { getLegacyApmHref } from '../../../shared/links/apm/apm_link'; +import { getLegacyApmHref } from '../../../shared/links/apm/apm_link_hooks'; export function AnomalyDetectionSetupLink() { const { query } = useApmParams('/*'); diff --git a/x-pack/solutions/observability/plugins/apm/public/components/routing/app_root/apm_header_action_menu/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/routing/app_root/apm_header_action_menu/index.tsx index 06d04c94cfddd..8b903c49da16a 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/routing/app_root/apm_header_action_menu/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/routing/app_root/apm_header_action_menu/index.tsx @@ -10,7 +10,7 @@ import { apmLabsButton } from '@kbn/observability-plugin/common'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { getAlertingCapabilities } from '../../../alerting/utils/get_alerting_capabilities'; -import { getLegacyApmHref } from '../../../shared/links/apm/apm_link'; +import { getLegacyApmHref } from '../../../shared/links/apm/apm_link_hooks'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { AlertingPopoverAndFlyout } from './alerting_popover_flyout'; import { AnomalyDetectionSetupLink } from './anomaly_detection_setup_link'; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/agent_configuration_links.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/agent_configuration_links.tsx index 2e1030653d5c6..7cd0779e19cc2 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/agent_configuration_links.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/agent_configuration_links.tsx @@ -7,7 +7,7 @@ import type { IBasePath } from '@kbn/core/public'; import type { AgentConfigurationIntake } from '../../../../../common/agent_configuration/configuration_types'; -import { getLegacyApmHref } from './apm_link'; +import { getLegacyApmHref } from './apm_link_hooks'; export function editAgentConfigurationHref( configService: AgentConfigurationIntake['service'], diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link.test.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link.test.tsx deleted file mode 100644 index 4f889f2eda5a6..0000000000000 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link.test.tsx +++ /dev/null @@ -1,54 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { Location } from 'history'; -import React from 'react'; -import { getRenderedHref } from '../../../../utils/test_helpers'; -import { LegacyAPMLink } from './apm_link'; - -describe('LegacyAPMLink', () => { - test('LegacyAPMLink should produce the correct URL', async () => { - const href = await getRenderedHref( - () => , - { - search: '?rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0', - } as Location - ); - - expect(href).toMatchInlineSnapshot( - `"/basepath/app/apm/some/path?rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0&transactionId=blah"` - ); - }); - - test('LegacyAPMLink should retain current kuery value if it exists', async () => { - const href = await getRenderedHref( - () => , - { - search: - '?kuery=host.hostname~20~3A~20~22fakehostname~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0', - } as Location - ); - - expect(href).toMatchInlineSnapshot( - `"/basepath/app/apm/some/path?kuery=host.hostname~20~3A~20~22fakehostname~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0&transactionId=blah"` - ); - }); - - test('LegacyAPMLink should overwrite current kuery value if new kuery value is provided', async () => { - const href = await getRenderedHref( - () => , - { - search: - '?kuery=host.hostname~20~3A~20~22fakehostname~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0', - } as Location - ); - - expect(href).toMatchInlineSnapshot( - `"/basepath/app/apm/some/path?kuery=host.os~20~3A~20~22linux~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0"` - ); - }); -}); diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link_hooks.ts similarity index 82% rename from x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link.tsx rename to x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link_hooks.ts index 6d3d57bc6b660..ecbb470d44802 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link_hooks.ts @@ -6,10 +6,8 @@ */ import type { EuiLinkAnchorProps } from '@elastic/eui'; -import { EuiLink } from '@elastic/eui'; import type { IBasePath } from '@kbn/core/public'; import { pick } from 'lodash'; -import React from 'react'; import { useLocation } from 'react-router-dom'; import url from 'url'; import { pickKeys } from '../../../../../common/utils/pick_keys'; @@ -87,15 +85,3 @@ export function getLegacyApmHref({ search: nextSearch, }); } - -export function LegacyAPMLink({ path = '', query, mergeQuery, ...rest }: Props) { - const { core } = useApmPluginContext(); - const { search } = useLocation(); - const { basePath } = core.http; - - const mergedQuery = mergeQuery ? mergeQuery(query ?? {}) : query; - - const href = getLegacyApmHref({ basePath, path, search, query: mergedQuery }); - - return ; -} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx index 53be2644dfcf5..8cb45e026993c 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx @@ -9,7 +9,7 @@ import React from 'react'; import type { TypeOf } from '@kbn/typed-react-router-config'; import { EuiLink } from '@elastic/eui'; import { useApmRouter } from '../../../../hooks/use_apm_router'; -import type { APMLinkExtendProps } from './apm_link'; +import type { APMLinkExtendProps } from './apm_link_hooks'; import type { ApmRoutes } from '../../../routing/apm_route_config'; interface Props extends APMLinkExtendProps { diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/home_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/home_link.tsx index 9d7b27b24daed..33b39df6cc478 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/home_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/home_link.tsx @@ -6,11 +6,14 @@ */ import React from 'react'; -import type { APMLinkExtendProps } from './apm_link'; -import { LegacyAPMLink } from './apm_link'; +import { EuiLink } from '@elastic/eui'; +import { useApmRouter } from '../../../../hooks/use_apm_router'; +import type { APMLinkExtendProps } from './apm_link_hooks'; function HomeLink(props: APMLinkExtendProps) { - return ; + const { link } = useApmRouter(); + const homeLink = link('/'); + return ; } export { HomeLink }; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx index a541e39f920ee..e67dd80f03dac 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx @@ -10,9 +10,9 @@ import type { TypeOf } from '@kbn/typed-react-router-config'; import { EuiLink } from '@elastic/eui'; import type { ApmRoutes } from '../../../routing/apm_route_config'; import type { APMQueryParams } from '../url_helpers'; -import { useAPMHref } from './apm_link'; +import { useAPMHref } from './apm_link_hooks'; import { useApmRouter } from '../../../../hooks/use_apm_router'; -import type { APMLinkExtendProps } from './apm_link'; +import type { APMLinkExtendProps } from './apm_link_hooks'; const persistedFilters: Array = [ 'host', diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_inventory_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_inventory_link.tsx index 4d95f3c70a483..6327ab1c91b84 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_inventory_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_inventory_link.tsx @@ -6,7 +6,7 @@ */ import type { APMQueryParams } from '../url_helpers'; -import { useAPMHref } from './apm_link'; +import { useAPMHref } from './apm_link_hooks'; const persistedFilters: Array = ['host', 'agentName']; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_map_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_map_link.tsx index d03aeea8e1e7b..78981e0422ce4 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_map_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_map_link.tsx @@ -10,7 +10,7 @@ import type { TypeOf } from '@kbn/typed-react-router-config'; import { EuiLink } from '@elastic/eui'; import { useApmRouter } from '../../../../hooks/use_apm_router'; import type { ApmRoutes } from '../../../routing/apm_route_config'; -import type { APMLinkExtendProps } from './apm_link'; +import type { APMLinkExtendProps } from './apm_link_hooks'; interface ServiceMapLinkProps extends APMLinkExtendProps { serviceName?: string; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx index d0f3e7b334931..826bf86c7a97d 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx @@ -8,8 +8,8 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; import type { APMQueryParams } from '../url_helpers'; -import type { APMLinkExtendProps } from './apm_link'; -import { useAPMHref } from './apm_link'; +import type { APMLinkExtendProps } from './apm_link_hooks'; +import { useAPMHref } from './apm_link_hooks'; interface Props extends APMLinkExtendProps { serviceName: string; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_overview_link.tsx index 93a52a9b470ff..a73c65998c38d 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_overview_link.tsx @@ -6,7 +6,7 @@ */ import type { APMQueryParams } from '../url_helpers'; -import { useAPMHref } from './apm_link'; +import { useAPMHref } from './apm_link_hooks'; const persistedFilters: Array = [ 'host', diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx index 6dbbb6e47535d..a3af952dcaaeb 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx @@ -8,8 +8,8 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; import type { APMQueryParams } from '../url_helpers'; -import type { APMLinkExtendProps } from './apm_link'; -import { useAPMHref } from './apm_link'; +import type { APMLinkExtendProps } from './apm_link_hooks'; +import { useAPMHref } from './apm_link_hooks'; import { removeUndefinedProps } from '../../../../context/url_params_context/helpers'; const persistedFilters: Array = [ diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/trace_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/trace_overview_link.tsx index 2462c5905c5e4..9eb70c34d393d 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/trace_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/trace_overview_link.tsx @@ -6,7 +6,7 @@ */ import type { APMQueryParams } from '../url_helpers'; -import { useAPMHref } from './apm_link'; +import { useAPMHref } from './apm_link_hooks'; const persistedFilters: Array = [ 'transactionResult', diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx index d9299ba3be101..8756e41b77108 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx @@ -18,8 +18,8 @@ import { PopoverTooltip } from '../../../popover_tooltip'; import { getComparisonEnabled } from '../../../time_comparison/get_comparison_enabled'; import { TruncateWithTooltip } from '../../../truncate_with_tooltip'; import type { APMQueryParams } from '../../url_helpers'; -import type { APMLinkExtendProps } from '../apm_link'; -import { getLegacyApmHref } from '../apm_link'; +import type { APMLinkExtendProps } from '../apm_link_hooks'; +import { getLegacyApmHref } from '../apm_link_hooks'; import { MaxGroupsMessage } from '../max_groups_message'; export const txGroupsDroppedBucketName = '_other'; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx index cb185dd6bc378..0e02a436a6579 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx @@ -12,8 +12,8 @@ import { useAnyOfApmParams } from '../../../../hooks/use_apm_params'; import { useApmRouter } from '../../../../hooks/use_apm_router'; import { removeUndefinedProps } from '../../../../context/url_params_context/helpers'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; -import type { APMLinkExtendProps } from './apm_link'; -import { getLegacyApmHref } from './apm_link'; +import type { APMLinkExtendProps } from './apm_link_hooks'; +import { getLegacyApmHref } from './apm_link_hooks'; interface Props extends APMLinkExtendProps { serviceName: string; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/ml_callout/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/ml_callout/index.tsx index 0a6058a2c28c6..3a8671f384811 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/ml_callout/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/ml_callout/index.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; import { AnomalyDetectionSetupState } from '../../../../common/anomaly_detection/get_anomaly_detection_setup_state'; import { useMlManageJobsHref } from '../../../hooks/use_ml_manage_jobs_href'; -import { useAPMHref } from '../links/apm/apm_link'; +import { useAPMHref } from '../links/apm/apm_link_hooks'; export function shouldDisplayMlCallout(anomalyDetectionSetupState: AnomalyDetectionSetupState) { return ( diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/custom_link_toolbar.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/custom_link_toolbar.tsx index 7c6734cf3d598..1139ac926fa67 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/custom_link_toolbar.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/custom_link_toolbar.tsx @@ -6,11 +6,18 @@ */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiButtonEmpty, EuiIcon } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiToolTip, + EuiButtonEmpty, + EuiIcon, + EuiLink, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useApmRouter } from '../../../../hooks/use_apm_router'; import { NO_PERMISSION_LABEL } from '../../../../../common/custom_link'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; -import { LegacyAPMLink } from '../../links/apm/apm_link'; export function CustomLinkToolbar({ onClickCreate, @@ -21,6 +28,7 @@ export function CustomLinkToolbar({ }) { const { core } = useApmPluginContext(); const canSave = !!core.application.capabilities.apm.save; + const { link } = useApmRouter(); return ( @@ -33,15 +41,28 @@ export function CustomLinkToolbar({ defaultMessage: 'Manage custom links', })} > - - - + + + {showCreateButton && ( Date: Fri, 4 Apr 2025 18:29:15 +0200 Subject: [PATCH 07/16] Refactor transactionViewLink --- .../apm/transaction_overview_link.test.tsx | 92 ++++++++++++------- .../links/apm/transaction_overview_link.tsx | 48 +++------- .../shared/transactions_table/index.tsx | 18 +--- 3 files changed, 75 insertions(+), 83 deletions(-) diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.test.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.test.tsx index a1836a2a336e0..3f69ff7256dc6 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.test.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.test.tsx @@ -5,12 +5,13 @@ * 2.0. */ -import { render, renderHook } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { createMemoryHistory } from 'history'; import React from 'react'; import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; import { MockUrlParamsContextProvider } from '../../../../context/url_params_context/mock_url_params_context_provider'; -import { TransactionOverviewLink, useTransactionsOverviewHref } from './transaction_overview_link'; +import { TransactionOverviewLink } from './transaction_overview_link'; +import type { LatencyAggregationType } from '../../../../../common/latency_aggregation_types'; const history = createMemoryHistory(); @@ -23,53 +24,82 @@ function Wrapper({ children }: React.PropsWithChildren) { } describe('Transactions overview link', () => { - describe('useTransactionsOverviewHref', () => { - it('returns transaction link', () => { - const { result } = renderHook(() => useTransactionsOverviewHref({ serviceName: 'foo' }), { - wrapper: Wrapper, - }); - expect(result.current).toEqual('/basepath/app/apm/services/foo/transactions'); - }); - - it('returns transaction link with persisted query items', () => { - const { result } = renderHook( - () => - useTransactionsOverviewHref({ - serviceName: 'foo', - latencyAggregationType: 'avg', - }), - { wrapper: Wrapper } - ); - expect(result.current).toEqual( - '/basepath/app/apm/services/foo/transactions?latencyAggregationType=avg' - ); - }); - }); describe('TransactionOverviewLink', () => { function getHref(container: HTMLElement) { return ((container as HTMLDivElement).children[0] as HTMLAnchorElement).href; } - it('returns transaction link', () => { + it('returns transaction link with persisted query and prop items', () => { + const avg = 'avg' as LatencyAggregationType; const { container } = render( - Service name + + Service name + ); expect(getHref(container)).toEqual( - 'http://localhost/basepath/app/apm/services/foo/transactions' + 'http://localhost/basepath/app/apm/services/foo/transactions?comparisonEnabled=false&environment=production&kuery=&latencyAggregationType=avg&rangeFrom=now-15m&rangeTo=now&serviceGroup=&transactionType=request' ); }); - - it('returns transaction link with persisted query items', () => { + it('returns transaction link with persisted without transaction type', () => { + const avg = 'avg' as LatencyAggregationType; + const { container } = render( + + + Service name + + + ); + expect(getHref(container)).toEqual( + 'http://localhost/basepath/app/apm/services/foo/transactions?comparisonEnabled=false&environment=production&kuery=&latencyAggregationType=avg&rangeFrom=now-15m&rangeTo=now&serviceGroup=' + ); + }); + it('returns transaction link with persisted query with transaction type', () => { + const avg = 'avg' as LatencyAggregationType; const { container } = render( - + Service name ); expect(getHref(container)).toEqual( - 'http://localhost/basepath/app/apm/services/foo/transactions?latencyAggregationType=avg' + 'http://localhost/basepath/app/apm/services/foo/transactions?comparisonEnabled=false&environment=production&kuery=&latencyAggregationType=avg&rangeFrom=now-15m&rangeTo=now&serviceGroup=&transactionType=request' ); }); }); diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx index 0e02a436a6579..d40e0a942b16d 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx @@ -7,57 +7,35 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; -import { useLocation } from 'react-router-dom'; -import { useAnyOfApmParams } from '../../../../hooks/use_apm_params'; +import type { TypeOf } from '@kbn/typed-react-router-config/src/types'; +import type { LatencyAggregationType } from '../../../../../common/latency_aggregation_types'; import { useApmRouter } from '../../../../hooks/use_apm_router'; -import { removeUndefinedProps } from '../../../../context/url_params_context/helpers'; -import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import type { APMLinkExtendProps } from './apm_link_hooks'; -import { getLegacyApmHref } from './apm_link_hooks'; +import type { ApmRoutes } from '../../../routing/apm_route_config'; interface Props extends APMLinkExtendProps { serviceName: string; - latencyAggregationType?: string; + latencyAggregationType?: LatencyAggregationType; transactionType?: string; -} - -// TODO remove legacy and refactor -export function useTransactionsOverviewHref({ - serviceName, - latencyAggregationType, - transactionType, -}: Props) { - const { core } = useApmPluginContext(); - const location = useLocation(); - const { search } = location; - - const query = { latencyAggregationType, transactionType }; - - return getLegacyApmHref({ - basePath: core.http.basePath, - path: `/services/${serviceName}/transactions`, - query: removeUndefinedProps(query), - search, - }); + query: TypeOf['query']; } export function TransactionOverviewLink({ serviceName, latencyAggregationType, transactionType, + query, ...rest }: Props) { - const { query } = useAnyOfApmParams( - '/services/{serviceName}/transactions', - '/services/{serviceName}/overview', - '/mobile-services/{serviceName}/transactions', - '/mobile-services/{serviceName}/overview' - ); - const apmRouter = useApmRouter(); + const { link } = useApmRouter(); - const href = apmRouter.link('/services/{serviceName}/transactions', { + const href = link('/services/{serviceName}/transactions', { path: { serviceName }, - query, + query: { + ...query, + latencyAggregationType, + transactionType: transactionType ?? query.transactionType, + }, }); return ; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/index.tsx index 774da199379b8..a09b2a985c546 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/index.tsx @@ -160,22 +160,6 @@ export function TransactionsTable({ }; }, [isTableSearchBarEnabled, mainStatistics.maxCountExceeded, setSearchQueryDebounced]); - const transactionViewLink = link('/services/{serviceName}/transactions/view', { - path: { serviceName }, - query: { - environment, - transactionName: '', - showCriticalPath: false, - transactionType, - latencyAggregationType, - rangeFrom: start, - rangeTo: end, - kuery, - serviceGroup: '', - comparisonEnabled: !!comparisonEnabled, - }, - }); - useEffect(() => { return setScreenContext?.({ data: [ @@ -213,7 +197,7 @@ export function TransactionsTable({ serviceName={serviceName} latencyAggregationType={latencyAggregationType} transactionType={transactionType} - href={transactionViewLink} + query={query} > {i18n.translate('xpack.apm.transactionsTable.linkText', { defaultMessage: 'View transactions', From 441bcda0adcbc955650d625efa34618f83d3bed6 Mon Sep 17 00:00:00 2001 From: Jenny Date: Fri, 4 Apr 2025 18:58:17 +0200 Subject: [PATCH 08/16] Add test for the encoding logic in getLegacyApmHref --- .../observability_integration/get_apm_href.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/lib/helper/observability_integration/get_apm_href.test.ts b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/lib/helper/observability_integration/get_apm_href.test.ts index 9d4ea4b4cf8d4..66666b96fcf72 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/lib/helper/observability_integration/get_apm_href.test.ts +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/lib/helper/observability_integration/get_apm_href.test.ts @@ -63,4 +63,16 @@ describe('getLegacyApmHref', () => { ); }); }); + describe('with service.name that needs encoding', () => { + const serviceNameWithSlashes = 'My/Service/Name'; + beforeEach(() => { + summary.state.service = { name: serviceNameWithSlashes }; + }); + it('links to the service with encoding', () => { + const result = getLegacyApmHref(summary, 'foo', 'now-15m', 'now'); + expect(result).toMatchInlineSnapshot( + `"foo/app/apm/services/My%2FService%2FName/overview/?rangeFrom=now-15m&rangeTo=now"` + ); + }); + }); }); From 9bd2a9325f94aef4e89b580ebe5b21aafbd877f6 Mon Sep 17 00:00:00 2001 From: Jenny Date: Fri, 4 Apr 2025 18:59:02 +0200 Subject: [PATCH 09/16] Refactor useAPMHref to support param encoding --- .../kbn-typed-react-router-config/index.ts | 1 + .../src/create_router.ts | 9 +-- .../src/encode_path.test.ts | 57 +++++++++++++++++++ .../src/encode_path.ts | 19 +++++++ .../shared/links/apm/apm_link_hooks.ts | 7 ++- .../shared/links/apm/metric_overview_link.tsx | 3 +- .../apm/service_node_metric_overview_link.tsx | 5 +- .../links/apm/service_node_overview_link.tsx | 3 +- .../service_transactions_overview_link.tsx | 3 +- 9 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 src/platform/packages/shared/kbn-typed-react-router-config/src/encode_path.test.ts create mode 100644 src/platform/packages/shared/kbn-typed-react-router-config/src/encode_path.ts diff --git a/src/platform/packages/shared/kbn-typed-react-router-config/index.ts b/src/platform/packages/shared/kbn-typed-react-router-config/index.ts index 6aff73c55e6c3..3efecb2d77d43 100644 --- a/src/platform/packages/shared/kbn-typed-react-router-config/index.ts +++ b/src/platform/packages/shared/kbn-typed-react-router-config/index.ts @@ -8,6 +8,7 @@ */ export * from './src/create_router'; +export * from './src/encode_path'; export * from './src/types'; export * from './src/outlet'; export * from './src/route_renderer'; diff --git a/src/platform/packages/shared/kbn-typed-react-router-config/src/create_router.ts b/src/platform/packages/shared/kbn-typed-react-router-config/src/create_router.ts index 4321f4c0744a7..4451603f33fa6 100644 --- a/src/platform/packages/shared/kbn-typed-react-router-config/src/create_router.ts +++ b/src/platform/packages/shared/kbn-typed-react-router-config/src/create_router.ts @@ -19,6 +19,7 @@ import { RouteConfig as ReactRouterConfig, } from 'react-router-config'; import { FlattenRoutesOf, Route, RouteMap, Router, RouteWithPath } from './types'; +import { encodePath } from './encode_path'; function toReactRouterPath(path: string) { return path.replace(/(?:{([^\/]+)})/g, ':$1'); @@ -177,13 +178,7 @@ export function createRouter(routes: TRoutes): Router< const paramsWithBuiltInDefaults = merge({ path: {}, query: {} }, params); - path = path - .split('/') - .map((part) => { - const match = part.match(/(?:{([a-zA-Z]+)})/); - return match ? encodeURIComponent(paramsWithBuiltInDefaults.path[match[1]]) : part; - }) - .join('/'); + path = encodePath(path, paramsWithBuiltInDefaults?.path); const matchedRoutes = getRoutesToMatch(path); diff --git a/src/platform/packages/shared/kbn-typed-react-router-config/src/encode_path.test.ts b/src/platform/packages/shared/kbn-typed-react-router-config/src/encode_path.test.ts new file mode 100644 index 0000000000000..7bff3d81d7bc9 --- /dev/null +++ b/src/platform/packages/shared/kbn-typed-react-router-config/src/encode_path.test.ts @@ -0,0 +1,57 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { encodePath } from './encode_path'; + +describe('encodePath', () => { + it('should return the same path if no pathParams are provided', () => { + const path = '/services/{serviceName}/transactions'; + const result = encodePath(path); + expect(result).toBe(path); + }); + + it('should encode path parameters correctly', () => { + const path = '/services/{serviceName}/transactions'; + const pathParams = { serviceName: 'my/service' }; + const result = encodePath(path, pathParams); + expect(result).toBe('/services/my%2Fservice/transactions'); + }); + + it('should handle two matching path parameters', () => { + const path = '/services/{serviceName}/transactions/{transactionId}'; + const pathParams = { serviceName: 'my/service', transactionId: '123/456' }; + const result = encodePath(path, pathParams); + expect(result).toBe('/services/my%2Fservice/transactions/123%2F456'); + }); + + it('should handle multiple path parameters', () => { + const path = '/services/{serviceName}/transactions/{transactionId}/details/{detailId}'; + const pathParams = { + serviceName: 'my/service', + transactionId: '123/456', + detailId: '111/222/333', + }; + const result = encodePath(path, pathParams); + expect(result).toBe('/services/my%2Fservice/transactions/123%2F456/details/111%2F222%2F333'); + }); + + it('should return the same path if no matching parameters are found', () => { + const path = '/services/{serviceName}/transactions'; + const pathParams = { otherParam: 'value' }; + const result = encodePath(path, pathParams); + expect(result).toBe(path); + }); + + it('should handle a path without placeholders', () => { + const path = '/services/transactions'; + const pathParams = { serviceName: 'my/service' }; + const result = encodePath(path, pathParams); + expect(result).toBe('/services/transactions'); + }); +}); diff --git a/src/platform/packages/shared/kbn-typed-react-router-config/src/encode_path.ts b/src/platform/packages/shared/kbn-typed-react-router-config/src/encode_path.ts new file mode 100644 index 0000000000000..c86da50b53b4d --- /dev/null +++ b/src/platform/packages/shared/kbn-typed-react-router-config/src/encode_path.ts @@ -0,0 +1,19 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export const encodePath = (path: string, pathParams?: Record) => + pathParams && Object.keys(pathParams).length > 0 + ? path + .split('/') + .map((part) => { + const match = part.match(/(?:{([a-zA-Z]+)})/); + return match && pathParams[match[1]] ? encodeURIComponent(pathParams[match[1]]) : part; + }) + .join('/') + : path; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link_hooks.ts b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link_hooks.ts index ecbb470d44802..12ab4a2ee9cd1 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link_hooks.ts +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/apm_link_hooks.ts @@ -10,6 +10,7 @@ import type { IBasePath } from '@kbn/core/public'; import { pick } from 'lodash'; import { useLocation } from 'react-router-dom'; import url from 'url'; +import { encodePath } from '@kbn/typed-react-router-config'; import { pickKeys } from '../../../../../common/utils/pick_keys'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params'; @@ -43,10 +44,12 @@ export function useAPMHref({ path, persistedFilters, query, + pathParams = undefined, }: { path: string; persistedFilters?: Array; query?: APMQueryParams; + pathParams?: Record; }) { const { urlParams } = useLegacyUrlParams(); const { basePath } = useApmPluginContext().core.http; @@ -56,7 +59,9 @@ export function useAPMHref({ ...query, }; - return getLegacyApmHref({ basePath, path, query: nextQuery, search }); + const encodedPath = encodePath(path, pathParams); + + return getLegacyApmHref({ basePath, path: encodedPath, query: nextQuery, search }); } /** diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx index e67dd80f03dac..001bc771b3b28 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx @@ -23,7 +23,8 @@ const persistedFilters: Array = [ export function useMetricOverviewHref(serviceName: string) { return useAPMHref({ - path: `/services/${encodeURIComponent(serviceName)}/metrics`, + path: `/services/{serviceName}/metrics`, + pathParams: { serviceName }, persistedFilters, }); } diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx index 826bf86c7a97d..397ff4456f526 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx @@ -31,9 +31,8 @@ export function useServiceNodeMetricOverviewHref({ serviceNodeName: string; }) { return useAPMHref({ - path: `/services/${encodeURIComponent(serviceName)}/metrics/${encodeURIComponent( - serviceNodeName - )}`, + path: '/services/{serviceName}/metrics/{serviceNodeName}', + pathParams: { serviceName, serviceNodeName }, persistedFilters, }); } diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_overview_link.tsx index a73c65998c38d..8452738820a0d 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_node_overview_link.tsx @@ -17,7 +17,8 @@ const persistedFilters: Array = [ export function useServiceNodeOverviewHref(serviceName: string) { return useAPMHref({ - path: `/services/${encodeURIComponent(serviceName)}/nodes`, + path: '/services/{serviceName}/nodes', + pathParams: { serviceName }, persistedFilters, }); } diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx index a3af952dcaaeb..0fbcd4fb4d6e1 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx @@ -34,7 +34,8 @@ export function useServiceOrTransactionsOverviewHref({ }: Props) { const query = { environment, transactionType }; return useAPMHref({ - path: `/services/${encodeURIComponent(serviceName)}`, + path: '/services/{serviceName}', + pathParams: { serviceName }, persistedFilters, query: removeUndefinedProps(query), }); From 997f79470d6c2db92e28779deaf6c8742edda6c2 Mon Sep 17 00:00:00 2001 From: Jenny Date: Fri, 4 Apr 2025 19:41:10 +0200 Subject: [PATCH 10/16] Refactor TransactionDetailLink --- .../error_sampler/error_sample_detail.tsx | 46 ++++++------- .../maybe_view_trace_link.tsx | 53 +++++++-------- .../waterfall/flyout_top_level_properties.tsx | 32 +++------ .../span_flyout/sticky_span_properties.tsx | 32 +++------ .../transaction_detail_link/index.test.tsx | 60 ++++------------ .../apm/transaction_detail_link/index.tsx | 68 +------------------ .../shared/transactions_table/get_columns.tsx | 6 -- 7 files changed, 82 insertions(+), 215 deletions(-) diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx index 026f931869805..e0d36ccf9cb22 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx @@ -56,6 +56,7 @@ import { ErrorTabKey, getTabs } from './error_tabs'; import { ErrorUiActionsContextMenu } from './error_ui_actions_context_menu'; import { SampleSummary } from './sample_summary'; import { ErrorSampleContextualInsight } from './error_sample_contextual_insight'; +import { getComparisonEnabled } from '../../../shared/time_comparison/get_comparison_enabled'; const TransactionLinkName = styled.div` margin-left: ${({ theme }) => theme.euiTheme.size.s}; @@ -91,7 +92,7 @@ export function ErrorSampleDetails({ urlParams: { detailTab, offset, comparisonEnabled }, } = useLegacyUrlParams(); - const { uiActions } = useApmPluginContext(); + const { uiActions, core } = useApmPluginContext(); const router = useApmRouter(); @@ -114,6 +115,11 @@ export function ErrorSampleDetails({ const isSucceeded = isSuccess(errorSamplesFetchStatus) && isSuccess(errorFetchStatus); + const defaultComparisonEnabled = getComparisonEnabled({ + core, + urlComparisonEnabled: comparisonEnabled, + }); + useEffect(() => { setSampleActivePage(0); }, [errorSampleIds]); @@ -176,23 +182,6 @@ export function ErrorSampleDetails({ }, }); - const transactionViewLink = transaction - ? router.link('/services/{serviceName}/transactions/view', { - path: { serviceName: transaction.service.name }, - query: { - ...query, - traceId: transaction.trace.id, - transactionId: transaction.transaction.id, - transactionName: transaction.transaction.name, - transactionType: transaction.transaction.type, - comparisonEnabled: !!comparisonEnabled, - showCriticalPath: false, - offset, - kuery, - }, - }) - : undefined; - return ( @@ -275,14 +264,21 @@ export function ErrorSampleDetails({ })} > {transaction.transaction.name} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx index 5e0226fa570df..ca2bd52d261e8 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx @@ -8,6 +8,7 @@ import { EuiButton, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { useApmRouter } from '../../../../hooks/use_apm_router'; import { getNextEnvironmentUrlParam } from '../../../../../common/environment_filter_values'; import type { Transaction as ITransaction } from '../../../../../typings/es_schemas/ui/transaction'; @@ -16,6 +17,7 @@ import type { IWaterfall } from './waterfall_container/waterfall/waterfall_helpe import type { Environment } from '../../../../../common/environment_rt'; import { useAnyOfApmParams } from '../../../../hooks/use_apm_params'; import { LatencyAggregationType } from '../../../../../common/latency_aggregation_types'; +import { getComparisonEnabled } from '../../../shared/time_comparison/get_comparison_enabled'; function FullTraceButton({ isLoading, isDisabled }: { isLoading?: boolean; isDisabled?: boolean }) { return ( @@ -55,6 +57,12 @@ export function MaybeViewTraceLink({ ); const { link } = useApmRouter(); + const { core } = useApmPluginContext(); + + const defaultComparisonEnabled = getComparisonEnabled({ + core, + urlComparisonEnabled: comparisonEnabled, + }); const latencyAggregationType = ('latencyAggregationType' in query && query.latencyAggregationType) || @@ -85,27 +93,6 @@ export function MaybeViewTraceLink({ currentEnvironmentUrlParam: environment, }); - const transactionViewLink = rootTransaction - ? link('/services/{serviceName}/transactions/view', { - path: { serviceName: rootTransaction.service.name }, - query: { - ...query, - traceId: rootTransaction.trace.id, - transactionId: rootTransaction.transaction.id, - transactionName: rootTransaction.transaction.name, - transactionType: rootTransaction.transaction.type, - comparisonEnabled: !!comparisonEnabled, - showCriticalPath: false, - offset, - environment: getNextEnvironmentUrlParam({ - requestedEnvironment: rootTransaction.service.environment, - currentEnvironmentUrlParam: environment, - }), - serviceGroup: '', - }, - }) - : undefined; - // the user is already viewing the full trace, so don't link to it if (isRoot) { return ( @@ -122,16 +109,22 @@ export function MaybeViewTraceLink({ } else { return ( diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/flyout_top_level_properties.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/flyout_top_level_properties.tsx index edfae7d95d6ad..f315382b79a46 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/flyout_top_level_properties.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/flyout_top_level_properties.tsx @@ -35,24 +35,10 @@ export function FlyoutTopLevelProperties({ transaction }: Props) { LatencyAggregationType.avg; const serviceGroup = ('serviceGroup' in query && query.serviceGroup) || ''; - const { comparisonEnabled, offset } = query; - if (!transaction) { return null; } - const transactionViewLink = transaction - ? link('/services/{serviceName}/transactions/view', { - path: { serviceName: transaction?.service.name }, - query: { - ...query, - serviceGroup, - latencyAggregationType, - transactionName: transaction.transaction.name, - }, - }) - : undefined; - const nextEnvironment = getNextEnvironmentUrlParam({ requestedEnvironment: transaction.service.environment, currentEnvironmentUrlParam: query.environment, @@ -90,16 +76,16 @@ export function FlyoutTopLevelProperties({ transaction }: Props) { fieldName: TRANSACTION_NAME, val: ( {transaction.transaction.name} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/sticky_span_properties.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/sticky_span_properties.tsx index 877a263364836..730af1182c35f 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/sticky_span_properties.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/sticky_span_properties.tsx @@ -40,7 +40,7 @@ export function StickySpanProperties({ span, transaction }: Props) { ); const router = useApmRouter(); - const { environment, comparisonEnabled, offset } = query; + const { environment } = query; const latencyAggregationType = ('latencyAggregationType' in query && query.latencyAggregationType) || @@ -57,17 +57,6 @@ export function StickySpanProperties({ span, transaction }: Props) { const spanName = span.span.name; const dependencyName = span.span.destination?.service.resource; - const transactionViewLink = transaction - ? router.link('/services/{serviceName}/transactions/view', { - path: { serviceName: transaction?.service.name }, - query: { - ...query, - serviceGroup, - latencyAggregationType, - transactionName: transaction.transaction.name, - }, - }) - : undefined; const transactionStickyProperties = transaction ? [ @@ -96,16 +85,17 @@ export function StickySpanProperties({ span, transaction }: Props) { fieldName: TRANSACTION_NAME, val: ( {transaction.transaction.name} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.test.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.test.tsx index 5c405c711c8b3..88d023700fdfc 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.test.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.test.tsx @@ -10,51 +10,21 @@ import { getRenderedHref } from '../../../../../utils/test_helpers'; import { TransactionDetailLink } from '.'; describe('TransactionDetailLink', () => { - describe('With comparison in the url', () => { - it('returns comparison defined in the url', async () => { - const href = await getRenderedHref( - () => ( - - Transaction - - ), - {} as Location - ); + it('returns the correct url', async () => { + const href = await getRenderedHref( + () => ( + + Transaction + + ), + {} as Location + ); - expect(href).toMatchInlineSnapshot( - '"/basepath/app/apm/services/foo/transactions/view?traceId=baz&transactionId=123&transactionName=bar&transactionType=request&comparisonEnabled=true&offset=1w"' - ); - }); - }); - - describe('use default comparison', () => { - it('returns default comparison', async () => { - const href = await getRenderedHref( - () => ( - - Transaction - - ), - {} as Location - ); - - expect(href).toMatchInlineSnapshot( - '"/basepath/app/apm/services/foo/transactions/view?traceId=baz&transactionId=123&transactionName=bar&transactionType=request&comparisonEnabled=true&offset=1d"' - ); - }); + expect(href).toMatchInlineSnapshot( + '"/basepath/app/apm/services/foo/transactions/view?traceId=baz&transactionId=123&transactionName=bar&transactionType=request&comparisonEnabled=true&offset=1w"' + ); }); }); diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx index 8756e41b77108..4f0b1097f89a7 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx @@ -7,88 +7,26 @@ import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { identity, pickBy } from 'lodash'; import React from 'react'; -import { useLocation } from 'react-router-dom'; -import { pickKeys } from '../../../../../../common/utils/pick_keys'; -import { useApmPluginContext } from '../../../../../context/apm_plugin/use_apm_plugin_context'; -import { useLegacyUrlParams } from '../../../../../context/url_params_context/use_url_params'; import { unit } from '../../../../../utils/style'; import { PopoverTooltip } from '../../../popover_tooltip'; -import { getComparisonEnabled } from '../../../time_comparison/get_comparison_enabled'; import { TruncateWithTooltip } from '../../../truncate_with_tooltip'; -import type { APMQueryParams } from '../../url_helpers'; import type { APMLinkExtendProps } from '../apm_link_hooks'; -import { getLegacyApmHref } from '../apm_link_hooks'; import { MaxGroupsMessage } from '../max_groups_message'; export const txGroupsDroppedBucketName = '_other'; interface Props extends APMLinkExtendProps { - serviceName: string; - traceId?: string; - transactionId?: string; transactionName: string; - transactionType?: string; - latencyAggregationType?: string; - href?: string; - environment?: string; - comparisonEnabled?: boolean; - offset?: string; - overflowCount?: number; + href: string; } -const persistedFilters: Array = ['transactionResult', 'serviceVersion']; - -export function TransactionDetailLink({ - serviceName, - traceId, - transactionId, - transactionName, - transactionType, - latencyAggregationType, - environment, - comparisonEnabled, - offset = '1d', - overflowCount = 0, - href, - ...rest -}: Props) { - const { urlParams } = useLegacyUrlParams(); - const { core } = useApmPluginContext(); - const defaultComparisonEnabled = getComparisonEnabled({ - core, - urlComparisonEnabled: comparisonEnabled, - }); - const location = useLocation(); - - const hrefLegacy = getLegacyApmHref({ - basePath: core.http.basePath, - path: `/services/${encodeURIComponent(serviceName)}/transactions/view`, - query: { - traceId, - transactionId, - transactionName, - transactionType, - comparisonEnabled: defaultComparisonEnabled, - offset, - ...pickKeys(urlParams as APMQueryParams, ...persistedFilters), - ...pickBy({ latencyAggregationType, environment }, identity), - }, - search: location.search, - }); - +export function TransactionDetailLink({ transactionName, href, ...rest }: Props) { if (transactionName !== txGroupsDroppedBucketName) { return ( - } + content={} /> ); } diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/get_columns.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/get_columns.tsx index 0c71cd2703086..b2de9517967b4 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/get_columns.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/get_columns.tsx @@ -131,13 +131,7 @@ export function getColumns({ render: (_, { name, transactionType: type }) => { return ( Date: Mon, 7 Apr 2025 12:42:00 +0200 Subject: [PATCH 11/16] Fix types and scenario --- .../src/scenarios/simple_trace.ts | 3 +- .../trace_with_service_names_with_slashes.ts | 106 ++++++++++++++++++ .../top_erroneous_transactions/index.tsx | 4 - .../app/top_traces_overview/trace_list.tsx | 24 +++- .../charts/timeline/marker/error_marker.tsx | 16 +-- .../shared/time_comparison/index.test.tsx | 8 +- 6 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/trace_with_service_names_with_slashes.ts diff --git a/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/simple_trace.ts b/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/simple_trace.ts index 304778626c001..e98fcb32cf3e0 100644 --- a/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/simple_trace.ts +++ b/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/simple_trace.ts @@ -25,10 +25,9 @@ const scenario: Scenario = async (runOptions) => { const successfulTimestamps = range.interval('1m').rate(180); const failedTimestamps = range.interval('1m').rate(180); - // TODO Keep using service name that needs encoding or add new scenario const instances = [...Array(numServices).keys()].map((index) => apm - .service({ name: `synth/node-${index}`, environment: ENVIRONMENT, agentName: 'nodejs' }) + .service({ name: `synth-node-${index}`, environment: ENVIRONMENT, agentName: 'nodejs' }) .instance('instance') ); const instanceSpans = (instance: Instance) => { diff --git a/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/trace_with_service_names_with_slashes.ts b/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/trace_with_service_names_with_slashes.ts new file mode 100644 index 0000000000000..304778626c001 --- /dev/null +++ b/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/trace_with_service_names_with_slashes.ts @@ -0,0 +1,106 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { ApmFields, apm, Instance } from '@kbn/apm-synthtrace-client'; +import { Scenario } from '../cli/scenario'; +import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment'; +import { withClient } from '../lib/utils/with_client'; + +const ENVIRONMENT = getSynthtraceEnvironment(__filename); + +const scenario: Scenario = async (runOptions) => { + const { logger } = runOptions; + const { numServices = 3 } = runOptions.scenarioOpts || {}; + + return { + generate: ({ range, clients: { apmEsClient } }) => { + const transactionName = '240rpm/75% 1000ms'; + + const successfulTimestamps = range.interval('1m').rate(180); + const failedTimestamps = range.interval('1m').rate(180); + + // TODO Keep using service name that needs encoding or add new scenario + const instances = [...Array(numServices).keys()].map((index) => + apm + .service({ name: `synth/node-${index}`, environment: ENVIRONMENT, agentName: 'nodejs' }) + .instance('instance') + ); + const instanceSpans = (instance: Instance) => { + const successfulTraceEvents = successfulTimestamps.generator((timestamp) => + instance + .transaction({ transactionName }) + .timestamp(timestamp) + .defaults({ + 'url.domain': 'foo.bar', + }) + .duration(1000) + .success() + .children( + instance + .span({ + spanName: 'GET apm-*/_search', + spanType: 'db', + spanSubtype: 'elasticsearch', + }) + .duration(1000) + .success() + .destination('elasticsearch') + .timestamp(timestamp), + instance + .span({ spanName: 'custom_operation', spanType: 'custom' }) + .duration(100) + .success() + .timestamp(timestamp) + ) + ); + + const failedTraceEvents = failedTimestamps.generator((timestamp) => + instance + .transaction({ transactionName }) + .timestamp(timestamp) + .duration(1000) + .failure() + .errors( + instance + .error({ + message: '[ResponseError] index_not_found_exception', + type: 'ResponseError', + }) + .timestamp(timestamp + 50) + ) + ); + + const metricsets = range + .interval('30s') + .rate(1) + .generator((timestamp) => + instance + .appMetrics({ + 'system.memory.actual.free': 800, + 'system.memory.total': 1000, + 'system.cpu.total.norm.pct': 0.6, + 'system.process.cpu.total.norm.pct': 0.7, + }) + .timestamp(timestamp) + ); + + return [successfulTraceEvents, failedTraceEvents, metricsets]; + }; + + return withClient( + apmEsClient, + logger.perf('generating_apm_events', () => + instances.flatMap((instance) => instanceSpans(instance)) + ) + ); + }, + }; +}; + +export default scenario; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/top_erroneous_transactions/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/top_erroneous_transactions/index.tsx index e4b83129a040c..660249741b23b 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/top_erroneous_transactions/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/top_erroneous_transactions/index.tsx @@ -88,11 +88,7 @@ export function TopErroneousTransactions({ serviceName }: Props) { text={transactionName} content={ ['data']['items'][number]; export function getTraceListColumns({ query, + link, }: { query: TypeOf['query']; + link: ( + path: '/services/{serviceName}/transactions/view', + params: { + path: { serviceName: string }; + query: TypeOf['query']; + } + ) => string; }): Array> { return [ { @@ -64,9 +73,17 @@ export function getTraceListColumns({ render: (_: string, { serviceName, transactionName, transactionType }: TraceGroup) => ( {transactionName} @@ -166,8 +183,9 @@ export function TraceList({ response }: Props) { query, query: { rangeFrom, rangeTo }, } = useApmParams('/traces'); + const { link } = useApmRouter(); - const traceListColumns = useMemo(() => getTraceListColumns({ query }), [query]); + const traceListColumns = useMemo(() => getTraceListColumns({ query, link }), [query, link]); useEffect(() => { if (status === FETCH_STATUS.SUCCESS) { diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.tsx index bdc8c1ded4a29..28daae6ad275c 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.tsx @@ -8,9 +8,9 @@ import { EuiPopover, EuiText, useEuiTheme } from '@elastic/eui'; import React, { useState } from 'react'; import styled from '@emotion/styled'; +import { useAnyOfApmParams } from '../../../../../hooks/use_apm_params'; import { TRACE_ID, TRANSACTION_ID } from '../../../../../../common/es_fields/apm'; import { asDuration } from '../../../../../../common/utils/formatters'; -import { useLegacyUrlParams } from '../../../../../context/url_params_context/use_url_params'; import type { ErrorMark } from '../../../../app/transaction_details/waterfall_with_summary/waterfall_container/marks/get_error_marks'; import { ErrorDetailLink } from '../../../links/apm/error_detail_link'; import { Legend, Shape } from '../legend'; @@ -51,8 +51,11 @@ function truncateMessage(errorMessage?: string) { export function ErrorMarker({ mark }: Props) { const { euiTheme } = useEuiTheme(); - const { urlParams } = useLegacyUrlParams(); const [isPopoverOpen, showPopover] = useState(false); + const { query } = useAnyOfApmParams( + '/services/{serviceName}/overview', + '/services/{serviceName}/errors' + ); const togglePopover = () => showPopover(!isPopoverOpen); @@ -68,15 +71,12 @@ export function ErrorMarker({ mark }: Props) { const { error } = mark; - const { rangeTo, rangeFrom } = urlParams; - - const query = { + const queryParam = { + ...query, kuery: [ ...(error.trace?.id ? [`${TRACE_ID} : "${error.trace?.id}"`] : []), ...(error.transaction?.id ? [`${TRANSACTION_ID} : "${error.transaction?.id}"`] : []), ].join(' and '), - rangeFrom, - rangeTo, }; const errorMessage = error.error.log?.message || error.error.exception?.[0]?.message; @@ -106,7 +106,7 @@ export function ErrorMarker({ mark }: Props) { data-test-subj="errorLink" serviceName={error.service.name} errorGroupId={error.error.grouping_key} - query={query} + query={queryParam} title={errorMessage} > {truncatedErrorMessage} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/time_comparison/index.test.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/time_comparison/index.test.tsx index 2bfdd2530aaf9..331adc9c58eef 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/time_comparison/index.test.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/time_comparison/index.test.tsx @@ -99,8 +99,10 @@ describe('TimeComparison component', () => { ); jest.spyOn(useEnvironmentContextModule, 'useEnvironmentsContext').mockReturnValue({ - // @ts-ignore mocking only partial data preferredEnvironment: 'prod', + environment: 'prod', + environments: [], + status: FETCH_STATUS.SUCCESS, }); }; beforeAll(() => { @@ -152,8 +154,10 @@ describe('TimeComparison component', () => { it('shows enabled option for expected bounds when there are ML jobs available matching the preferred environment', () => { jest.spyOn(useEnvironmentContextModule, 'useEnvironmentsContext').mockReturnValueOnce({ - // @ts-ignore mocking only partial data preferredEnvironment: 'prod', + environment: 'prod', + environments: [], + status: FETCH_STATUS.SUCCESS, }); const Wrapper = getWrapper({ From 981b52acfbb53dd37213748832fe82ca56a74d0a Mon Sep 17 00:00:00 2001 From: Jenny Date: Mon, 7 Apr 2025 19:20:18 +0200 Subject: [PATCH 12/16] Fix test and cleaunp --- .../scenarios/trace_with_service_names_with_slashes.ts | 1 - .../charts/timeline/marker/error_marker.test.tsx | 10 ++++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/trace_with_service_names_with_slashes.ts b/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/trace_with_service_names_with_slashes.ts index 304778626c001..b62bd2fd77336 100644 --- a/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/trace_with_service_names_with_slashes.ts +++ b/src/platform/packages/shared/kbn-apm-synthtrace/src/scenarios/trace_with_service_names_with_slashes.ts @@ -25,7 +25,6 @@ const scenario: Scenario = async (runOptions) => { const successfulTimestamps = range.interval('1m').rate(180); const failedTimestamps = range.interval('1m').rate(180); - // TODO Keep using service name that needs encoding or add new scenario const instances = [...Array(numServices).keys()].map((index) => apm .service({ name: `synth/node-${index}`, environment: ENVIRONMENT, agentName: 'nodejs' }) diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.test.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.test.tsx index 58e0ab63fedb3..871a431940711 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.test.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.test.tsx @@ -16,7 +16,11 @@ import { ErrorMarker } from './error_marker'; function Wrapper({ children }: { children?: ReactNode }) { return ( - + {children} ); @@ -54,7 +58,9 @@ describe('ErrorMarker', () => { return component; } function getKueryDecoded(url: string) { - return decodeURIComponent(url.substring(url.indexOf('kuery='), url.indexOf('&'))); + return decodeURIComponent( + url.substring(url.indexOf('kuery='), url.indexOf('&latencyAggregationType')) + ); } it('renders link with trace and transaction', () => { const component = openPopover(mark); From 6305f2cee9b2c11c4a91279f96770557aee47f7b Mon Sep 17 00:00:00 2001 From: Jenny Date: Thu, 10 Apr 2025 12:10:31 +0200 Subject: [PATCH 13/16] Fix comparison enabled value to fallback to the default --- .../agent_instances_details/index.tsx | 13 +++++++++++-- .../shared/transactions_table/get_columns.tsx | 5 ++--- .../components/shared/transactions_table/index.tsx | 12 +++++++++--- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_explorer/agent_instances/agent_instances_details/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_explorer/agent_instances/agent_instances_details/index.tsx index 8de982b34bb05..e3166465331b4 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_explorer/agent_instances/agent_instances_details/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_explorer/agent_instances/agent_instances_details/index.tsx @@ -12,6 +12,8 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; import type { ValuesType } from 'utility-types'; import type { TypeOf } from '@kbn/typed-react-router-config'; +import { getComparisonEnabled } from '../../../../../shared/time_comparison/get_comparison_enabled'; +import { useApmPluginContext } from '../../../../../../context/apm_plugin/use_apm_plugin_context'; import { ENVIRONMENT_NOT_DEFINED } from '../../../../../../../common/environment_filter_values'; import { useAnyOfApmParams } from '../../../../../../hooks/use_apm_params'; import { MetricOverviewLink } from '../../../../../shared/links/apm/metric_overview_link'; @@ -69,7 +71,7 @@ export function getInstanceColumns( } )} > - +

; @@ -73,6 +74,7 @@ export function TransactionsTable({ showSparkPlots, }: Props) { const { link } = useApmRouter(); + const { core, observabilityAIAssistant } = useApmPluginContext(); const [renderedItemIndices, setRenderedItemIndices] = useState([0, 0]); const { @@ -88,6 +90,11 @@ export function TransactionsTable({ const latencyAggregationType = getLatencyAggregationType(latencyAggregationTypeFromQuery); + const defaultComparisonEnabled = getComparisonEnabled({ + core, + urlComparisonEnabled: comparisonEnabled, + }); + const { isLarge } = useBreakpoints(); const shouldShowSparkPlots = showSparkPlots ?? !isLarge; const { transactionType, serviceName } = useApmServiceContext(); @@ -120,7 +127,7 @@ export function TransactionsTable({ latencyAggregationType: latencyAggregationType as LatencyAggregationType, detailedStatisticsLoading: isPending(detailedStatisticsStatus), detailedStatistics, - comparisonEnabled, + comparisonEnabled: defaultComparisonEnabled, shouldShowSparkPlots, offset, transactionOverflowCount: mainStatistics.transactionOverflowCount, @@ -129,7 +136,7 @@ export function TransactionsTable({ query, }); }, [ - comparisonEnabled, + defaultComparisonEnabled, detailedStatistics, detailedStatisticsStatus, latencyAggregationType, @@ -142,7 +149,6 @@ export function TransactionsTable({ shouldShowSparkPlots, ]); - const { core, observabilityAIAssistant } = useApmPluginContext(); const setScreenContext = observabilityAIAssistant?.service.setScreenContext; const isTableSearchBarEnabled = core.uiSettings.get(apmEnableTableSearchBar, true); From f89f011bfc660a4216a39f61b3fc7fee3b87af2b Mon Sep 17 00:00:00 2001 From: Jenny Date: Tue, 15 Apr 2025 15:06:15 +0200 Subject: [PATCH 14/16] CR changes --- .../agent_instances_details/index.tsx | 27 ++++++++++++------- .../waterfall/flyout_top_level_properties.tsx | 2 +- .../span_flyout/sticky_span_properties.tsx | 2 +- .../shared/links/apm/apm_link_hooks.ts | 2 +- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_explorer/agent_instances/agent_instances_details/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_explorer/agent_instances/agent_instances_details/index.tsx index e3166465331b4..8e066e55c7a4d 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_explorer/agent_instances/agent_instances_details/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/settings/agent_explorer/agent_instances/agent_instances_details/index.tsx @@ -44,12 +44,19 @@ enum AgentExplorerInstanceFieldName { LastReport = 'lastReport', } -export function getInstanceColumns( - serviceName: string, - agentName: AgentName, - query: Omit['query'], 'kuery'>, - agentDocsPageUrl?: string -): Array> { +interface GetInstanceColumnsProps { + serviceName: string; + agentName: AgentName; + query: Omit['query'], 'kuery'>; + agentDocsPageUrl?: string; +} + +export function getInstanceColumns({ + serviceName, + agentName, + query, + agentDocsPageUrl, +}: GetInstanceColumnsProps): Array> { return [ { field: AgentExplorerInstanceFieldName.InstanceName, @@ -200,10 +207,10 @@ export function AgentInstancesDetails({ <> ; From a0b0aecffbdaae2352611688c285b7b6c0b89400 Mon Sep 17 00:00:00 2001 From: Jenny Date: Wed, 16 Apr 2025 14:12:09 +0200 Subject: [PATCH 15/16] Merge fixes --- .../otel_service_overview_and_transactions.cy.ts | 1 - .../components/shared/charts/timeline/marker/error_marker.tsx | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/solutions/observability/plugins/apm/ftr_e2e/cypress/e2e/service_overview/otel_service_overview_and_transactions.cy.ts b/x-pack/solutions/observability/plugins/apm/ftr_e2e/cypress/e2e/service_overview/otel_service_overview_and_transactions.cy.ts index dde47a70d77f6..add3332d5c62e 100644 --- a/x-pack/solutions/observability/plugins/apm/ftr_e2e/cypress/e2e/service_overview/otel_service_overview_and_transactions.cy.ts +++ b/x-pack/solutions/observability/plugins/apm/ftr_e2e/cypress/e2e/service_overview/otel_service_overview_and_transactions.cy.ts @@ -159,7 +159,6 @@ describe('Service Overview', () => { cy.getByTestSubj('apmHttpInfoRequestMethod').contains('GET'); cy.getByTestSubj('apmHttpInfoUrl').should('exist'); cy.getByTestSubj('apmHttpInfoUrl').contains('https://elastic.co/'); - cy.getByTestSubj('apmHttpInfoRequestMethod').should('exist'); cy.getByTestSubj('apmHttpStatusBadge').should('exist'); cy.getByTestSubj('apmHttpStatusBadge').contains('OK'); }); diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.tsx index 28daae6ad275c..24d91265472b1 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.tsx @@ -54,7 +54,8 @@ export function ErrorMarker({ mark }: Props) { const [isPopoverOpen, showPopover] = useState(false); const { query } = useAnyOfApmParams( '/services/{serviceName}/overview', - '/services/{serviceName}/errors' + '/services/{serviceName}/errors', + '/services/{serviceName}/transactions/view' ); const togglePopover = () => showPopover(!isPopoverOpen); From 732adc6666c9e55dde4dd6d8c4267b958419978f Mon Sep 17 00:00:00 2001 From: Jenny Date: Wed, 16 Apr 2025 14:37:29 +0200 Subject: [PATCH 16/16] Fix nav error --- .../shared/charts/timeline/marker/error_marker.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.tsx index 24d91265472b1..bedd09082f443 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.tsx @@ -55,7 +55,8 @@ export function ErrorMarker({ mark }: Props) { const { query } = useAnyOfApmParams( '/services/{serviceName}/overview', '/services/{serviceName}/errors', - '/services/{serviceName}/transactions/view' + '/services/{serviceName}/transactions/view', + '/traces/explorer/waterfall' ); const togglePopover = () => showPopover(!isPopoverOpen); @@ -71,9 +72,11 @@ export function ErrorMarker({ mark }: Props) { ); const { error } = mark; + const serviceGroup = 'serviceGroup' in query ? query.serviceGroup : ''; const queryParam = { ...query, + serviceGroup, kuery: [ ...(error.trace?.id ? [`${TRACE_ID} : "${error.trace?.id}"`] : []), ...(error.transaction?.id ? [`${TRANSACTION_ID} : "${error.transaction?.id}"`] : []),