[APM] Rename backends to dependencies in URLs and APIs (#136330)

* renaming api endpoints

* renaming route functions

* GET /internal/apm/dependencies/top_dependencies

* GET /internal/apm/dependencies/upstream_services

* server changes

* renaming route params

* refactoring

* refactoring client

* refactogin client

* refactoring routes

* fixing redirection

* refactoring component

* renaming nodetype

* renaming backend link

* backend template

* renaming backends operations

* dependency operation

* DependencyMetricCharts

* DependencyMetricCharts

* DependencyOperationDetailView

* DependencyDetailOverview

* remaining

* updating tests and i18n
This commit is contained in:
Cauê Marcondes 2022-07-18 11:28:54 -04:00 committed by GitHub
parent aea46fd372
commit 031729e81a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
75 changed files with 1060 additions and 998 deletions

View file

@ -9,7 +9,7 @@ import { Coordinate } from '../typings/timeseries';
export enum NodeType {
service = 'service',
backend = 'backend',
dependency = 'dependency',
}
interface NodeBase {
@ -23,14 +23,14 @@ export interface ServiceNode extends NodeBase {
environment: string;
}
export interface BackendNode extends NodeBase {
type: NodeType.backend;
backendName: string;
export interface DependencyNode extends NodeBase {
type: NodeType.dependency;
dependencyName: string;
spanType: string;
spanSubtype: string;
}
export type Node = ServiceNode | BackendNode;
export type Node = ServiceNode | DependencyNode;
export interface ConnectionStatsItem {
location: Node;
@ -67,5 +67,7 @@ export interface ConnectionStatsItemWithComparisonData {
}
export function getNodeName(node: Node) {
return node.type === NodeType.service ? node.serviceName : node.backendName;
return node.type === NodeType.service
? node.serviceName
: node.dependencyName;
}

View file

@ -21,17 +21,17 @@ export const kueryBarPlaceholder = i18n.translate(
);
export const getKueryBarBoolFilter = ({
backendName,
dependencyName,
environment,
}: {
backendName?: string;
dependencyName?: string;
environment: string;
}) => {
return [
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.metric } },
{ exists: { field: SPAN_DESTINATION_SERVICE_RESOURCE } },
...(backendName
? [{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: backendName } }]
...(dependencyName
? [{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: dependencyName } }]
: []),
...environmentQuery(environment),
];

View file

@ -49,7 +49,7 @@ describe('Comparison feature flag', () => {
});
it('shows the comparison feature enabled in services overview', () => {
cy.visit('/app/apm/backends');
cy.visit('/app/apm/dependencies');
cy.get('input[type="checkbox"]#comparison').should('be.checked');
cy.get('[data-test-subj="comparisonSelect"]').should('not.be.disabled');
});
@ -89,7 +89,7 @@ describe('Comparison feature flag', () => {
});
it('shows the comparison feature disabled in dependencies overview page', () => {
cy.visit('/app/apm/backends');
cy.visit('/app/apm/dependencies');
cy.get('input[type="checkbox"]#comparison').should('not.be.checked');
cy.get('[data-test-subj="comparisonSelect"]').should('be.disabled');
});

View file

@ -60,9 +60,9 @@ describe('Dependencies', () => {
describe.skip('dependency overview page', () => {
it('shows dependency information and you can navigate to a page for an upstream service', () => {
cy.visit(
`/app/apm/backends/overview?${new URLSearchParams({
`/app/apm/dependencies/overview?${new URLSearchParams({
...timeRange,
backendName: 'postgresql',
dependencyName: 'postgresql',
})}`
);
@ -77,9 +77,9 @@ describe('Dependencies', () => {
it('has no detectable a11y violations on load', () => {
cy.visit(
`/app/apm/backends/overview?${new URLSearchParams({
`/app/apm/dependencies/overview?${new URLSearchParams({
...timeRange,
backendName: 'postgresql',
dependencyName: 'postgresql',
})}`
);
cy.contains('h1', 'postgresql');

View file

@ -1,16 +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 React from 'react';
import { useBackendDetailOperationsBreadcrumb } from '../../../hooks/use_backend_detail_operations_breadcrumb';
import { BackendDetailOperationsList } from './backend_detail_operations_list';
export function BackendDetailOperations() {
useBackendDetailOperationsBreadcrumb();
return <BackendDetailOperationsList />;
}

View file

@ -14,10 +14,10 @@ import { getNodeName, NodeType } from '../../../../../common/connections';
import { useApmParams } from '../../../../hooks/use_apm_params';
import { useFetcher } from '../../../../hooks/use_fetcher';
import { useTimeRange } from '../../../../hooks/use_time_range';
import { BackendLink } from '../../../shared/backend_link';
import { DependencyLink } from '../../../shared/dependency_link';
import { DependenciesTable } from '../../../shared/dependencies_table';
export function BackendInventoryDependenciesTable() {
export function DependenciesInventoryTable() {
const {
query: {
rangeFrom,
@ -27,7 +27,7 @@ export function BackendInventoryDependenciesTable() {
comparisonEnabled,
offset,
},
} = useApmParams('/backends/inventory');
} = useApmParams('/dependencies/inventory');
const { start, end } = useTimeRange({ rangeFrom, rangeTo });
@ -39,7 +39,7 @@ export function BackendInventoryDependenciesTable() {
return;
}
return callApmApi('GET /internal/apm/backends/top_backends', {
return callApmApi('GET /internal/apm/dependencies/top_dependencies', {
params: {
query: {
start,
@ -59,19 +59,19 @@ export function BackendInventoryDependenciesTable() {
);
const dependencies =
data?.backends.map((dependency) => {
data?.dependencies.map((dependency) => {
const { location } = dependency;
const name = getNodeName(location);
if (location.type !== NodeType.backend) {
throw new Error('Expected a backend node');
if (location.type !== NodeType.dependency) {
throw new Error('Expected a dependency node');
}
const link = (
<BackendLink
<DependencyLink
type={location.spanType}
subtype={location.spanSubtype}
query={{
backendName: location.backendName,
dependencyName: location.dependencyName,
comparisonEnabled,
offset,
environment,
@ -83,7 +83,7 @@ export function BackendInventoryDependenciesTable() {
trackEvent({
app: 'apm',
metricType: METRIC_TYPE.CLICK,
metric: 'backend_inventory_to_backend_detail',
metric: 'dependencies_inventory_to_dependency_detail',
});
}}
/>
@ -102,7 +102,7 @@ export function BackendInventoryDependenciesTable() {
dependencies={dependencies}
title={null}
nameColumnTitle={i18n.translate(
'xpack.apm.backendInventory.dependencyTableColumn',
'xpack.apm.dependenciesInventory.dependencyTableColumn',
{
defaultMessage: 'Dependency',
}

View file

@ -10,15 +10,15 @@ import React from 'react';
import {
getKueryBarBoolFilter,
kueryBarPlaceholder,
} from '../../../../common/backends';
} from '../../../../common/dependencies';
import { useApmParams } from '../../../hooks/use_apm_params';
import { SearchBar } from '../../shared/search_bar';
import { BackendInventoryDependenciesTable } from './backend_inventory_dependencies_table';
import { DependenciesInventoryTable } from './dependencies_inventory_table';
export function BackendInventory() {
export function DependenciesInventory() {
const {
query: { environment },
} = useApmParams('/backends/inventory');
} = useApmParams('/dependencies/inventory');
const kueryBarBoolFilter = getKueryBarBoolFilter({
environment,
});
@ -31,7 +31,7 @@ export function BackendInventory() {
kueryBarBoolFilter={kueryBarBoolFilter}
/>
<EuiSpacer size="s" />
<BackendInventoryDependenciesTable />
<DependenciesInventoryTable />
</>
);
}

View file

@ -21,36 +21,36 @@ import { EmptyMessage } from '../../../shared/empty_message';
import { ITableColumn, ManagedTable } from '../../../shared/managed_table';
import { getComparisonEnabled } from '../../../shared/time_comparison/get_comparison_enabled';
import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip';
import { BackendOperationDetailLink } from '../../backend_operation_detail_view/backend_operation_detail_link';
import { DependencyOperationDetailLink } from '../../dependency_operation_detail_view/dependency_operation_detail_link';
interface OperationStatisticsItem extends SpanMetricGroup {
spanName: string;
}
function OperationLink({ spanName }: { spanName: string }) {
const { query } = useApmParams('/backends/operations');
const { query } = useApmParams('/dependencies/operations');
return (
<TruncateWithTooltip
data-test-subj="apmOperationsListAppLink"
text={spanName}
content={<BackendOperationDetailLink {...query} spanName={spanName} />}
content={<DependencyOperationDetailLink {...query} spanName={spanName} />}
/>
);
}
export function BackendDetailOperationsList() {
export function DependencyDetailOperationsList() {
const {
query: {
rangeFrom,
rangeTo,
backendName,
dependencyName,
environment,
kuery,
comparisonEnabled: urlComparisonEnabled,
offset,
},
} = useApmParams('/backends/operations');
} = useApmParams('/dependencies/operations');
const { core } = useApmPluginContext();
@ -68,10 +68,10 @@ export function BackendDetailOperationsList() {
const primaryStatsFetch = useFetcher(
(callApmApi) => {
return callApmApi('GET /internal/apm/backends/operations', {
return callApmApi('GET /internal/apm/dependencies/operations', {
params: {
query: {
backendName,
dependencyName,
start,
end,
environment,
@ -80,7 +80,7 @@ export function BackendDetailOperationsList() {
},
});
},
[backendName, start, end, environment, kuery]
[dependencyName, start, end, environment, kuery]
);
const comparisonStatsFetch = useFetcher(
@ -90,10 +90,10 @@ export function BackendDetailOperationsList() {
operations: [],
});
}
return callApmApi('GET /internal/apm/backends/operations', {
return callApmApi('GET /internal/apm/dependencies/operations', {
params: {
query: {
backendName,
dependencyName,
start,
end,
offset,
@ -103,16 +103,14 @@ export function BackendDetailOperationsList() {
},
});
},
[backendName, start, end, offset, environment, kuery, comparisonEnabled]
[dependencyName, start, end, offset, environment, kuery, comparisonEnabled]
);
const columns: Array<ITableColumn<OperationStatisticsItem>> = [
{
name: i18n.translate(
'xpack.apm.backendDetailOperationsList.spanNameColumnLabel',
{
defaultMessage: 'Span name',
}
'xpack.apm.dependencyDetailOperationsList.spanNameColumnLabel',
{ defaultMessage: 'Span name' }
),
field: 'spanName',
sortable: true,
@ -132,10 +130,8 @@ export function BackendDetailOperationsList() {
const noItemsMessage = (
<EmptyMessage
heading={i18n.translate(
'xpack.apm.backendDetailOperationsList.notFoundLabel',
{
defaultMessage: 'No operations found',
}
'xpack.apm.dependencyDetailOperationsList.notFoundLabel',
{ defaultMessage: 'No operations found' }
)}
/>
);

View file

@ -0,0 +1,16 @@
/*
* 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 React from 'react';
import { useDependencyDetailOperationsBreadcrumb } from '../../../hooks/use_dependency_detail_operations_breadcrumb';
import { DependencyDetailOperationsList } from './dependency_detail_operations_list';
export function DependencyDetailOperations() {
useDependencyDetailOperationsBreadcrumb();
return <DependencyDetailOperationsList />;
}

View file

@ -17,10 +17,10 @@ import { useTimeRange } from '../../../hooks/use_time_range';
import { getComparisonEnabled } from '../../shared/time_comparison/get_comparison_enabled';
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
export function BackendDetailDependenciesTable() {
export function DependenciesDetailTable() {
const {
query: {
backendName,
dependencyName,
rangeFrom,
rangeTo,
kuery,
@ -28,7 +28,7 @@ export function BackendDetailDependenciesTable() {
comparisonEnabled: urlComparisonEnabled,
offset,
},
} = useApmParams('/backends/overview');
} = useApmParams('/dependencies/overview');
const { core } = useApmPluginContext();
@ -41,10 +41,10 @@ export function BackendDetailDependenciesTable() {
const { data, status } = useFetcher(
(callApmApi) => {
return callApmApi('GET /internal/apm/backends/upstream_services', {
return callApmApi('GET /internal/apm/dependencies/upstream_services', {
params: {
query: {
backendName,
dependencyName,
start,
end,
environment,
@ -58,7 +58,7 @@ export function BackendDetailDependenciesTable() {
},
});
},
[start, end, environment, offset, backendName, kuery, comparisonEnabled]
[start, end, environment, offset, dependencyName, kuery, comparisonEnabled]
);
const dependencies =
@ -97,14 +97,13 @@ export function BackendDetailDependenciesTable() {
return (
<DependenciesTable
dependencies={dependencies}
title={i18n.translate('xpack.apm.backendDetail.dependenciesTableTitle', {
defaultMessage: 'Upstream services',
})}
title={i18n.translate(
'xpack.apm.dependencyDetail.dependenciesTableTitle',
{ defaultMessage: 'Upstream services' }
)}
nameColumnTitle={i18n.translate(
'xpack.apm.backendDetail.dependenciesTableColumnBackend',
{
defaultMessage: 'Service',
}
'xpack.apm.dependencyDetail.dependenciesTableColumn',
{ defaultMessage: 'Service' }
)}
status={status}
compact={false}

View file

@ -11,13 +11,13 @@ import { useBreadcrumb } from '../../../context/breadcrumbs/use_breadcrumb';
import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context';
import { useApmParams } from '../../../hooks/use_apm_params';
import { useApmRouter } from '../../../hooks/use_apm_router';
import { BackendDetailDependenciesTable } from './backend_detail_dependencies_table';
import { BackendMetricCharts } from '../../shared/backend_metric_charts';
import { DependenciesDetailTable } from './dependencies_detail_table';
import { DependencyMetricCharts } from '../../shared/dependency_metric_charts';
export function BackendDetailOverview() {
export function DependencyDetailOverview() {
const {
query: {
backendName,
dependencyName,
rangeFrom,
rangeTo,
refreshInterval,
@ -26,18 +26,19 @@ export function BackendDetailOverview() {
kuery,
comparisonEnabled,
},
} = useApmParams('/backends/overview');
} = useApmParams('/dependencies/overview');
const apmRouter = useApmRouter();
useBreadcrumb([
{
title: i18n.translate('xpack.apm.backendDetailOverview.breadcrumbTitle', {
defaultMessage: 'Overview',
}),
href: apmRouter.link('/backends/overview', {
title: i18n.translate(
'xpack.apm.dependencyDetailOverview.breadcrumbTitle',
{ defaultMessage: 'Overview' }
),
href: apmRouter.link('/dependencies/overview', {
query: {
backendName,
dependencyName,
rangeFrom,
rangeTo,
refreshInterval,
@ -53,10 +54,10 @@ export function BackendDetailOverview() {
return (
<>
<ChartPointerEventContextProvider>
<BackendMetricCharts />
<DependencyMetricCharts />
</ChartPointerEventContextProvider>
<EuiSpacer size="l" />
<BackendDetailDependenciesTable />
<DependenciesDetailTable />
</>
);
}

View file

@ -8,17 +8,17 @@ import React from 'react';
import { useBreadcrumb } from '../../../context/breadcrumbs/use_breadcrumb';
import { useApmParams } from '../../../hooks/use_apm_params';
import { useApmRouter } from '../../../hooks/use_apm_router';
import { DependenciesInventoryTitle } from '../../routing/home';
import { BackendDetailTemplate } from '../../routing/templates/backend_detail_template';
import { DependenciesInventoryTitle } from '../../routing/home/dependencies';
import { DependencyDetailTemplate } from '../../routing/templates/dependency_detail_template';
export function BackendDetailView({
export function DependencyDetailView({
children,
}: {
children: React.ReactChild;
}) {
const {
query: {
backendName,
dependencyName,
rangeFrom,
rangeTo,
refreshInterval,
@ -27,14 +27,14 @@ export function BackendDetailView({
kuery,
comparisonEnabled,
},
} = useApmParams('/backends');
} = useApmParams('/dependencies');
const apmRouter = useApmRouter();
useBreadcrumb([
{
title: DependenciesInventoryTitle,
href: apmRouter.link('/backends/inventory', {
href: apmRouter.link('/dependencies/inventory', {
query: {
rangeFrom,
rangeTo,
@ -47,10 +47,10 @@ export function BackendDetailView({
}),
},
{
title: backendName,
href: apmRouter.link('/backends', {
title: dependencyName,
href: apmRouter.link('/dependencies', {
query: {
backendName,
dependencyName,
rangeFrom,
rangeTo,
refreshInterval,
@ -62,6 +62,5 @@ export function BackendDetailView({
}),
},
]);
return <BackendDetailTemplate>{children}</BackendDetailTemplate>;
return <DependencyDetailTemplate>{children}</DependencyDetailTemplate>;
}

View file

@ -10,14 +10,14 @@ import { TypeOf } from '@kbn/typed-react-router-config';
import { useApmRouter } from '../../../hooks/use_apm_router';
import { ApmRoutes } from '../../routing/apm_route_config';
type Query = TypeOf<ApmRoutes, '/backends/operation'>['query'];
type Query = TypeOf<ApmRoutes, '/dependencies/operation'>['query'];
export function BackendOperationDetailLink(query: Query) {
export function DependencyOperationDetailLink(query: Query) {
const router = useApmRouter();
const { spanName } = query;
const link = router.link('/backends/operation', {
const link = router.link('/dependencies/operation', {
query,
});

View file

@ -28,18 +28,18 @@ import { ITableColumn, ManagedTable } from '../../shared/managed_table';
import { ServiceLink } from '../../shared/service_link';
import { TimestampTooltip } from '../../shared/timestamp_tooltip';
type BackendSpan = ValuesType<
APIReturnType<'GET /internal/apm/backends/operations/spans'>['spans']
type DependencySpan = ValuesType<
APIReturnType<'GET /internal/apm/dependencies/operations/spans'>['spans']
>;
export function BackendOperationDetailTraceList() {
export function DependencyOperationDetailTraceList() {
const router = useApmRouter();
const theme = useTheme();
const {
query: {
backendName,
dependencyName,
spanName,
comparisonEnabled,
environment,
@ -52,7 +52,7 @@ export function BackendOperationDetailTraceList() {
sampleRangeFrom,
sampleRangeTo,
},
} = useApmParams('/backends/operation');
} = useApmParams('/dependencies/operation');
function getTraceLink({
transactionName,
@ -101,10 +101,10 @@ export function BackendOperationDetailTraceList() {
const { start, end } = useTimeRange({ rangeFrom, rangeTo });
const columns: Array<ITableColumn<BackendSpan>> = [
const columns: Array<ITableColumn<DependencySpan>> = [
{
name: i18n.translate(
'xpack.apm.backendOperationDetailTraceListOutcomeColumn',
'xpack.apm.dependencyOperationDetailTraceListOutcomeColumn',
{ defaultMessage: 'Outcome' }
),
field: 'outcome',
@ -123,7 +123,7 @@ export function BackendOperationDetailTraceList() {
},
{
name: i18n.translate(
'xpack.apm.backendOperationDetailTraceListTraceIdColumn',
'xpack.apm.dependencyOperationDetailTraceListTraceIdColumn',
{ defaultMessage: 'Trace' }
),
field: 'traceId',
@ -154,7 +154,7 @@ export function BackendOperationDetailTraceList() {
},
{
name: i18n.translate(
'xpack.apm.backendOperationDetailTraceListServiceNameColumn',
'xpack.apm.dependencyOperationDetailTraceListServiceNameColumn',
{ defaultMessage: 'Originating service' }
),
field: 'serviceName',
@ -183,7 +183,7 @@ export function BackendOperationDetailTraceList() {
},
{
name: i18n.translate(
'xpack.apm.backendOperationDetailTraceListTransactionNameColumn',
'xpack.apm.dependencyOperationDetailTraceListTransactionNameColumn',
{ defaultMessage: 'Transaction name' }
),
field: 'transactionName',
@ -211,7 +211,7 @@ export function BackendOperationDetailTraceList() {
},
{
name: i18n.translate(
'xpack.apm.backendOperationDetailTraceListDurationColumn',
'xpack.apm.dependencyOperationDetailTraceListDurationColumn',
{ defaultMessage: 'Duration' }
),
field: 'duration',
@ -223,7 +223,7 @@ export function BackendOperationDetailTraceList() {
},
{
name: i18n.translate(
'xpack.apm.backendOperationDetailTraceListTimestampColumn',
'xpack.apm.dependencyOperationDetailTraceListTimestampColumn',
{ defaultMessage: 'Timestamp' }
),
field: '@timestamp',
@ -237,10 +237,10 @@ export function BackendOperationDetailTraceList() {
const { data = { spans: [] }, status } = useFetcher(
(callApmApi) => {
return callApmApi('GET /internal/apm/backends/operations/spans', {
return callApmApi('GET /internal/apm/dependencies/operations/spans', {
params: {
query: {
backendName,
dependencyName,
spanName,
start,
end,
@ -253,7 +253,7 @@ export function BackendOperationDetailTraceList() {
});
},
[
backendName,
dependencyName,
spanName,
start,
end,
@ -269,7 +269,7 @@ export function BackendOperationDetailTraceList() {
<EuiFlexItem>
<EuiTitle size="xxs">
<EuiText>
{i18n.translate('xpack.apm.backendOperationDetailTraceList', {
{i18n.translate('xpack.apm.dependencyOperationDetailTraceList', {
defaultMessage: 'Traces',
})}
</EuiText>

View file

@ -16,18 +16,18 @@ import { useTimeRange } from '../../../hooks/use_time_range';
import { DurationDistributionChartData } from '../../shared/charts/duration_distribution_chart';
import { DurationDistributionChartWithScrubber } from '../../shared/charts/duration_distribution_chart_with_scrubber';
export function BackendOperationDistributionChart() {
export function DependencyOperationDistributionChart() {
const { clearChartSelection, selectSampleFromChartSelection } =
useSampleChartSelection();
// there is no "current" event in the backend operation detail view
// there is no "current" event in the dependency operation detail view
const markerCurrentEvent = undefined;
const euiTheme = useTheme();
const {
query: {
backendName,
dependencyName,
spanName,
environment,
kuery,
@ -36,7 +36,7 @@ export function BackendOperationDistributionChart() {
sampleRangeFrom = 0,
sampleRangeTo = 0,
},
} = useApmParams('/backends/operation');
} = useApmParams('/dependencies/operation');
const selection: [number, number] | undefined =
sampleRangeFrom >= 0 && sampleRangeTo > 0
@ -47,11 +47,11 @@ export function BackendOperationDistributionChart() {
const { status, data } = useFetcher(
(callApmApi) => {
return callApmApi('GET /internal/apm/backends/charts/distribution', {
return callApmApi('GET /internal/apm/dependencies/charts/distribution', {
params: {
query: {
percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD,
backendName,
dependencyName,
spanName,
environment,
kuery,
@ -61,7 +61,7 @@ export function BackendOperationDistributionChart() {
},
});
},
[backendName, spanName, environment, kuery, start, end]
[dependencyName, spanName, environment, kuery, start, end]
);
const hasData =
@ -73,20 +73,16 @@ export function BackendOperationDistributionChart() {
areaSeriesColor: euiTheme.eui.euiColorVis1,
histogram: data?.allSpansDistribution.overallHistogram ?? [],
id: i18n.translate(
'xpack.apm.backendOperationDistributionChart.allSpansLegendLabel',
{
defaultMessage: 'All spans',
}
'xpack.apm.dependencyOperationDistributionChart.allSpansLegendLabel',
{ defaultMessage: 'All spans' }
),
},
{
areaSeriesColor: euiTheme.eui.euiColorVis7,
histogram: data?.failedSpansDistribution?.overallHistogram ?? [],
id: i18n.translate(
'xpack.apm.backendOperationDistributionChart.failedSpansLegendLabel',
{
defaultMessage: 'Failed spans',
}
'xpack.apm.dependencyOperationDistributionChart.failedSpansLegendLabel',
{ defaultMessage: 'Failed spans' }
),
},
];

View file

@ -10,47 +10,47 @@ import React from 'react';
import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context';
import { useApmParams } from '../../../hooks/use_apm_params';
import { useApmRouter } from '../../../hooks/use_apm_router';
import { useBackendDetailOperationsBreadcrumb } from '../../../hooks/use_backend_detail_operations_breadcrumb';
import { BackendMetricCharts } from '../../shared/backend_metric_charts';
import { useDependencyDetailOperationsBreadcrumb } from '../../../hooks/use_dependency_detail_operations_breadcrumb';
import { DependencyMetricCharts } from '../../shared/dependency_metric_charts';
import { DetailViewHeader } from '../../shared/detail_view_header';
import { BackendOperationDistributionChart } from './backend_operation_distribution_chart';
import { BackendOperationDetailTraceList } from './backend_operation_detail_trace_list';
import { DependencyOperationDistributionChart } from './dependendecy_operation_distribution_chart';
import { DependencyOperationDetailTraceList } from './dependency_operation_detail_trace_list';
export function BackendOperationDetailView() {
export function DependencyOperationDetailView() {
const router = useApmRouter();
const {
query: { spanName, ...query },
} = useApmParams('/backends/operation');
} = useApmParams('/dependencies/operation');
useBackendDetailOperationsBreadcrumb();
useDependencyDetailOperationsBreadcrumb();
return (
<EuiFlexGroup direction="column">
<EuiFlexItem>
<DetailViewHeader
backLabel={i18n.translate(
'xpack.apm.backendOperationDetailView.header.backLinkLabel',
'xpack.apm.dependecyOperationDetailView.header.backLinkLabel',
{ defaultMessage: 'All operations' }
)}
backHref={router.link('/backends/operations', { query })}
backHref={router.link('/dependencies/operations', { query })}
title={spanName}
/>
</EuiFlexItem>
<EuiSpacer size="s" />
<EuiFlexItem>
<ChartPointerEventContextProvider>
<BackendMetricCharts />
<DependencyMetricCharts />
</ChartPointerEventContextProvider>
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel hasBorder>
<BackendOperationDistributionChart />
<DependencyOperationDistributionChart />
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel hasBorder>
<BackendOperationDetailTraceList />
<DependencyOperationDetailTraceList />
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -21,14 +21,15 @@ import { ApmRoutes } from '../../../routing/apm_route_config';
import { StatsList } from './stats_list';
import { APIReturnType } from '../../../../services/rest/create_call_apm_api';
type BackendReturn = APIReturnType<'GET /internal/apm/service-map/backend'>;
type DependencyReturn =
APIReturnType<'GET /internal/apm/service-map/dependency'>;
const INITIAL_STATE: Partial<BackendReturn> = {
const INITIAL_STATE: Partial<DependencyReturn> = {
currentPeriod: undefined,
previousPeriod: undefined,
};
export function BackendContents({
export function DependencyContents({
elementData,
environment,
start,
@ -45,15 +46,15 @@ export function BackendContents({
const apmRouter = useApmRouter();
const backendName = nodeData.label;
const dependencyName = nodeData.label;
const { data = INITIAL_STATE, status } = useFetcher(
(callApmApi) => {
if (backendName) {
return callApmApi('GET /internal/apm/service-map/backend', {
if (dependencyName) {
return callApmApi('GET /internal/apm/service-map/dependency', {
params: {
query: {
backendName,
dependencyName,
environment,
start,
end,
@ -66,16 +67,16 @@ export function BackendContents({
});
}
},
[environment, backendName, start, end, offset, comparisonEnabled]
[environment, dependencyName, start, end, offset, comparisonEnabled]
);
const isLoading = status === FETCH_STATUS.LOADING;
const detailsUrl = backendName
? apmRouter.link('/backends/overview', {
const detailsUrl = dependencyName
? apmRouter.link('/dependencies/overview', {
query: {
...query,
backendName,
} as TypeOf<ApmRoutes, '/backends/overview'>['query'],
dependencyName,
} as TypeOf<ApmRoutes, '/dependencies/overview'>['query'],
})
: undefined;
@ -96,7 +97,7 @@ export function BackendContents({
trackEvent({
app: 'apm',
metricType: METRIC_TYPE.CLICK,
metric: 'service_map_to_backend_detail',
metric: 'service_map_to_dependency_detail',
});
}}
>

View file

@ -31,7 +31,7 @@ import { useTheme } from '../../../../hooks/use_theme';
import { useTraceExplorerEnabledSetting } from '../../../../hooks/use_trace_explorer_enabled_setting';
import { CytoscapeContext } from '../cytoscape';
import { getAnimationOptions, popoverWidth } from '../cytoscape_options';
import { BackendContents } from './backend_contents';
import { DependencyContents } from './dependency_contents';
import { EdgeContents } from './edge_contents';
import { ExternalsListContents } from './externals_list_contents';
import { ResourceContents } from './resource_contents';
@ -64,7 +64,7 @@ function getContentsComponent(
return EdgeContents;
}
return BackendContents;
return DependencyContents;
}
export interface ContentsProps {

View file

@ -99,7 +99,7 @@ const stories: Meta<Args> = {
};
export default stories;
export const Backend: Story<Args> = () => {
export const Dependency: Story<Args> = () => {
return (
<Popover
environment={ENVIRONMENT_ALL.value}
@ -109,7 +109,7 @@ export const Backend: Story<Args> = () => {
/>
);
};
Backend.args = {
Dependency.args = {
nodeData: {
'span.subtype': 'postgresql',
'span.destination.service.resource': 'postgresql',
@ -119,7 +119,7 @@ Backend.args = {
},
};
export const BackendWithLongTitle: Story<Args> = () => {
export const DependencyWithLongTitle: Story<Args> = () => {
return (
<Popover
environment={ENVIRONMENT_ALL.value}
@ -129,7 +129,7 @@ export const BackendWithLongTitle: Story<Args> = () => {
/>
);
};
BackendWithLongTitle.args = {
DependencyWithLongTitle.args = {
nodeData: {
'span.subtype': 'http',
'span.destination.service.resource':

View file

@ -10,12 +10,13 @@ import { render, screen, waitFor } from '@testing-library/react';
import React from 'react';
import * as stories from './popover.stories';
const { Backend, ExternalsList, Resource, Service } = composeStories(stories);
const { Dependency, ExternalsList, Resource, Service } =
composeStories(stories);
describe('Popover', () => {
describe('with backend data', () => {
describe('with dependency data', () => {
it('renders a dependency link', async () => {
render(<Backend />);
render(<Dependency />);
await waitFor(() => {
expect(

View file

@ -16,7 +16,7 @@ import { useApmServiceContext } from '../../../../context/apm_service/use_apm_se
import { useApmParams } from '../../../../hooks/use_apm_params';
import { useFetcher } from '../../../../hooks/use_fetcher';
import { useTimeRange } from '../../../../hooks/use_time_range';
import { BackendLink } from '../../../shared/backend_link';
import { DependencyLink } from '../../../shared/dependency_link';
import { DependenciesTable } from '../../../shared/dependencies_table';
import { ServiceLink } from '../../../shared/service_link';
@ -83,12 +83,12 @@ export function ServiceOverviewDependenciesTable({
const { location } = dependency;
const name = getNodeName(location);
const itemLink =
location.type === NodeType.backend ? (
<BackendLink
location.type === NodeType.dependency ? (
<DependencyLink
type={location.spanType}
subtype={location.spanSubtype}
query={{
backendName: location.backendName,
dependencyName: location.dependencyName,
comparisonEnabled,
offset,
environment,
@ -100,7 +100,7 @@ export function ServiceOverviewDependenciesTable({
trackEvent({
app: 'apm',
metricType: METRIC_TYPE.CLICK,
metric: 'service_dependencies_to_backend_detail',
metric: 'service_dependencies_to_dependency_detail',
});
}}
/>

View file

@ -19,7 +19,7 @@ import { NOT_AVAILABLE_LABEL } from '../../../../../../../../common/i18n';
import { Span } from '../../../../../../../../typings/es_schemas/ui/span';
import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction';
import { useAnyOfApmParams } from '../../../../../../../hooks/use_apm_params';
import { BackendLink } from '../../../../../../shared/backend_link';
import { DependencyLink } from '../../../../../../shared/dependency_link';
import { TransactionDetailLink } from '../../../../../../shared/links/apm/transaction_detail_link';
import { ServiceLink } from '../../../../../../shared/service_link';
import { StickyProperties } from '../../../../../../shared/sticky_properties';
@ -112,10 +112,10 @@ export function StickySpanProperties({ span, transaction }: Props) {
),
fieldName: SPAN_DESTINATION_SERVICE_RESOURCE,
val: (
<BackendLink
<DependencyLink
query={{
...query,
backendName: dependencyName,
dependencyName,
}}
subtype={span.span.subtype}
type={span.span.type}
@ -123,7 +123,7 @@ export function StickySpanProperties({ span, transaction }: Props) {
trackEvent({
app: 'apm',
metricType: METRIC_TYPE.CLICK,
metric: 'span_flyout_to_backend_detail',
metric: 'span_flyout_to_dependency_detail',
});
}}
/>

View file

@ -39,7 +39,7 @@ import { apmRouter } from './apm_route_config';
import { TrackPageview } from './track_pageview';
import { RedirectWithDefaultEnvironment } from '../shared/redirect_with_default_environment';
import { RedirectWithOffset } from '../shared/redirect_with_offset';
import { RedirectBackendsToBackendInventory } from './home/redirect_backends_to_backend_inventory';
import { RedirectDependenciesToDependenciesInventory } from './home/redirect_dependencies_to_dependencies_inventory';
const storage = new Storage(localStorage);
@ -66,7 +66,7 @@ export function ApmAppRoot({
<i18nCore.Context>
<TimeRangeIdContextProvider>
<RouterProvider history={history} router={apmRouter as any}>
<RedirectBackendsToBackendInventory>
<RedirectDependenciesToDependenciesInventory>
<RedirectWithDefaultEnvironment>
<RedirectWithDefaultDateRange>
<RedirectWithOffset>
@ -93,7 +93,7 @@ export function ApmAppRoot({
</RedirectWithOffset>
</RedirectWithDefaultDateRange>
</RedirectWithDefaultEnvironment>
</RedirectBackendsToBackendInventory>
</RedirectDependenciesToDependenciesInventory>
</RouterProvider>
</TimeRangeIdContextProvider>
</i18nCore.Context>

View file

@ -0,0 +1,90 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { i18n } from '@kbn/i18n';
import { toBooleanRt, toNumberRt } from '@kbn/io-ts-utils';
import { Outlet } from '@kbn/typed-react-router-config';
import * as t from 'io-ts';
import React from 'react';
import { Redirect } from 'react-router-dom';
import qs from 'query-string';
import { page } from '.';
import { offsetRt } from '../../../../common/comparison_rt';
import { DependencyDetailOperations } from '../../app/dependency_detail_operations';
import { DependencyDetailOverview } from '../../app/dependency_detail_overview';
import { DependencyDetailView } from '../../app/dependency_detail_view';
import { DependenciesInventory } from '../../app/dependencies_inventory';
import { DependencyOperationDetailView } from '../../app/dependency_operation_detail_view';
import { useApmParams } from '../../../hooks/use_apm_params';
export const DependenciesInventoryTitle = i18n.translate(
'xpack.apm.views.dependenciesInventory.title',
{ defaultMessage: 'Dependencies' }
);
function RedirectDependenciesToDependenciesOverview() {
const { query } = useApmParams('/dependencies');
const search = qs.stringify(query);
return <Redirect to={{ pathname: `/dependencies/overview`, search }} />;
}
export const dependencies = {
...page({
path: '/dependencies/inventory',
title: DependenciesInventoryTitle,
element: <DependenciesInventory />,
params: t.partial({
query: t.intersection([
t.type({
comparisonEnabled: toBooleanRt,
}),
offsetRt,
]),
}),
}),
'/dependencies': {
element: (
<DependencyDetailView>
<Outlet />
</DependencyDetailView>
),
params: t.partial({
query: t.intersection([
t.type({
comparisonEnabled: toBooleanRt,
dependencyName: t.string,
}),
offsetRt,
]),
}),
children: {
'/dependencies': {
element: <RedirectDependenciesToDependenciesOverview />,
},
'/dependencies/operations': {
element: <DependencyDetailOperations />,
},
'/dependencies/operation': {
params: t.type({
query: t.intersection([
t.type({
spanName: t.string,
}),
t.partial({
sampleRangeFrom: toNumberRt,
sampleRangeTo: toNumberRt,
}),
]),
}),
element: <DependencyOperationDetailView />,
},
'/dependencies/overview': {
element: <DependencyDetailOverview />,
},
},
},
};

View file

@ -5,35 +5,30 @@
* 2.0.
*/
import { i18n } from '@kbn/i18n';
import { toBooleanRt, toNumberRt } from '@kbn/io-ts-utils';
import { Outlet, Route } from '@kbn/typed-react-router-config';
import * as t from 'io-ts';
import React, { ComponentProps } from 'react';
import { toBooleanRt, toNumberRt } from '@kbn/io-ts-utils';
import { offsetRt } from '../../../../common/comparison_rt';
import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values';
import { environmentRt } from '../../../../common/environment_rt';
import { TraceSearchType } from '../../../../common/trace_explorer';
import { BackendDetailOverview } from '../../app/backend_detail_overview';
import { BackendInventory } from '../../app/backend_inventory';
import { TimeRangeMetadataContextProvider } from '../../../context/time_range_metadata/time_range_metadata_context';
import { Breadcrumb } from '../../app/breadcrumb';
import { ServiceInventory } from '../../app/service_inventory';
import { ServiceMapHome } from '../../app/service_map';
import { TraceOverview } from '../../app/trace_overview';
import { TraceExplorer } from '../../app/trace_explorer';
import { TopTracesOverview } from '../../app/top_traces_overview';
import { TraceExplorer } from '../../app/trace_explorer';
import { TraceOverview } from '../../app/trace_overview';
import { TransactionTab } from '../../app/transaction_details/waterfall_with_summary/transaction_tabs';
import { RedirectTo } from '../redirect_to';
import { ServiceGroupsRedirect } from '../service_groups_redirect';
import { ApmMainTemplate } from '../templates/apm_main_template';
import { ServiceGroupTemplate } from '../templates/service_group_template';
import { ServiceGroupsRedirect } from '../service_groups_redirect';
import { RedirectTo } from '../redirect_to';
import { offsetRt } from '../../../../common/comparison_rt';
import { TransactionTab } from '../../app/transaction_details/waterfall_with_summary/transaction_tabs';
import { BackendDetailOperations } from '../../app/backend_detail_operations';
import { BackendDetailView } from '../../app/backend_detail_view';
import { RedirectPathBackendDetailView } from './redirect_path_backend_detail_view';
import { RedirectBackendsToBackendDetailOverview } from './redirect_backends_to_backend_detail_view';
import { BackendOperationDetailView } from '../../app/backend_operation_detail_view';
import { TimeRangeMetadataContextProvider } from '../../../context/time_range_metadata/time_range_metadata_context';
import { dependencies } from './dependencies';
import { legacyBackends } from './legacy_backends';
function page<
export function page<
TPath extends string,
TChildren extends Record<string, Route> | undefined = undefined,
TParams extends t.Type<any> | undefined = undefined
@ -136,13 +131,6 @@ export const ServiceMapTitle = i18n.translate(
}
);
export const DependenciesInventoryTitle = i18n.translate(
'xpack.apm.views.dependenciesInventory.title',
{
defaultMessage: 'Dependencies',
}
);
export const DependenciesOperationsTitle = i18n.translate(
'xpack.apm.views.dependenciesOperations.title',
{
@ -242,74 +230,8 @@ export const home = {
},
},
}),
...page({
path: '/backends/inventory',
title: DependenciesInventoryTitle,
element: <BackendInventory />,
params: t.partial({
query: t.intersection([
t.type({
comparisonEnabled: toBooleanRt,
}),
offsetRt,
]),
}),
}),
'/backends/{backendName}/overview': {
element: <RedirectPathBackendDetailView />,
params: t.type({
path: t.type({
backendName: t.string,
}),
}),
},
'/backends': {
element: <Outlet />,
params: t.partial({
query: t.intersection([
t.type({
comparisonEnabled: toBooleanRt,
backendName: t.string,
}),
offsetRt,
]),
}),
children: {
'/backends': {
element: (
<BackendDetailView>
<Outlet />
</BackendDetailView>
),
children: {
'/backends/operations': {
element: <BackendDetailOperations />,
},
'/backends/operation': {
params: t.type({
query: t.intersection([
t.type({
spanName: t.string,
}),
t.partial({
sampleRangeFrom: toNumberRt,
sampleRangeTo: toNumberRt,
}),
]),
}),
element: <BackendOperationDetailView />,
},
'/backends/overview': {
element: <BackendDetailOverview />,
},
'/backends': {
element: <RedirectBackendsToBackendDetailOverview />,
},
},
},
},
},
...dependencies,
...legacyBackends,
'/': {
element: (
<ServiceGroupsRedirect>

View file

@ -0,0 +1,82 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { toBooleanRt, toNumberRt } from '@kbn/io-ts-utils';
import { Outlet } from '@kbn/typed-react-router-config';
import * as t from 'io-ts';
import React from 'react';
import { Redirect } from 'react-router-dom';
import qs from 'query-string';
import { offsetRt } from '../../../../common/comparison_rt';
import { useApmParams } from '../../../hooks/use_apm_params';
function RedirectBackends({ to }: { to: string }) {
const { query } = useApmParams('/backends/*');
const search = qs.stringify(query);
return <Redirect to={{ pathname: to, search }} />;
}
function RedirectBackendsOverviewToDependenciesOverview() {
const {
path: { dependencyName },
query,
} = useApmParams('/backends/{dependencyName}/overview');
const search = qs.stringify({ ...query, dependencyName });
return <Redirect to={{ pathname: `/dependencies/overview`, search }} />;
}
export const legacyBackends = {
'/backends/inventory': {
element: <RedirectBackends to="/dependencies/inventory" />,
params: t.partial({
query: t.intersection([
t.type({ comparisonEnabled: toBooleanRt }),
offsetRt,
]),
}),
},
'/backends/{dependencyName}/overview': {
element: <RedirectBackendsOverviewToDependenciesOverview />,
params: t.type({ path: t.type({ dependencyName: t.string }) }),
},
'/backends': {
element: <Outlet />,
params: t.partial({
query: t.intersection([
t.type({
comparisonEnabled: toBooleanRt,
dependencyName: t.string,
}),
offsetRt,
]),
}),
children: {
'/backends': {
element: <RedirectBackends to="/dependencies" />,
},
'/backends/operations': {
element: <RedirectBackends to="/dependencies/operations" />,
},
'/backends/operation': {
params: t.type({
query: t.intersection([
t.type({ spanName: t.string }),
t.partial({
sampleRangeFrom: toNumberRt,
sampleRangeTo: toNumberRt,
}),
]),
}),
element: <RedirectBackends to="/dependencies/operation" />,
},
'/backends/overview': {
element: <RedirectBackends to="/dependencies/overview" />,
},
},
},
};

View file

@ -1,19 +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 qs from 'query-string';
import React from 'react';
import { Redirect } from 'react-router-dom';
import { useApmParams } from '../../../hooks/use_apm_params';
export function RedirectBackendsToBackendDetailOverview() {
const { query } = useApmParams('/backends');
const search = qs.stringify(query);
return <Redirect to={{ pathname: `/backends/overview`, search }} />;
}

View file

@ -9,7 +9,7 @@ import { useLocation, Redirect } from 'react-router-dom';
import qs from 'query-string';
import React from 'react';
export function RedirectBackendsToBackendInventory({
export function RedirectDependenciesToDependenciesInventory({
children,
}: {
children: React.ReactElement;
@ -19,7 +19,7 @@ export function RedirectBackendsToBackendInventory({
const query = qs.parse(location.search);
const normalizedPathname = location.pathname.replace(/\/$/, '');
if (normalizedPathname === '/backends' && !('backendName' in query)) {
if (normalizedPathname === '/dependencies' && !('dependencyName' in query)) {
return (
<Redirect
to={qs.stringifyUrl({

View file

@ -1,22 +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 qs from 'query-string';
import React from 'react';
import { Redirect } from 'react-router-dom';
import { useApmParams } from '../../../hooks/use_apm_params';
export function RedirectPathBackendDetailView() {
const {
path: { backendName },
query,
} = useApmParams('/backends/{backendName}/overview');
const search = qs.stringify({ ...query, backendName });
return <Redirect to={{ pathname: `/backends/overview`, search }} />;
}

View file

@ -19,18 +19,18 @@ import { SearchBar } from '../../shared/search_bar';
import {
getKueryBarBoolFilter,
kueryBarPlaceholder,
} from '../../../../common/backends';
} from '../../../../common/dependencies';
import { useOperationBreakdownEnabledSetting } from '../../../hooks/use_operations_breakdown_enabled_setting';
interface Props {
children: React.ReactNode;
}
export function BackendDetailTemplate({ children }: Props) {
export function DependencyDetailTemplate({ children }: Props) {
const {
query,
query: { backendName, rangeFrom, rangeTo, environment },
} = useApmParams('/backends');
query: { dependencyName, rangeFrom, rangeTo, environment },
} = useApmParams('/dependencies');
const router = useApmRouter();
@ -43,52 +43,53 @@ export function BackendDetailTemplate({ children }: Props) {
const kueryBarBoolFilter = getKueryBarBoolFilter({
environment,
backendName,
dependencyName,
});
const backendMetadataFetch = useFetcher(
const dependencyMetadataFetch = useFetcher(
(callApmApi) => {
if (!start || !end) {
return;
}
return callApmApi('GET /internal/apm/backends/metadata', {
return callApmApi('GET /internal/apm/dependencies/metadata', {
params: {
query: {
backendName,
dependencyName,
start,
end,
},
},
});
},
[backendName, start, end]
[dependencyName, start, end]
);
const { data: { metadata } = {} } = backendMetadataFetch;
const { data: { metadata } = {} } = dependencyMetadataFetch;
const tabs = isOperationsBreakdownFeatureEnabled
? [
{
key: 'overview',
href: router.link('/backends/overview', {
href: router.link('/dependencies/overview', {
query,
}),
label: i18n.translate('xpack.apm.backendDetailOverview.title', {
label: i18n.translate('xpack.apm.DependencyDetailOverview.title', {
defaultMessage: 'Overview',
}),
isSelected: path === '/backends/overview',
isSelected: path === '/dependencies/overview',
},
{
key: 'operations',
href: router.link('/backends/operations', {
href: router.link('/dependencies/operations', {
query,
}),
label: i18n.translate('xpack.apm.backendDetailOperations.title', {
label: i18n.translate('xpack.apm.DependencyDetailOperations.title', {
defaultMessage: 'Operations',
}),
isSelected:
path === '/backends/operations' || path === '/backends/operation',
path === '/dependencies/operations' ||
path === '/dependencies/operation',
},
]
: [];
@ -101,7 +102,7 @@ export function BackendDetailTemplate({ children }: Props) {
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<EuiTitle size="l">
<h1>{backendName}</h1>
<h1>{dependencyName}</h1>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>

View file

@ -91,7 +91,11 @@ export function TimeseriesChart({
const chartTheme = useChartTheme();
const {
query: { comparisonEnabled, offset },
} = useAnyOfApmParams('/services', '/backends/*', '/services/{serviceName}');
} = useAnyOfApmParams(
'/services',
'/dependencies/*',
'/services/{serviceName}'
);
const anomalyChartTimeseries = getChartAnomalyTimeseries({
anomalyTimeseries,

View file

@ -8,13 +8,13 @@
import { Story } from '@storybook/react';
import React, { ComponentProps, ComponentType } from 'react';
import { MockApmPluginContextWrapper } from '../../context/apm_plugin/mock_apm_plugin_context';
import { BackendLink } from './backend_link';
import { DependencyLink } from './dependency_link';
type Args = ComponentProps<typeof BackendLink>;
type Args = ComponentProps<typeof DependencyLink>;
export default {
title: 'shared/BackendLink',
component: BackendLink,
title: 'shared/DependencyLink',
component: DependencyLink,
decorators: [
(StoryComponent: ComponentType) => {
return (
@ -27,11 +27,11 @@ export default {
};
export const Example: Story<Args> = (args) => {
return <BackendLink {...args} />;
return <DependencyLink {...args} />;
};
Example.args = {
query: {
backendName: 'postgres',
dependencyName: 'postgres',
environment: 'ENVIRONMENT_ALL',
kuery: '',
rangeFrom: 'now-15m',

View file

@ -8,11 +8,11 @@
import { composeStories } from '@storybook/testing-react';
import { render } from '@testing-library/react';
import React from 'react';
import * as stories from './backend_link.stories';
import * as stories from './dependency_link.stories';
const { Example } = composeStories(stories);
describe('BackendLink', () => {
describe('DependencyLink', () => {
it('renders', () => {
expect(() => render(<Example />)).not.toThrowError();
});

View file

@ -16,24 +16,19 @@ import { SpanIcon } from './span_icon';
const StyledLink = euiStyled(EuiLink)`${truncate('100%')};`;
interface BackendLinkProps {
query: TypeOf<ApmRoutes, '/backends/overview'>['query'];
interface Props {
query: TypeOf<ApmRoutes, '/dependencies/overview'>['query'];
subtype?: string;
type?: string;
onClick?: React.ComponentProps<typeof EuiLink>['onClick'];
}
export function BackendLink({
query,
subtype,
type,
onClick,
}: BackendLinkProps) {
export function DependencyLink({ query, subtype, type, onClick }: Props) {
const { link } = useApmRouter();
return (
<StyledLink
href={link('/backends/overview', {
href={link('/dependencies/overview', {
query,
})}
onClick={onClick}
@ -43,7 +38,7 @@ export function BackendLink({
<SpanIcon type={type} subtype={subtype} />
</EuiFlexItem>
<EuiFlexItem className="eui-textTruncate">
<span className="eui-textTruncate">{query.backendName}</span>
<span className="eui-textTruncate">{query.dependencyName}</span>
</EuiFlexItem>
</EuiFlexGroup>
</StyledLink>

View file

@ -18,16 +18,16 @@ import {
getTimeSeriesColor,
} from '../charts/helper/get_timeseries_color';
import { getComparisonChartTheme } from '../time_comparison/get_comparison_chart_theme';
import { BackendMetricChartsRouteParams } from './backend_metric_charts_route_params';
import { DependencyMetricChartsRouteParams } from './dependency_metric_charts_route_params';
import { useSearchServiceDestinationMetrics } from '../../../context/time_range_metadata/use_search_service_destination_metrics';
function yLabelFormat(y?: number | null) {
return asPercent(y || 0, 1);
}
export function BackendFailedTransactionRateChart({
export function DependencyFailedTransactionRateChart({
height,
backendName,
dependencyName,
kuery,
environment,
rangeFrom,
@ -37,7 +37,7 @@ export function BackendFailedTransactionRateChart({
spanName,
}: {
height: number;
} & BackendMetricChartsRouteParams) {
} & DependencyMetricChartsRouteParams) {
const { start, end } = useTimeRange({ rangeFrom, rangeTo });
const comparisonChartTheme = getComparisonChartTheme();
@ -51,10 +51,10 @@ export function BackendFailedTransactionRateChart({
return;
}
return callApmApi('GET /internal/apm/backends/charts/error_rate', {
return callApmApi('GET /internal/apm/dependencies/charts/error_rate', {
params: {
query: {
backendName,
dependencyName,
start,
end,
offset:
@ -70,7 +70,7 @@ export function BackendFailedTransactionRateChart({
});
},
[
backendName,
dependencyName,
start,
end,
offset,
@ -96,7 +96,7 @@ export function BackendFailedTransactionRateChart({
data: data.currentTimeseries,
type: 'linemark',
color: currentPeriodColor,
title: i18n.translate('xpack.apm.backendErrorRateChart.chartTitle', {
title: i18n.translate('xpack.apm.dependencyErrorRateChart.chartTitle', {
defaultMessage: 'Failed transaction rate',
}),
});

View file

@ -22,12 +22,12 @@ import {
getTimeSeriesColor,
} from '../charts/helper/get_timeseries_color';
import { getComparisonChartTheme } from '../time_comparison/get_comparison_chart_theme';
import { BackendMetricChartsRouteParams } from './backend_metric_charts_route_params';
import { DependencyMetricChartsRouteParams } from './dependency_metric_charts_route_params';
import { useSearchServiceDestinationMetrics } from '../../../context/time_range_metadata/use_search_service_destination_metrics';
export function BackendLatencyChart({
export function DependencyLatencyChart({
height,
backendName,
dependencyName,
rangeFrom,
rangeTo,
kuery,
@ -35,7 +35,7 @@ export function BackendLatencyChart({
offset,
comparisonEnabled,
spanName,
}: { height: number } & BackendMetricChartsRouteParams) {
}: { height: number } & DependencyMetricChartsRouteParams) {
const { start, end } = useTimeRange({ rangeFrom, rangeTo });
const comparisonChartTheme = getComparisonChartTheme();
@ -49,10 +49,10 @@ export function BackendLatencyChart({
return;
}
return callApmApi('GET /internal/apm/backends/charts/latency', {
return callApmApi('GET /internal/apm/dependencies/charts/latency', {
params: {
query: {
backendName,
dependencyName,
start,
end,
offset:
@ -68,7 +68,7 @@ export function BackendLatencyChart({
});
},
[
backendName,
dependencyName,
start,
end,
offset,
@ -95,7 +95,7 @@ export function BackendLatencyChart({
data: data.currentTimeseries,
type: 'linemark',
color: currentPeriodColor,
title: i18n.translate('xpack.apm.backendLatencyChart.chartTitle', {
title: i18n.translate('xpack.apm.dependencyLatencyChart.chartTitle', {
defaultMessage: 'Latency',
}),
});

View file

@ -7,12 +7,12 @@
import { TypeOf } from '@kbn/typed-react-router-config';
import { ApmRoutes } from '../../routing/apm_route_config';
export type BackendMetricChartsRouteParams = Pick<
export type DependencyMetricChartsRouteParams = Pick<
{ spanName?: string } & TypeOf<
ApmRoutes,
'/backends/operation' | '/backends/overview'
'/dependencies/operation' | '/dependencies/overview'
>['query'],
| 'backendName'
| 'dependencyName'
| 'comparisonEnabled'
| 'spanName'
| 'rangeFrom'

View file

@ -18,12 +18,12 @@ import {
getTimeSeriesColor,
} from '../charts/helper/get_timeseries_color';
import { getComparisonChartTheme } from '../time_comparison/get_comparison_chart_theme';
import { BackendMetricChartsRouteParams } from './backend_metric_charts_route_params';
import { DependencyMetricChartsRouteParams } from './dependency_metric_charts_route_params';
import { useSearchServiceDestinationMetrics } from '../../../context/time_range_metadata/use_search_service_destination_metrics';
export function BackendThroughputChart({
export function DependencyThroughputChart({
height,
backendName,
dependencyName,
rangeFrom,
rangeTo,
kuery,
@ -31,7 +31,7 @@ export function BackendThroughputChart({
offset,
comparisonEnabled,
spanName,
}: { height: number } & BackendMetricChartsRouteParams) {
}: { height: number } & DependencyMetricChartsRouteParams) {
const { start, end } = useTimeRange({ rangeFrom, rangeTo });
const comparisonChartTheme = getComparisonChartTheme();
@ -45,10 +45,10 @@ export function BackendThroughputChart({
return;
}
return callApmApi('GET /internal/apm/backends/charts/throughput', {
return callApmApi('GET /internal/apm/dependencies/charts/throughput', {
params: {
query: {
backendName,
dependencyName,
start,
end,
offset:
@ -64,7 +64,7 @@ export function BackendThroughputChart({
});
},
[
backendName,
dependencyName,
start,
end,
offset,
@ -91,9 +91,10 @@ export function BackendThroughputChart({
data: data.currentTimeseries,
type: 'linemark',
color: currentPeriodColor,
title: i18n.translate('xpack.apm.backendThroughputChart.chartTitle', {
defaultMessage: 'Throughput',
}),
title: i18n.translate(
'xpack.apm.dependencyThroughputChart.chartTitle',
{ defaultMessage: 'Throughput' }
),
});
}

View file

@ -10,18 +10,18 @@ import { i18n } from '@kbn/i18n';
import React from 'react';
import { useAnyOfApmParams } from '../../../hooks/use_apm_params';
import { useBreakpoints } from '../../../hooks/use_breakpoints';
import { BackendFailedTransactionRateChart } from './backend_error_rate_chart';
import { BackendLatencyChart } from './backend_latency_chart';
import { BackendMetricChartsRouteParams } from './backend_metric_charts_route_params';
import { BackendThroughputChart } from './backend_throughput_chart';
import { DependencyFailedTransactionRateChart } from './dependency_failed_transaction_rate_chart';
import { DependencyLatencyChart } from './dependency_latency_chart';
import { DependencyMetricChartsRouteParams } from './dependency_metric_charts_route_params';
import { DependencyThroughputChart } from './dependency_throughput_chart';
export function BackendMetricCharts() {
export function DependencyMetricCharts() {
const largeScreenOrSmaller = useBreakpoints().isLarge;
const {
query,
query: {
backendName,
dependencyName,
rangeFrom,
rangeTo,
kuery,
@ -29,12 +29,12 @@ export function BackendMetricCharts() {
comparisonEnabled,
offset,
},
} = useAnyOfApmParams('/backends/overview', '/backends/operation');
} = useAnyOfApmParams('/dependencies/overview', '/dependencies/operation');
const spanName = 'spanName' in query ? query.spanName : undefined;
const props: BackendMetricChartsRouteParams = {
backendName,
const props: DependencyMetricChartsRouteParams = {
dependencyName,
rangeFrom,
rangeTo,
kuery,
@ -53,24 +53,12 @@ export function BackendMetricCharts() {
<EuiPanel hasBorder={true}>
<EuiTitle size="xs">
<h2>
{i18n.translate('xpack.apm.backendDetailLatencyChartTitle', {
{i18n.translate('xpack.apm.dependencyDetailLatencyChartTitle', {
defaultMessage: 'Latency',
})}
</h2>
</EuiTitle>
<BackendLatencyChart height={200} {...props} />
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel hasBorder={true}>
<EuiTitle size="xs">
<h2>
{i18n.translate('xpack.apm.backendDetailThroughputChartTitle', {
defaultMessage: 'Throughput',
})}
</h2>
</EuiTitle>
<BackendThroughputChart height={200} {...props} />
<DependencyLatencyChart height={200} {...props} />
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
@ -78,12 +66,25 @@ export function BackendMetricCharts() {
<EuiTitle size="xs">
<h2>
{i18n.translate(
'xpack.apm.backendDetailFailedTransactionRateChartTitle',
'xpack.apm.dependencyDetailThroughputChartTitle',
{ defaultMessage: 'Throughput' }
)}
</h2>
</EuiTitle>
<DependencyThroughputChart height={200} {...props} />
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel hasBorder={true}>
<EuiTitle size="xs">
<h2>
{i18n.translate(
'xpack.apm.dependencyDetailFailedTransactionRateChartTitle',
{ defaultMessage: 'Failed transaction rate' }
)}
</h2>
</EuiTitle>
<BackendFailedTransactionRateChart height={200} {...props} />
<DependencyFailedTransactionRateChart height={200} {...props} />
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -20,8 +20,8 @@ export function isRouteWithTimeRange({
route.path === '/services' ||
route.path === '/traces' ||
route.path === '/service-map' ||
route.path === '/backends' ||
route.path === '/backends/inventory' ||
route.path === '/dependencies' ||
route.path === '/dependencies/inventory' ||
route.path === '/services/{serviceName}' ||
route.path === '/service-groups' ||
location.pathname === '/' ||
@ -44,8 +44,8 @@ export function isRouteWithComparison({
return (
route.path === '/services' ||
route.path === '/service-map' ||
route.path === '/backends' ||
route.path === '/backends/inventory' ||
route.path === '/dependencies' ||
route.path === '/dependencies/inventory' ||
route.path === '/services/{serviceName}' ||
route.path === '/service-groups' ||
location.pathname === '/' ||

View file

@ -39,7 +39,11 @@ export function TimeComparison() {
const { isSmall } = useBreakpoints();
const {
query: { rangeFrom, rangeTo, comparisonEnabled, offset },
} = useAnyOfApmParams('/services', '/backends/*', '/services/{serviceName}');
} = useAnyOfApmParams(
'/services',
'/dependencies/*',
'/services/{serviceName}'
);
const location = useLocation();
const apmRouter = useApmRouter();

View file

@ -38,7 +38,8 @@ export function TimeRangeMetadataContextProvider({
const routePath = useApmRoutePath();
const isOperationView =
routePath === '/backends/operation' || routePath === '/backends/operations';
routePath === '/dependencies/operation' ||
routePath === '/dependencies/operations';
const fetcherResult = useFetcher(
(callApmApi) => {

View file

@ -10,10 +10,10 @@ import { useBreadcrumb } from '../context/breadcrumbs/use_breadcrumb';
import { useAnyOfApmParams } from './use_apm_params';
import { useApmRouter } from './use_apm_router';
export function useBackendDetailOperationsBreadcrumb() {
export function useDependencyDetailOperationsBreadcrumb() {
const {
query: {
backendName,
dependencyName,
rangeFrom,
rangeTo,
refreshInterval,
@ -22,19 +22,19 @@ export function useBackendDetailOperationsBreadcrumb() {
kuery,
comparisonEnabled,
},
} = useAnyOfApmParams('/backends/operations', '/backends/operation');
} = useAnyOfApmParams('/dependencies/operations', '/dependencies/operation');
const apmRouter = useApmRouter();
useBreadcrumb([
{
title: i18n.translate(
'xpack.apm.backendDetailOperations.breadcrumbTitle',
'xpack.apm.dependencyDetailOperations.breadcrumbTitle',
{ defaultMessage: 'Operations' }
),
href: apmRouter.link('/backends/operations', {
href: apmRouter.link('/dependencies/operations', {
query: {
backendName,
dependencyName,
rangeFrom,
rangeTo,
refreshInterval,

View file

@ -19,7 +19,11 @@ const fallbackPreviousPeriodText = i18n.translate(
export const usePreviousPeriodLabel = () => {
const {
query: { rangeFrom, rangeTo, offset },
} = useAnyOfApmParams('/services', '/backends/*', '/services/{serviceName}');
} = useAnyOfApmParams(
'/services',
'/dependencies/*',
'/services/{serviceName}'
);
const { start, end } = useTimeRange({ rangeFrom, rangeTo });

View file

@ -178,7 +178,7 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
{
label: dependenciesTitle,
app: 'apm',
path: '/backends/inventory',
path: '/dependencies/inventory',
onClick: () => {
const { usageCollection } = pluginsStart as {
usageCollection?: UsageCollectionStart;
@ -188,7 +188,7 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
usageCollection.reportUiCounter(
'apm',
METRIC_TYPE.CLICK,
'side_nav_backend'
'side_nav_dependency'
);
}
},
@ -299,9 +299,9 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
{ id: 'traces', title: tracesTitle, path: '/traces' },
{ id: 'service-map', title: serviceMapTitle, path: '/service-map' },
{
id: 'backends',
id: 'dependencies',
title: dependenciesTitle,
path: '/backends/inventory',
path: '/dependencies/inventory',
},
],

View file

@ -30,7 +30,7 @@ import { Node, NodeType } from '../../../../common/connections';
import { excludeRumExitSpansQuery } from '../exclude_rum_exit_spans_query';
type Destination = {
backendName: string;
dependencyName: string;
spanId: string;
spanType: string;
spanSubtype: string;
@ -43,10 +43,10 @@ type Destination = {
}
);
// This operation tries to find a service for a backend, by:
// This operation tries to find a service for a dependency, by:
// - getting a span for each value of span.destination.service.resource (which indicates an outgoing call)
// - for each span, find the transaction it creates
// - if there is a transaction, match the backend name (span.destination.service.resource) to a service
// - if there is a transaction, match the dependency name (span.destination.service.resource) to a service
export const getDestinationMap = ({
setup,
start,
@ -91,7 +91,7 @@ export const getDestinationMap = ({
size: 10000,
sources: asMutableArray([
{
backendName: {
dependencyName: {
terms: { field: SPAN_DESTINATION_SERVICE_RESOURCE },
},
},
@ -130,7 +130,7 @@ export const getDestinationMap = ({
const spanId = sample[SPAN_ID] as string;
destinationsBySpanId.set(spanId, {
backendName: bucket.key.backendName as string,
dependencyName: bucket.key.dependencyName as string,
spanId,
spanType: (sample[SPAN_TYPE] as string | null) || '',
spanSubtype: (sample[SPAN_SUBTYPE] as string | null) || '',
@ -189,11 +189,11 @@ export const getDestinationMap = ({
}
});
const nodesByBackendName = new Map<string, Node>();
const nodesBydependencyName = new Map<string, Node>();
destinationsBySpanId.forEach((destination) => {
const existingDestination =
nodesByBackendName.get(destination.backendName) ?? {};
nodesBydependencyName.get(destination.dependencyName) ?? {};
const mergedDestination = {
...existingDestination,
@ -211,17 +211,17 @@ export const getDestinationMap = ({
};
} else {
node = {
backendName: mergedDestination.backendName,
dependencyName: mergedDestination.dependencyName,
spanType: mergedDestination.spanType,
spanSubtype: mergedDestination.spanSubtype,
id: objectHash({ backendName: mergedDestination.backendName }),
type: NodeType.backend,
id: objectHash({ dependencyName: mergedDestination.dependencyName }),
type: NodeType.dependency,
};
}
nodesByBackendName.set(destination.backendName, node);
nodesBydependencyName.set(destination.dependencyName, node);
});
return nodesByBackendName;
return nodesBydependencyName;
});
};

View file

@ -88,7 +88,7 @@ export const getStats = async ({
},
},
{
backendName: {
dependencyName: {
terms: {
field: SPAN_DESTINATION_SERVICE_RESOURCE,
},
@ -178,7 +178,7 @@ export const getStats = async ({
response.aggregations?.connections.buckets.map((bucket) => {
const sample = bucket.sample.top[0].metrics;
const serviceName = bucket.key.serviceName as string;
const backendName = bucket.key.backendName as string;
const dependencyName = bucket.key.dependencyName as string;
return {
from: {
@ -190,11 +190,11 @@ export const getStats = async ({
type: NodeType.service as const,
},
to: {
id: objectHash({ backendName }),
backendName,
id: objectHash({ dependencyName }),
dependencyName,
spanType: sample[SPAN_TYPE] as string,
spanSubtype: (sample[SPAN_SUBTYPE] || '') as string,
type: NodeType.backend as const,
type: NodeType.dependency as const,
},
value: {
count: sum(

View file

@ -53,7 +53,8 @@ export function getConnectionStats({
const statsWithLocationIds = allMetrics.map((statsItem) => {
const { from, timeseries, value } = statsItem;
const to = destinationMap.get(statsItem.to.backendName) ?? statsItem.to;
const to =
destinationMap.get(statsItem.to.dependencyName) ?? statsItem.to;
const location = collapseBy === 'upstream' ? from : to;

View file

@ -12,7 +12,7 @@ import type {
import { PickByValue } from 'utility-types';
import { agentKeysRouteRepository } from '../agent_keys/route';
import { alertsChartPreviewRouteRepository } from '../alerts/route';
import { backendsRouteRepository } from '../backends/route';
import { dependencisRouteRepository } from '../dependencies/route';
import { correlationsRouteRepository } from '../correlations/route';
import { dataViewRouteRepository } from '../data_view/route';
import { debugTelemetryRoute } from '../debug_telemetry/route';
@ -63,7 +63,7 @@ function getTypedGlobalApmServerRouteRepository() {
...customLinkRouteRepository,
...sourceMapsRouteRepository,
...apmFleetRouteRepository,
...backendsRouteRepository,
...dependencisRouteRepository,
...correlationsRouteRepository,
...fallbackToTransactionsRouteRepository,
...historicalDataRouteRepository,

View file

@ -1,145 +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 {
kqlQuery,
rangeQuery,
termQuery,
} from '@kbn/observability-plugin/server';
import { EventOutcome } from '../../../common/event_outcome';
import {
EVENT_OUTCOME,
SPAN_DESTINATION_SERVICE_RESOURCE,
SPAN_NAME,
} from '../../../common/elasticsearch_fieldnames';
import { environmentQuery } from '../../../common/utils/environment_query';
import { Setup } from '../../lib/helpers/setup_request';
import { getMetricsDateHistogramParams } from '../../lib/helpers/metrics';
import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms';
import {
getDocCountFieldForServiceDestinationStatistics,
getDocumentTypeFilterForServiceDestinationStatistics,
getProcessorEventForServiceDestinationStatistics,
} from '../../lib/helpers/spans/get_is_using_service_destination_metrics';
export async function getErrorRateChartsForBackend({
backendName,
spanName,
setup,
start,
end,
environment,
kuery,
searchServiceDestinationMetrics,
offset,
}: {
backendName: string;
spanName: string;
setup: Setup;
start: number;
end: number;
environment: string;
kuery: string;
searchServiceDestinationMetrics: boolean;
offset?: string;
}) {
const { apmEventClient } = setup;
const { offsetInMs, startWithOffset, endWithOffset } = getOffsetInMs({
start,
end,
offset,
});
const response = await apmEventClient.search('get_error_rate_for_backend', {
apm: {
events: [
getProcessorEventForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
],
},
body: {
size: 0,
query: {
bool: {
filter: [
...environmentQuery(environment),
...kqlQuery(kuery),
...rangeQuery(startWithOffset, endWithOffset),
...termQuery(SPAN_NAME, spanName || null),
...getDocumentTypeFilterForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: backendName } },
{
terms: {
[EVENT_OUTCOME]: [EventOutcome.success, EventOutcome.failure],
},
},
],
},
},
aggs: {
timeseries: {
date_histogram: getMetricsDateHistogramParams({
start: startWithOffset,
end: endWithOffset,
metricsInterval: 60,
}),
aggs: {
...(searchServiceDestinationMetrics
? {
total_count: {
sum: {
field: getDocCountFieldForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
},
},
}
: {}),
failures: {
filter: {
term: {
[EVENT_OUTCOME]: EventOutcome.failure,
},
},
aggs: {
...(searchServiceDestinationMetrics
? {
total_count: {
sum: {
field:
getDocCountFieldForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
},
},
}
: {}),
},
},
},
},
},
},
});
return (
response.aggregations?.timeseries.buckets.map((bucket) => {
const totalCount = bucket.total_count?.value ?? bucket.doc_count;
const failureCount =
bucket.failures.total_count?.value ?? bucket.failures.doc_count;
return {
x: bucket.key + offsetInMs,
y: failureCount / totalCount,
};
}) ?? []
);
}

View file

@ -18,9 +18,9 @@ import { Setup } from '../../lib/helpers/setup_request';
import { getOverallLatencyDistribution } from '../latency_distribution/get_overall_latency_distribution';
import { OverallLatencyDistributionResponse } from '../latency_distribution/types';
export async function getBackendLatencyDistribution({
export async function getDependencyLatencyDistribution({
setup,
backendName,
dependencyName,
spanName,
kuery,
environment,
@ -29,7 +29,7 @@ export async function getBackendLatencyDistribution({
percentileThreshold,
}: {
setup: Setup;
backendName: string;
dependencyName: string;
spanName: string;
kuery: string;
environment: Environment;
@ -54,7 +54,7 @@ export async function getBackendLatencyDistribution({
bool: {
filter: [
...termQuery(SPAN_NAME, spanName),
...termQuery(SPAN_DESTINATION_SERVICE_RESOURCE, backendName),
...termQuery(SPAN_DESTINATION_SERVICE_RESOURCE, dependencyName),
],
},
};

View file

@ -0,0 +1,148 @@
/*
* 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 {
kqlQuery,
rangeQuery,
termQuery,
} from '@kbn/observability-plugin/server';
import { EventOutcome } from '../../../common/event_outcome';
import {
EVENT_OUTCOME,
SPAN_DESTINATION_SERVICE_RESOURCE,
SPAN_NAME,
} from '../../../common/elasticsearch_fieldnames';
import { environmentQuery } from '../../../common/utils/environment_query';
import { Setup } from '../../lib/helpers/setup_request';
import { getMetricsDateHistogramParams } from '../../lib/helpers/metrics';
import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms';
import {
getDocCountFieldForServiceDestinationStatistics,
getDocumentTypeFilterForServiceDestinationStatistics,
getProcessorEventForServiceDestinationStatistics,
} from '../../lib/helpers/spans/get_is_using_service_destination_metrics';
export async function getErrorRateChartsForDependency({
dependencyName,
spanName,
setup,
start,
end,
environment,
kuery,
searchServiceDestinationMetrics,
offset,
}: {
dependencyName: string;
spanName: string;
setup: Setup;
start: number;
end: number;
environment: string;
kuery: string;
searchServiceDestinationMetrics: boolean;
offset?: string;
}) {
const { apmEventClient } = setup;
const { offsetInMs, startWithOffset, endWithOffset } = getOffsetInMs({
start,
end,
offset,
});
const response = await apmEventClient.search(
'get_error_rate_for_dependency',
{
apm: {
events: [
getProcessorEventForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
],
},
body: {
size: 0,
query: {
bool: {
filter: [
...environmentQuery(environment),
...kqlQuery(kuery),
...rangeQuery(startWithOffset, endWithOffset),
...termQuery(SPAN_NAME, spanName || null),
...getDocumentTypeFilterForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: dependencyName } },
{
terms: {
[EVENT_OUTCOME]: [EventOutcome.success, EventOutcome.failure],
},
},
],
},
},
aggs: {
timeseries: {
date_histogram: getMetricsDateHistogramParams({
start: startWithOffset,
end: endWithOffset,
metricsInterval: 60,
}),
aggs: {
...(searchServiceDestinationMetrics
? {
total_count: {
sum: {
field: getDocCountFieldForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
},
},
}
: {}),
failures: {
filter: {
term: {
[EVENT_OUTCOME]: EventOutcome.failure,
},
},
aggs: {
...(searchServiceDestinationMetrics
? {
total_count: {
sum: {
field:
getDocCountFieldForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
},
},
}
: {}),
},
},
},
},
},
},
}
);
return (
response.aggregations?.timeseries.buckets.map((bucket) => {
const totalCount = bucket.total_count?.value ?? bucket.doc_count;
const failureCount =
bucket.failures.total_count?.value ?? bucket.failures.doc_count;
return {
x: bucket.key + offsetInMs,
y: failureCount / totalCount,
};
}) ?? []
);
}

View file

@ -25,8 +25,8 @@ import {
getProcessorEventForServiceDestinationStatistics,
} from '../../lib/helpers/spans/get_is_using_service_destination_metrics';
export async function getLatencyChartsForBackend({
backendName,
export async function getLatencyChartsForDependency({
dependencyName,
spanName,
searchServiceDestinationMetrics,
setup,
@ -36,7 +36,7 @@ export async function getLatencyChartsForBackend({
kuery,
offset,
}: {
backendName: string;
dependencyName: string;
spanName: string;
searchServiceDestinationMetrics: boolean;
setup: Setup;
@ -54,7 +54,7 @@ export async function getLatencyChartsForBackend({
offset,
});
const response = await apmEventClient.search('get_latency_for_backend', {
const response = await apmEventClient.search('get_latency_for_dependency', {
apm: {
events: [
getProcessorEventForServiceDestinationStatistics(
@ -74,7 +74,7 @@ export async function getLatencyChartsForBackend({
...getDocumentTypeFilterForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: backendName } },
{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: dependencyName } },
],
},
},

View file

@ -11,42 +11,45 @@ import { ProcessorEvent } from '../../../common/processor_event';
import { SPAN_DESTINATION_SERVICE_RESOURCE } from '../../../common/elasticsearch_fieldnames';
import { Setup } from '../../lib/helpers/setup_request';
export async function getMetadataForBackend({
export async function getMetadataForDependency({
setup,
backendName,
dependencyName,
start,
end,
}: {
setup: Setup;
backendName: string;
dependencyName: string;
start: number;
end: number;
}) {
const { apmEventClient } = setup;
const sampleResponse = await apmEventClient.search('get_backend_sample', {
apm: {
events: [ProcessorEvent.span],
},
body: {
size: 1,
query: {
bool: {
filter: [
{
term: {
[SPAN_DESTINATION_SERVICE_RESOURCE]: backendName,
const sampleResponse = await apmEventClient.search(
'get_metadata_for_dependency',
{
apm: {
events: [ProcessorEvent.span],
},
body: {
size: 1,
query: {
bool: {
filter: [
{
term: {
[SPAN_DESTINATION_SERVICE_RESOURCE]: dependencyName,
},
},
},
...rangeQuery(start, end),
],
...rangeQuery(start, end),
],
},
},
sort: {
'@timestamp': 'desc',
},
},
sort: {
'@timestamp': 'desc',
},
},
});
}
);
const sample = maybe(sampleResponse.hits.hits[0])?._source;

View file

@ -24,8 +24,8 @@ import {
getProcessorEventForServiceDestinationStatistics,
} from '../../lib/helpers/spans/get_is_using_service_destination_metrics';
export async function getThroughputChartsForBackend({
backendName,
export async function getThroughputChartsForDependency({
dependencyName,
spanName,
setup,
start,
@ -35,7 +35,7 @@ export async function getThroughputChartsForBackend({
searchServiceDestinationMetrics,
offset,
}: {
backendName: string;
dependencyName: string;
spanName: string;
setup: Setup;
start: number;
@ -59,56 +59,59 @@ export async function getThroughputChartsForBackend({
minBucketSize: 60,
});
const response = await apmEventClient.search('get_throughput_for_backend', {
apm: {
events: [
getProcessorEventForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
],
},
body: {
size: 0,
query: {
bool: {
filter: [
...environmentQuery(environment),
...kqlQuery(kuery),
...rangeQuery(startWithOffset, endWithOffset),
...termQuery(SPAN_NAME, spanName || null),
...getDocumentTypeFilterForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: backendName } },
],
},
const response = await apmEventClient.search(
'get_throughput_for_dependency',
{
apm: {
events: [
getProcessorEventForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
],
},
aggs: {
timeseries: {
date_histogram: {
field: '@timestamp',
fixed_interval: intervalString,
min_doc_count: 0,
extended_bounds: { min: startWithOffset, max: endWithOffset },
body: {
size: 0,
query: {
bool: {
filter: [
...environmentQuery(environment),
...kqlQuery(kuery),
...rangeQuery(startWithOffset, endWithOffset),
...termQuery(SPAN_NAME, spanName || null),
...getDocumentTypeFilterForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: dependencyName } },
],
},
aggs: {
throughput: {
rate: {
...(searchServiceDestinationMetrics
? {
field: getDocCountFieldForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
}
: {}),
unit: 'minute',
},
aggs: {
timeseries: {
date_histogram: {
field: '@timestamp',
fixed_interval: intervalString,
min_doc_count: 0,
extended_bounds: { min: startWithOffset, max: endWithOffset },
},
aggs: {
throughput: {
rate: {
...(searchServiceDestinationMetrics
? {
field: getDocCountFieldForServiceDestinationStatistics(
searchServiceDestinationMetrics
),
}
: {}),
unit: 'minute',
},
},
},
},
},
},
},
});
}
);
return (
response.aggregations?.timeseries.buckets.map((bucket) => {

View file

@ -12,7 +12,7 @@ import { getConnectionStats } from '../../lib/connections/get_connection_stats';
import { getConnectionStatsItemsWithRelativeImpact } from '../../lib/connections/get_connection_stats/get_connection_stats_items_with_relative_impact';
import { Setup } from '../../lib/helpers/setup_request';
export async function getTopBackends({
export async function getTopDependencies({
setup,
start,
end,

View file

@ -31,7 +31,7 @@ import { calculateImpactBuilder } from '../traces/calculate_impact_builder';
const MAX_NUM_OPERATIONS = 500;
export interface BackendOperation {
export interface DependencyOperation {
spanName: string;
latency: number | null;
throughput: number;
@ -43,9 +43,9 @@ export interface BackendOperation {
>;
}
export async function getTopBackendOperations({
export async function getTopDependencyOperations({
setup,
backendName,
dependencyName,
start,
end,
offset,
@ -53,7 +53,7 @@ export async function getTopBackendOperations({
kuery,
}: {
setup: Setup;
backendName: string;
dependencyName: string;
start: number;
end: number;
offset?: string;
@ -90,48 +90,51 @@ export async function getTopBackendOperations({
},
};
const response = await apmEventClient.search('get_top_backend_operations', {
apm: {
events: [ProcessorEvent.span],
},
body: {
size: 0,
query: {
bool: {
filter: [
...rangeQuery(startWithOffset, endWithOffset),
...environmentQuery(environment),
...kqlQuery(kuery),
...termQuery(SPAN_DESTINATION_SERVICE_RESOURCE, backendName),
],
},
const response = await apmEventClient.search(
'get_top_dependency_operations',
{
apm: {
events: [ProcessorEvent.span],
},
aggs: {
operationName: {
terms: {
field: SPAN_NAME,
size: MAX_NUM_OPERATIONS,
body: {
size: 0,
query: {
bool: {
filter: [
...rangeQuery(startWithOffset, endWithOffset),
...environmentQuery(environment),
...kqlQuery(kuery),
...termQuery(SPAN_DESTINATION_SERVICE_RESOURCE, dependencyName),
],
},
aggs: {
over_time: {
date_histogram: getMetricsDateHistogramParams({
start: startWithOffset,
end: endWithOffset,
metricsInterval: 60,
}),
aggs,
},
aggs: {
operationName: {
terms: {
field: SPAN_NAME,
size: MAX_NUM_OPERATIONS,
},
...aggs,
total_time: {
sum: {
field: SPAN_DURATION,
aggs: {
over_time: {
date_histogram: getMetricsDateHistogramParams({
start: startWithOffset,
end: endWithOffset,
metricsInterval: 60,
}),
aggs,
},
...aggs,
total_time: {
sum: {
field: SPAN_DURATION,
},
},
},
},
},
},
},
});
}
);
const getImpact = calculateImpactBuilder(
response.aggregations?.operationName.buckets.map(
@ -141,8 +144,8 @@ export async function getTopBackendOperations({
return (
response.aggregations?.operationName.buckets.map(
(bucket): BackendOperation => {
const timeseries: BackendOperation['timeseries'] = {
(bucket): DependencyOperation => {
const timeseries: DependencyOperation['timeseries'] = {
latency: [],
throughput: [],
failureRate: [],

View file

@ -34,7 +34,7 @@ import { Setup } from '../../lib/helpers/setup_request';
const MAX_NUM_SPANS = 1000;
export interface BackendSpan {
export interface DependencySpan {
'@timestamp': number;
spanName: string;
serviceName: string;
@ -47,9 +47,9 @@ export interface BackendSpan {
outcome: EventOutcome;
}
export async function getTopBackendSpans({
export async function getTopDependencySpans({
setup,
backendName,
dependencyName,
spanName,
start,
end,
@ -59,7 +59,7 @@ export async function getTopBackendSpans({
sampleRangeTo,
}: {
setup: Setup;
backendName: string;
dependencyName: string;
spanName: string;
start: number;
end: number;
@ -67,11 +67,11 @@ export async function getTopBackendSpans({
kuery: string;
sampleRangeFrom?: number;
sampleRangeTo?: number;
}): Promise<BackendSpan[]> {
}): Promise<DependencySpan[]> {
const { apmEventClient } = setup;
const spans = (
await apmEventClient.search('get_top_backend_spans', {
await apmEventClient.search('get_top_dependency_spans', {
apm: {
events: [ProcessorEvent.span],
},
@ -83,7 +83,7 @@ export async function getTopBackendSpans({
...rangeQuery(start, end),
...environmentQuery(environment),
...kqlQuery(kuery),
...termQuery(SPAN_DESTINATION_SERVICE_RESOURCE, backendName),
...termQuery(SPAN_DESTINATION_SERVICE_RESOURCE, dependencyName),
...termQuery(SPAN_NAME, spanName),
...((sampleRangeFrom ?? 0) >= 0 && (sampleRangeTo ?? 0) > 0
? [
@ -118,7 +118,7 @@ export async function getTopBackendSpans({
const transactionIds = compact(spans.map((span) => span.transaction?.id));
const transactions = (
await apmEventClient.search('get_transactions_for_backend_spans', {
await apmEventClient.search('get_transactions_for_dependency_spans', {
apm: {
events: [ProcessorEvent.transaction],
},
@ -142,7 +142,7 @@ export async function getTopBackendSpans({
(transaction) => transaction.transaction.id
);
return spans.map((span): BackendSpan => {
return spans.map((span): DependencySpan => {
const transaction = span.transaction
? transactionsById[span.transaction.id]
: undefined;

View file

@ -12,11 +12,11 @@ import { getConnectionStats } from '../../lib/connections/get_connection_stats';
import { getConnectionStatsItemsWithRelativeImpact } from '../../lib/connections/get_connection_stats/get_connection_stats_items_with_relative_impact';
import { Setup } from '../../lib/helpers/setup_request';
export async function getUpstreamServicesForBackend({
export async function getUpstreamServicesForDependency({
setup,
start,
end,
backendName,
dependencyName,
numBuckets,
kuery,
environment,
@ -25,7 +25,7 @@ export async function getUpstreamServicesForBackend({
setup: Setup;
start: number;
end: number;
backendName: string;
dependencyName: string;
numBuckets: number;
kuery: string;
environment: string;
@ -36,7 +36,7 @@ export async function getUpstreamServicesForBackend({
start,
end,
filter: [
{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: backendName } },
{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: dependencyName } },
...environmentQuery(environment),
...kqlQuery(kuery),
],

View file

@ -10,24 +10,27 @@ import { toBooleanRt, toNumberRt } from '@kbn/io-ts-utils';
import { setupRequest } from '../../lib/helpers/setup_request';
import { environmentRt, kueryRt, rangeRt } from '../default_api_types';
import { createApmServerRoute } from '../apm_routes/create_apm_server_route';
import { getMetadataForBackend } from './get_metadata_for_backend';
import { getLatencyChartsForBackend } from './get_latency_charts_for_backend';
import { getTopBackends } from './get_top_backends';
import { getUpstreamServicesForBackend } from './get_upstream_services_for_backend';
import { getThroughputChartsForBackend } from './get_throughput_charts_for_backend';
import { getErrorRateChartsForBackend } from './get_error_rate_charts_for_backend';
import { getMetadataForDependency } from './get_metadata_for_dependency';
import { getLatencyChartsForDependency } from './get_latency_charts_for_dependency';
import { getTopDependencies } from './get_top_dependencies';
import { getUpstreamServicesForDependency } from './get_upstream_services_for_dependency';
import { getThroughputChartsForDependency } from './get_throughput_charts_for_dependency';
import { getErrorRateChartsForDependency } from './get_error_rate_charts_for_dependency';
import { ConnectionStatsItemWithImpact } from '../../../common/connections';
import { offsetRt } from '../../../common/comparison_rt';
import {
BackendOperation,
getTopBackendOperations,
} from './get_top_backend_operations';
import { getBackendLatencyDistribution } from './get_backend_latency_distribution';
DependencyOperation,
getTopDependencyOperations,
} from './get_top_dependency_operations';
import { getDependencyLatencyDistribution } from './get_dependency_latency_distribution';
import { OverallLatencyDistributionResponse } from '../latency_distribution/types';
import { BackendSpan, getTopBackendSpans } from './get_top_backend_spans';
import {
DependencySpan,
getTopDependencySpans,
} from './get_top_dependency_spans';
const topBackendsRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/backends/top_backends',
const topDependenciesRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/dependencies/top_dependencies',
params: t.intersection([
t.type({
query: t.intersection([
@ -47,7 +50,7 @@ const topBackendsRoute = createApmServerRoute({
handler: async (
resources
): Promise<{
backends: Array<{
dependencies: Array<{
currentStats: {
latency: {
value: number | null;
@ -103,17 +106,17 @@ const topBackendsRoute = createApmServerRoute({
const opts = { setup, start, end, numBuckets, environment, kuery };
const [currentBackends, previousBackends] = await Promise.all([
getTopBackends(opts),
offset ? getTopBackends({ ...opts, offset }) : Promise.resolve([]),
const [currentDependencies, previousDependencies] = await Promise.all([
getTopDependencies(opts),
offset ? getTopDependencies({ ...opts, offset }) : Promise.resolve([]),
]);
return {
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
backends: currentBackends.map((backend) => {
const { stats, ...rest } = backend;
const prev = previousBackends.find(
(item): boolean => item.location.id === backend.location.id
dependencies: currentDependencies.map((dependency) => {
const { stats, ...rest } = dependency;
const prev = previousDependencies.find(
(item): boolean => item.location.id === dependency.location.id
);
return {
...rest,
@ -125,12 +128,12 @@ const topBackendsRoute = createApmServerRoute({
},
});
const upstreamServicesForBackendRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/backends/upstream_services',
const upstreamServicesForDependencyRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/dependencies/upstream_services',
params: t.intersection([
t.type({
query: t.intersection([
t.type({ backendName: t.string }),
t.type({ dependencyName: t.string }),
rangeRt,
t.type({ numBuckets: toNumberRt }),
]),
@ -198,7 +201,7 @@ const upstreamServicesForBackendRoute = createApmServerRoute({
const setup = await setupRequest(resources);
const {
query: {
backendName,
dependencyName,
environment,
offset,
numBuckets,
@ -209,7 +212,7 @@ const upstreamServicesForBackendRoute = createApmServerRoute({
} = resources.params;
const opts = {
backendName,
dependencyName,
setup,
start,
end,
@ -219,9 +222,9 @@ const upstreamServicesForBackendRoute = createApmServerRoute({
};
const [currentServices, previousServices] = await Promise.all([
getUpstreamServicesForBackend(opts),
getUpstreamServicesForDependency(opts),
offset
? getUpstreamServicesForBackend({ ...opts, offset })
? getUpstreamServicesForDependency({ ...opts, offset })
: Promise.resolve([]),
]);
@ -248,10 +251,10 @@ const upstreamServicesForBackendRoute = createApmServerRoute({
},
});
const backendMetadataRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/backends/metadata',
const dependencyMetadataRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/dependencies/metadata',
params: t.type({
query: t.intersection([t.type({ backendName: t.string }), rangeRt]),
query: t.intersection([t.type({ dependencyName: t.string }), rangeRt]),
}),
options: {
tags: ['access:apm'],
@ -264,10 +267,10 @@ const backendMetadataRoute = createApmServerRoute({
const setup = await setupRequest(resources);
const { params } = resources;
const { backendName, start, end } = params.query;
const { dependencyName, start, end } = params.query;
const metadata = await getMetadataForBackend({
backendName,
const metadata = await getMetadataForDependency({
dependencyName,
setup,
start,
end,
@ -277,12 +280,12 @@ const backendMetadataRoute = createApmServerRoute({
},
});
const backendLatencyChartsRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/backends/charts/latency',
const dependencyLatencyChartsRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/dependencies/charts/latency',
params: t.type({
query: t.intersection([
t.type({
backendName: t.string,
dependencyName: t.string,
spanName: t.string,
searchServiceDestinationMetrics: toBooleanRt,
}),
@ -304,7 +307,7 @@ const backendLatencyChartsRoute = createApmServerRoute({
const setup = await setupRequest(resources);
const { params } = resources;
const {
backendName,
dependencyName,
searchServiceDestinationMetrics,
spanName,
kuery,
@ -315,8 +318,8 @@ const backendLatencyChartsRoute = createApmServerRoute({
} = params.query;
const [currentTimeseries, comparisonTimeseries] = await Promise.all([
getLatencyChartsForBackend({
backendName,
getLatencyChartsForDependency({
dependencyName,
spanName,
searchServiceDestinationMetrics,
setup,
@ -326,8 +329,8 @@ const backendLatencyChartsRoute = createApmServerRoute({
environment,
}),
offset
? getLatencyChartsForBackend({
backendName,
? getLatencyChartsForDependency({
dependencyName,
spanName,
searchServiceDestinationMetrics,
setup,
@ -344,12 +347,12 @@ const backendLatencyChartsRoute = createApmServerRoute({
},
});
const backendThroughputChartsRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/backends/charts/throughput',
const dependencyThroughputChartsRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/dependencies/charts/throughput',
params: t.type({
query: t.intersection([
t.type({
backendName: t.string,
dependencyName: t.string,
spanName: t.string,
searchServiceDestinationMetrics: toBooleanRt,
}),
@ -371,7 +374,7 @@ const backendThroughputChartsRoute = createApmServerRoute({
const setup = await setupRequest(resources);
const { params } = resources;
const {
backendName,
dependencyName,
searchServiceDestinationMetrics,
spanName,
kuery,
@ -382,8 +385,8 @@ const backendThroughputChartsRoute = createApmServerRoute({
} = params.query;
const [currentTimeseries, comparisonTimeseries] = await Promise.all([
getThroughputChartsForBackend({
backendName,
getThroughputChartsForDependency({
dependencyName,
spanName,
setup,
start,
@ -393,8 +396,8 @@ const backendThroughputChartsRoute = createApmServerRoute({
searchServiceDestinationMetrics,
}),
offset
? getThroughputChartsForBackend({
backendName,
? getThroughputChartsForDependency({
dependencyName,
spanName,
setup,
start,
@ -411,12 +414,12 @@ const backendThroughputChartsRoute = createApmServerRoute({
},
});
const backendFailedTransactionRateChartsRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/backends/charts/error_rate',
const dependencyFailedTransactionRateChartsRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/dependencies/charts/error_rate',
params: t.type({
query: t.intersection([
t.type({
backendName: t.string,
dependencyName: t.string,
spanName: t.string,
searchServiceDestinationMetrics: toBooleanRt,
}),
@ -438,7 +441,7 @@ const backendFailedTransactionRateChartsRoute = createApmServerRoute({
const setup = await setupRequest(resources);
const { params } = resources;
const {
backendName,
dependencyName,
spanName,
searchServiceDestinationMetrics,
kuery,
@ -449,8 +452,8 @@ const backendFailedTransactionRateChartsRoute = createApmServerRoute({
} = params.query;
const [currentTimeseries, comparisonTimeseries] = await Promise.all([
getErrorRateChartsForBackend({
backendName,
getErrorRateChartsForDependency({
dependencyName,
spanName,
setup,
start,
@ -460,8 +463,8 @@ const backendFailedTransactionRateChartsRoute = createApmServerRoute({
searchServiceDestinationMetrics,
}),
offset
? getErrorRateChartsForBackend({
backendName,
? getErrorRateChartsForDependency({
dependencyName,
spanName,
setup,
start,
@ -478,8 +481,8 @@ const backendFailedTransactionRateChartsRoute = createApmServerRoute({
},
});
const backendOperationsRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/backends/operations',
const dependencyOperationsRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/dependencies/operations',
options: {
tags: ['access:apm'],
},
@ -489,19 +492,21 @@ const backendOperationsRoute = createApmServerRoute({
environmentRt,
kueryRt,
offsetRt,
t.type({ backendName: t.string }),
t.type({ dependencyName: t.string }),
]),
}),
handler: async (resources): Promise<{ operations: BackendOperation[] }> => {
handler: async (
resources
): Promise<{ operations: DependencyOperation[] }> => {
const setup = await setupRequest(resources);
const {
query: { backendName, start, end, environment, kuery, offset },
query: { dependencyName, start, end, environment, kuery, offset },
} = resources.params;
const operations = await getTopBackendOperations({
const operations = await getTopDependencyOperations({
setup,
backendName,
dependencyName,
start,
end,
offset,
@ -513,12 +518,12 @@ const backendOperationsRoute = createApmServerRoute({
},
});
const backendLatencyDistributionChartsRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/backends/charts/distribution',
const dependencyLatencyDistributionChartsRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/dependencies/charts/distribution',
params: t.type({
query: t.intersection([
t.type({
backendName: t.string,
dependencyName: t.string,
spanName: t.string,
percentileThreshold: toNumberRt,
}),
@ -539,7 +544,7 @@ const backendLatencyDistributionChartsRoute = createApmServerRoute({
const setup = await setupRequest(resources);
const { params } = resources;
const {
backendName,
dependencyName,
spanName,
percentileThreshold,
kuery,
@ -548,9 +553,9 @@ const backendLatencyDistributionChartsRoute = createApmServerRoute({
end,
} = params.query;
return getBackendLatencyDistribution({
return getDependencyLatencyDistribution({
setup,
backendName,
dependencyName,
spanName,
percentileThreshold,
kuery,
@ -561,8 +566,8 @@ const backendLatencyDistributionChartsRoute = createApmServerRoute({
},
});
const topBackendSpansRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/backends/operations/spans',
const topDependencySpansRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/dependencies/operations/spans',
options: {
tags: ['access:apm'],
},
@ -571,16 +576,16 @@ const topBackendSpansRoute = createApmServerRoute({
rangeRt,
environmentRt,
kueryRt,
t.type({ backendName: t.string, spanName: t.string }),
t.type({ dependencyName: t.string, spanName: t.string }),
t.partial({ sampleRangeFrom: toNumberRt, sampleRangeTo: toNumberRt }),
]),
}),
handler: async (resources): Promise<{ spans: BackendSpan[] }> => {
handler: async (resources): Promise<{ spans: DependencySpan[] }> => {
const setup = await setupRequest(resources);
const {
query: {
backendName,
dependencyName,
spanName,
start,
end,
@ -591,9 +596,9 @@ const topBackendSpansRoute = createApmServerRoute({
},
} = resources.params;
const spans = await getTopBackendSpans({
const spans = await getTopDependencySpans({
setup,
backendName,
dependencyName,
spanName,
start,
end,
@ -607,14 +612,14 @@ const topBackendSpansRoute = createApmServerRoute({
},
});
export const backendsRouteRepository = {
...topBackendsRoute,
...upstreamServicesForBackendRoute,
...backendMetadataRoute,
...backendLatencyChartsRoute,
...backendThroughputChartsRoute,
...backendFailedTransactionRateChartsRoute,
...backendOperationsRoute,
...backendLatencyDistributionChartsRoute,
...topBackendSpansRoute,
export const dependencisRouteRepository = {
...topDependenciesRoute,
...upstreamServicesForDependencyRoute,
...dependencyMetadataRoute,
...dependencyLatencyChartsRoute,
...dependencyThroughputChartsRoute,
...dependencyFailedTransactionRateChartsRoute,
...dependencyOperationsRoute,
...dependencyLatencyDistributionChartsRoute,
...topDependencySpansRoute,
};

View file

@ -26,21 +26,21 @@ import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms';
interface Options {
setup: Setup;
environment: string;
backendName: string;
dependencyName: string;
start: number;
end: number;
offset?: string;
}
export function getServiceMapBackendNodeInfo({
export function getServiceMapDependencyNodeInfo({
environment,
backendName,
dependencyName,
setup,
start,
end,
offset,
}: Options): Promise<NodeStats> {
return withApmSpan('get_service_map_backend_node_stats', async () => {
return withApmSpan('get_service_map_dependency_node_stats', async () => {
const { apmEventClient } = setup;
const { offsetInMs, startWithOffset, endWithOffset } = getOffsetInMs({
start,
@ -67,7 +67,7 @@ export function getServiceMapBackendNodeInfo({
};
const response = await apmEventClient.search(
'get_service_map_backend_node_stats',
'get_service_map_dependency_node_stats',
{
apm: {
events: [ProcessorEvent.metric],
@ -77,7 +77,9 @@ export function getServiceMapBackendNodeInfo({
query: {
bool: {
filter: [
{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: backendName } },
{
term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: dependencyName },
},
...rangeQuery(startWithOffset, endWithOffset),
...environmentQuery(environment),
],

View file

@ -13,7 +13,7 @@ import { notifyFeatureUsage } from '../../feature';
import { getSearchAggregatedTransactions } from '../../lib/helpers/transactions';
import { setupRequest } from '../../lib/helpers/setup_request';
import { getServiceMap } from './get_service_map';
import { getServiceMapBackendNodeInfo } from './get_service_map_backend_node_info';
import { getServiceMapDependencyNodeInfo } from './get_service_map_dependency_node_info';
import { getServiceMapServiceNodeInfo } from './get_service_map_service_node_info';
import { createApmServerRoute } from '../apm_routes/create_apm_server_route';
import { environmentRt, rangeRt } from '../default_api_types';
@ -206,11 +206,11 @@ const serviceMapServiceNodeRoute = createApmServerRoute({
},
});
const serviceMapBackendNodeRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/service-map/backend',
const serviceMapDependencyNodeRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/service-map/dependency',
params: t.type({
query: t.intersection([
t.type({ backendName: t.string }),
t.type({ dependencyName: t.string }),
environmentRt,
rangeRt,
offsetRt,
@ -237,15 +237,15 @@ const serviceMapBackendNodeRoute = createApmServerRoute({
const setup = await setupRequest(resources);
const {
query: { backendName, environment, start, end, offset },
query: { dependencyName, environment, start, end, offset },
} = params;
const commonProps = { environment, setup, backendName, start, end };
const commonProps = { environment, setup, dependencyName, start, end };
const [currentPeriod, previousPeriod] = await Promise.all([
getServiceMapBackendNodeInfo(commonProps),
getServiceMapDependencyNodeInfo(commonProps),
offset
? getServiceMapBackendNodeInfo({ ...commonProps, offset })
? getServiceMapDependencyNodeInfo({ ...commonProps, offset })
: undefined,
]);
@ -256,5 +256,5 @@ const serviceMapBackendNodeRoute = createApmServerRoute({
export const serviceMapRouteRepository = {
...serviceMapRoute,
...serviceMapServiceNodeRoute,
...serviceMapBackendNodeRoute,
...serviceMapDependencyNodeRoute,
};

View file

@ -7555,15 +7555,6 @@
"xpack.apm.apmSchema.index": "Schéma du serveur APM - Index",
"xpack.apm.apmServiceGroups.index": "Groupes de services APM - Index",
"xpack.apm.apmSettings.index": "Paramètres APM - Index",
"xpack.apm.backendDetail.dependenciesTableColumnBackend": "Service",
"xpack.apm.backendDetail.dependenciesTableTitle": "Services en amont",
"xpack.apm.backendDetailFailedTransactionRateChartTitle": "Taux de transactions ayant échoué",
"xpack.apm.backendDetailLatencyChartTitle": "Latence",
"xpack.apm.backendDetailThroughputChartTitle": "Rendement",
"xpack.apm.backendErrorRateChart.chartTitle": "Taux de transactions ayant échoué",
"xpack.apm.backendInventory.dependencyTableColumn": "Dépendance",
"xpack.apm.backendLatencyChart.chartTitle": "Latence",
"xpack.apm.backendThroughputChart.chartTitle": "Rendement",
"xpack.apm.chart.annotation.version": "Version",
"xpack.apm.chart.cpuSeries.processAverageLabel": "Moyenne de processus",
"xpack.apm.chart.cpuSeries.processMaxLabel": "Max de processus",

View file

@ -7549,15 +7549,6 @@
"xpack.apm.apmSchema.index": "APMサーバースキーマ - インデックス",
"xpack.apm.apmServiceGroups.index": "APMサービスグループ - インデックス",
"xpack.apm.apmSettings.index": "APM 設定 - インデックス",
"xpack.apm.backendDetail.dependenciesTableColumnBackend": "サービス",
"xpack.apm.backendDetail.dependenciesTableTitle": "アップストリームサービス",
"xpack.apm.backendDetailFailedTransactionRateChartTitle": "失敗したトランザクション率",
"xpack.apm.backendDetailLatencyChartTitle": "レイテンシ",
"xpack.apm.backendDetailThroughputChartTitle": "スループット",
"xpack.apm.backendErrorRateChart.chartTitle": "失敗したトランザクション率",
"xpack.apm.backendInventory.dependencyTableColumn": "依存関係",
"xpack.apm.backendLatencyChart.chartTitle": "レイテンシ",
"xpack.apm.backendThroughputChart.chartTitle": "スループット",
"xpack.apm.chart.annotation.version": "バージョン",
"xpack.apm.chart.cpuSeries.processAverageLabel": "プロセス平均",
"xpack.apm.chart.cpuSeries.processMaxLabel": "プロセス最大",

View file

@ -7561,15 +7561,6 @@
"xpack.apm.apmSchema.index": "APM Server 架构 - 索引",
"xpack.apm.apmServiceGroups.index": "APM 服务组 - 索引",
"xpack.apm.apmSettings.index": "APM 设置 - 索引",
"xpack.apm.backendDetail.dependenciesTableColumnBackend": "服务",
"xpack.apm.backendDetail.dependenciesTableTitle": "上游服务",
"xpack.apm.backendDetailFailedTransactionRateChartTitle": "失败事务率",
"xpack.apm.backendDetailLatencyChartTitle": "延迟",
"xpack.apm.backendDetailThroughputChartTitle": "吞吐量",
"xpack.apm.backendErrorRateChart.chartTitle": "失败事务率",
"xpack.apm.backendInventory.dependencyTableColumn": "依赖项",
"xpack.apm.backendLatencyChart.chartTitle": "延迟",
"xpack.apm.backendThroughputChart.chartTitle": "吞吐量",
"xpack.apm.chart.annotation.version": "版本",
"xpack.apm.chart.cpuSeries.processAverageLabel": "进程平均值",
"xpack.apm.chart.cpuSeries.processMaxLabel": "进程最大值",

View file

@ -33,27 +33,27 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1;
async function callApi<TMetricName extends 'latency' | 'throughput' | 'error_rate'>({
backendName,
dependencyName,
searchServiceDestinationMetrics,
spanName = '',
metric,
kuery = '',
environment = ENVIRONMENT_ALL.value,
}: {
backendName: string;
dependencyName: string;
searchServiceDestinationMetrics: boolean;
spanName?: string;
metric: TMetricName;
kuery?: string;
environment?: string;
}): Promise<SupertestReturnType<`GET /internal/apm/backends/charts/${TMetricName}`>> {
}): Promise<SupertestReturnType<`GET /internal/apm/dependencies/charts/${TMetricName}`>> {
return await apmApiClient.readUser({
endpoint: `GET /internal/apm/backends/charts/${
endpoint: `GET /internal/apm/dependencies/charts/${
metric as 'latency' | 'throughput' | 'error_rate'
}`,
params: {
query: {
backendName,
dependencyName,
start: new Date(start).toISOString(),
end: new Date(end).toISOString(),
environment,
@ -80,7 +80,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
() => {
it('handles empty state', async () => {
const { body, status } = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
metric: 'latency',
searchServiceDestinationMetrics: true,
});
@ -110,7 +110,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
describe('without a kuery or environment', () => {
it('returns the correct latency', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
searchServiceDestinationMetrics: true,
spanName: '',
metric: 'latency',
@ -131,7 +131,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('returns the correct throughput', async () => {
const response = await callApi({
backendName: 'redis',
dependencyName: 'redis',
searchServiceDestinationMetrics: true,
spanName: '',
metric: 'throughput',
@ -142,7 +142,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('returns the correct failure rate', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
searchServiceDestinationMetrics: true,
spanName: '',
metric: 'error_rate',
@ -158,7 +158,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
describe('with a kuery', () => {
it('returns the correct latency', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
searchServiceDestinationMetrics: true,
spanName: '',
metric: 'latency',
@ -179,7 +179,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('returns the correct throughput', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
searchServiceDestinationMetrics: true,
spanName: '',
metric: 'throughput',
@ -194,7 +194,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('returns the correct failure rate', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
searchServiceDestinationMetrics: true,
spanName: '',
metric: 'error_rate',
@ -208,7 +208,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
describe('with an environment', () => {
it('returns the correct latency', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
searchServiceDestinationMetrics: true,
spanName: '',
metric: 'latency',
@ -229,7 +229,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('returns the correct throughput', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
searchServiceDestinationMetrics: true,
spanName: '',
metric: 'throughput',
@ -245,7 +245,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('returns the correct failure rate', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
searchServiceDestinationMetrics: true,
spanName: '',
metric: 'error_rate',
@ -260,7 +260,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
describe('with spanName', () => {
it('returns the correct latency', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
searchServiceDestinationMetrics: false,
spanName: '/_search',
metric: 'latency',
@ -281,7 +281,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('returns the correct throughput', async () => {
const response = await callApi({
backendName: 'redis',
dependencyName: 'redis',
searchServiceDestinationMetrics: false,
spanName: 'SET',
metric: 'throughput',
@ -292,7 +292,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('returns the correct failure rate', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
searchServiceDestinationMetrics: false,
spanName: '/_bulk',
metric: 'error_rate',

View file

@ -18,10 +18,10 @@ export default function ApiTest({ getService }: FtrProviderContext) {
async function callApi() {
return await apmApiClient.readUser({
endpoint: `GET /internal/apm/backends/metadata`,
endpoint: `GET /internal/apm/dependencies/metadata`,
params: {
query: {
backendName: dataConfig.span.destination,
dependencyName: dataConfig.span.destination,
start: new Date(start).toISOString(),
end: new Date(end).toISOString(),
},

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import expect from '@kbn/expect';
import { BackendNode } from '@kbn/apm-plugin/common/connections';
import { DependencyNode } from '@kbn/apm-plugin/common/connections';
import { FtrProviderContext } from '../../common/ftr_provider_context';
import { generateData } from './generate_data';
@ -15,7 +15,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const registry = getService('registry');
const start = new Date('2021-01-01T00:00:00.000Z').getTime();
const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1;
const backendName = 'elasticsearch';
const dependencyName = 'elasticsearch';
const serviceName = 'synth-go';
async function callApi() {
@ -62,8 +62,10 @@ export default function ApiTest({ getService }: FtrProviderContext) {
expect(status).to.be(200);
expect(
body.serviceDependencies.map(({ location }) => (location as BackendNode).backendName)
).to.eql([backendName]);
body.serviceDependencies.map(
({ location }) => (location as DependencyNode).dependencyName
)
).to.eql([dependencyName]);
const currentStatsLatencyValues =
body.serviceDependencies[0].currentStats.latency.timeseries;
@ -101,8 +103,10 @@ export default function ApiTest({ getService }: FtrProviderContext) {
expect(status).to.be(200);
expect(
body.serviceDependencies.map(({ location }) => (location as BackendNode).backendName)
).to.eql([backendName]);
body.serviceDependencies.map(
({ location }) => (location as DependencyNode).dependencyName
)
).to.eql([dependencyName]);
const currentStatsLatencyValues =
body.serviceDependencies[0].currentStats.latency.timeseries;

View file

@ -6,12 +6,12 @@
*/
import expect from '@kbn/expect';
import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api';
import { NodeType, BackendNode } from '@kbn/apm-plugin/common/connections';
import { NodeType, DependencyNode } from '@kbn/apm-plugin/common/connections';
import { FtrProviderContext } from '../../common/ftr_provider_context';
import { dataConfig, generateData } from './generate_data';
import { roundNumber } from '../../utils';
type TopDependencies = APIReturnType<'GET /internal/apm/backends/top_backends'>;
type TopDependencies = APIReturnType<'GET /internal/apm/dependencies/top_dependencies'>;
export default function ApiTest({ getService }: FtrProviderContext) {
const registry = getService('registry');
@ -23,7 +23,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
async function callApi() {
return await apmApiClient.readUser({
endpoint: 'GET /internal/apm/backends/top_backends',
endpoint: 'GET /internal/apm/dependencies/top_dependencies',
params: {
query: {
start: new Date(start).toISOString(),
@ -44,7 +44,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('handles empty state', async () => {
const { status, body } = await callApi();
expect(status).to.be(200);
expect(body.backends).to.empty();
expect(body.dependencies).to.empty();
});
}
);
@ -65,40 +65,40 @@ export default function ApiTest({ getService }: FtrProviderContext) {
after(() => synthtraceEsClient.clean());
it('returns an array of dependencies', () => {
expect(topDependencies).to.have.property('backends');
expect(topDependencies.backends).to.have.length(1);
expect(topDependencies).to.have.property('dependencies');
expect(topDependencies.dependencies).to.have.length(1);
});
it('returns correct dependency information', () => {
const location = topDependencies.backends[0].location as BackendNode;
const location = topDependencies.dependencies[0].location as DependencyNode;
const { span } = dataConfig;
expect(location.type).to.be(NodeType.backend);
expect(location.backendName).to.be(span.destination);
expect(location.type).to.be(NodeType.dependency);
expect(location.dependencyName).to.be(span.destination);
expect(location.spanType).to.be(span.type);
expect(location.spanSubtype).to.be(span.subType);
expect(location).to.have.property('id');
});
describe('returns the correct stats', () => {
let backends: TopDependencies['backends'][number];
let dependencies: TopDependencies['dependencies'][number];
before(() => {
backends = topDependencies.backends[0];
dependencies = topDependencies.dependencies[0];
});
it("doesn't have previous stats", () => {
expect(backends.previousStats).to.be(null);
expect(dependencies.previousStats).to.be(null);
});
it('has an "impact" property', () => {
expect(backends.currentStats).to.have.property('impact');
expect(dependencies.currentStats).to.have.property('impact');
});
it('returns the correct latency', () => {
const {
currentStats: { latency },
} = backends;
} = dependencies;
const { transaction } = dataConfig;
@ -111,7 +111,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('returns the correct throughput', () => {
const {
currentStats: { throughput },
} = backends;
} = dependencies;
const { rate } = dataConfig;
expect(roundNumber(throughput.value)).to.be(roundNumber(rate));
@ -120,7 +120,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('returns the correct total time', () => {
const {
currentStats: { totalTime },
} = backends;
} = dependencies;
const { rate, transaction } = dataConfig;
expect(
@ -131,7 +131,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('returns the correct error rate', () => {
const {
currentStats: { errorRate },
} = backends;
} = dependencies;
expect(errorRate.value).to.be(0);
expect(errorRate.timeseries.every(({ y }) => y === 0)).to.be(true);
});

View file

@ -12,7 +12,7 @@ import { FtrProviderContext } from '../../common/ftr_provider_context';
import { roundNumber } from '../../utils';
import { generateOperationData, generateOperationDataConfig } from './generate_operation_data';
type TopOperations = APIReturnType<'GET /internal/apm/backends/operations'>['operations'];
type TopOperations = APIReturnType<'GET /internal/apm/dependencies/operations'>['operations'];
const {
ES_BULK_DURATION,
@ -34,24 +34,24 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1;
async function callApi({
backendName,
dependencyName,
environment = ENVIRONMENT_ALL.value,
kuery = '',
}: {
backendName: string;
dependencyName: string;
environment?: string;
kuery?: string;
}) {
return await apmApiClient
.readUser({
endpoint: 'GET /internal/apm/backends/operations',
endpoint: 'GET /internal/apm/dependencies/operations',
params: {
query: {
start: new Date(start).toISOString(),
end: new Date(end).toISOString(),
environment,
kuery,
backendName,
dependencyName,
},
},
})
@ -60,7 +60,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
registry.when('Top operations when data is not loaded', { config: 'basic', archives: [] }, () => {
it('handles empty state', async () => {
const operations = await callApi({ backendName: 'elasticsearch' });
const operations = await callApi({ dependencyName: 'elasticsearch' });
expect(operations).to.empty();
});
});
@ -85,7 +85,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
let bulkOperation: ValuesType<TopOperations>;
before(async () => {
response = await callApi({ backendName: 'elasticsearch' });
response = await callApi({ dependencyName: 'elasticsearch' });
searchOperation = response.find((op) => op.spanName === '/_search')!;
bulkOperation = response.find((op) => op.spanName === '/_bulk')!;
});
@ -151,7 +151,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
let setOperation: ValuesType<TopOperations>;
before(async () => {
response = await callApi({ backendName: 'redis' });
response = await callApi({ dependencyName: 'redis' });
setOperation = response.find((op) => op.spanName === 'SET')!;
});
@ -177,7 +177,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
before(async () => {
response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
kuery: `service.name:"synth-go"`,
});
searchOperation = response.find((op) => op.spanName === '/_search')!;
@ -199,7 +199,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
before(async () => {
response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
environment: 'development',
});
searchOperation = response.find((op) => op.spanName === '/_search');

View file

@ -19,14 +19,14 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1;
async function callApi({
backendName,
dependencyName,
spanName,
kuery = '',
environment = ENVIRONMENT_ALL.value,
sampleRangeFrom,
sampleRangeTo,
}: {
backendName: string;
dependencyName: string;
spanName: string;
kuery?: string;
environment?: string;
@ -34,10 +34,10 @@ export default function ApiTest({ getService }: FtrProviderContext) {
sampleRangeTo?: number;
}) {
return await apmApiClient.readUser({
endpoint: `GET /internal/apm/backends/operations/spans`,
endpoint: `GET /internal/apm/dependencies/operations/spans`,
params: {
query: {
backendName,
dependencyName,
start: new Date(start).toISOString(),
end: new Date(end).toISOString(),
environment,
@ -51,12 +51,12 @@ export default function ApiTest({ getService }: FtrProviderContext) {
}
registry.when(
'Top backend spans when data is not loaded',
'Top dependency spans when data is not loaded',
{ config: 'basic', archives: [] },
() => {
it('handles empty state', async () => {
const { body, status } = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
spanName: '/_search',
});
@ -67,7 +67,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
);
registry.when(
'Top backend spans when data is loaded',
'Top dependency spans when data is loaded',
{ config: 'basic', archives: ['apm_mappings_only_8.0.0'] },
() => {
const javaInstance = apm.service('java', 'production', 'java').instance('instance-a');
@ -126,7 +126,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
describe('without a kuery or environment', () => {
it('returns the correct spans for the requested spanName', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
spanName: '/_search',
});
@ -170,7 +170,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
describe('with a kuery', () => {
it('returns the correct spans for the requested spanName', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
spanName: '/_search',
kuery: 'service.name:go',
});
@ -192,7 +192,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
describe('with an environment', () => {
it('returns the correct spans for the requested spanName', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
spanName: '/_search',
environment: 'development',
});
@ -214,7 +214,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
describe('when requesting spans without a transaction', () => {
it('should return the spans without transaction metadata', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
spanName: 'without transaction',
});
@ -242,7 +242,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
describe('when requesting spans within a specific sample range', () => {
it('returns only spans whose duration falls into the requested range', async () => {
const response = await callApi({
backendName: 'elasticsearch',
dependencyName: 'elasticsearch',
spanName: '/_search',
sampleRangeFrom: 50000,
sampleRangeTo: 99999,

View file

@ -15,14 +15,14 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const registry = getService('registry');
const start = new Date('2021-01-01T00:00:00.000Z').getTime();
const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1;
const backendName = 'elasticsearch';
const dependencyName = 'elasticsearch';
async function callApi() {
return await apmApiClient.readUser({
endpoint: 'GET /internal/apm/backends/upstream_services',
endpoint: 'GET /internal/apm/dependencies/upstream_services',
params: {
query: {
backendName,
dependencyName,
environment: 'production',
kuery: '',
numBuckets: 20,

View file

@ -12,7 +12,7 @@ import { ApmApiError, SupertestReturnType } from '../../common/apm_api_supertest
import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata';
import { FtrProviderContext } from '../../common/ftr_provider_context';
type BackendResponse = SupertestReturnType<'GET /internal/apm/service-map/backend'>;
type DependencyResponse = SupertestReturnType<'GET /internal/apm/service-map/dependency'>;
type ServiceNodeResponse =
SupertestReturnType<'GET /internal/apm/service-map/service/{serviceName}'>;
type ServiceMapResponse = SupertestReturnType<'GET /internal/apm/service-map'>;
@ -101,14 +101,14 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext)
});
});
describe('/internal/apm/service-map/backend', () => {
let response: BackendResponse;
describe('/internal/apm/service-map/dependency', () => {
let response: DependencyResponse;
before(async () => {
response = await apmApiClient.readUser({
endpoint: `GET /internal/apm/service-map/backend`,
endpoint: `GET /internal/apm/service-map/dependency`,
params: {
query: {
backendName: 'postgres',
dependencyName: 'postgres',
start: metadata.start,
end: metadata.end,
environment: 'ENVIRONMENT_ALL',
@ -371,14 +371,14 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext)
});
});
describe('/internal/apm/service-map/backend', () => {
let response: BackendResponse;
describe('/internal/apm/service-map/dependency', () => {
let response: DependencyResponse;
before(async () => {
response = await apmApiClient.readUser({
endpoint: `GET /internal/apm/service-map/backend`,
endpoint: `GET /internal/apm/service-map/dependency`,
params: {
query: {
backendName: 'postgresql',
dependencyName: 'postgresql',
start: metadata.start,
end: metadata.end,
environment: 'ENVIRONMENT_ALL',
@ -416,14 +416,14 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext)
});
describe('With comparison', () => {
describe('/internal/apm/service-map/backend', () => {
let response: BackendResponse;
describe('/internal/apm/service-map/dependency', () => {
let response: DependencyResponse;
before(async () => {
response = await apmApiClient.readUser({
endpoint: `GET /internal/apm/service-map/backend`,
endpoint: `GET /internal/apm/service-map/dependency`,
params: {
query: {
backendName: 'postgresql',
dependencyName: 'postgresql',
start: metadata.start,
end: metadata.end,
environment: 'ENVIRONMENT_ALL',

View file

@ -28,7 +28,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const { start, end } = archives[archiveName];
function getName(node: Node) {
return node.type === NodeType.service ? node.serviceName : node.backendName;
return node.type === NodeType.service ? node.serviceName : node.dependencyName;
}
registry.when(
@ -282,7 +282,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
throughput: roundNumber(postgres?.currentStats.throughput.value),
errorRate: roundNumber(postgres?.currentStats.errorRate.value),
impact: postgres?.currentStats.impact,
...pick(postgres?.location, 'spanType', 'spanSubtype', 'backendName', 'type'),
...pick(postgres?.location, 'spanType', 'spanSubtype', 'dependencyName', 'type'),
};
const count = 1;
@ -292,8 +292,8 @@ export default function ApiTest({ getService }: FtrProviderContext) {
expect(values).to.eql({
spanType: 'external',
spanSubtype: 'http',
backendName: 'postgres',
type: 'backend',
dependencyName: 'postgres',
type: 'dependency',
errorRate: roundNumber(errors / count),
latency: roundNumber(sum / count),
throughput: roundNumber(count / ((endTime - startTime) / 1000 / 60)),

View file

@ -7,7 +7,7 @@
import { apm, timerange } from '@elastic/apm-synthtrace';
import expect from '@kbn/expect';
import { meanBy, sumBy } from 'lodash';
import { BackendNode, ServiceNode } from '@kbn/apm-plugin/common/connections';
import { DependencyNode, ServiceNode } from '@kbn/apm-plugin/common/connections';
import { FtrProviderContext } from '../../common/ftr_provider_context';
import { roundNumber } from '../../utils';
@ -19,51 +19,57 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const start = new Date('2021-01-01T00:00:00.000Z').getTime();
const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1;
async function getThroughputValues(overrides?: { serviceName?: string; backendName?: string }) {
async function getThroughputValues(overrides?: {
serviceName?: string;
dependencyName?: string;
}) {
const commonQuery = {
start: new Date(start).toISOString(),
end: new Date(end).toISOString(),
environment: 'ENVIRONMENT_ALL',
};
const [topBackendsAPIResponse, backendThroughputChartAPIResponse, upstreamServicesApiResponse] =
await Promise.all([
apmApiClient.readUser({
endpoint: `GET /internal/apm/backends/top_backends`,
params: {
query: {
...commonQuery,
numBuckets: 20,
kuery: '',
},
const [
topDependenciesAPIResponse,
dependencyThroughputChartAPIResponse,
upstreamServicesApiResponse,
] = await Promise.all([
apmApiClient.readUser({
endpoint: `GET /internal/apm/dependencies/top_dependencies`,
params: {
query: {
...commonQuery,
numBuckets: 20,
kuery: '',
},
}),
apmApiClient.readUser({
endpoint: `GET /internal/apm/backends/charts/throughput`,
params: {
query: {
...commonQuery,
backendName: overrides?.backendName || 'elasticsearch',
spanName: '',
searchServiceDestinationMetrics: false,
kuery: '',
},
},
}),
apmApiClient.readUser({
endpoint: `GET /internal/apm/dependencies/charts/throughput`,
params: {
query: {
...commonQuery,
dependencyName: overrides?.dependencyName || 'elasticsearch',
spanName: '',
searchServiceDestinationMetrics: false,
kuery: '',
},
}),
apmApiClient.readUser({
endpoint: `GET /internal/apm/backends/upstream_services`,
params: {
query: {
...commonQuery,
backendName: overrides?.backendName || 'elasticsearch',
numBuckets: 20,
offset: '1d',
kuery: '',
},
},
}),
apmApiClient.readUser({
endpoint: `GET /internal/apm/dependencies/upstream_services`,
params: {
query: {
...commonQuery,
dependencyName: overrides?.dependencyName || 'elasticsearch',
numBuckets: 20,
offset: '1d',
kuery: '',
},
}),
]);
const backendThroughputChartMean = roundNumber(
meanBy(backendThroughputChartAPIResponse.body.currentTimeseries, 'y')
},
}),
]);
const dependencyThroughputChartMean = roundNumber(
meanBy(dependencyThroughputChartAPIResponse.body.currentTimeseries, 'y')
);
const upstreamServicesThroughput = upstreamServicesApiResponse.body.services.map(
@ -76,11 +82,11 @@ export default function ApiTest({ getService }: FtrProviderContext) {
);
return {
topBackends: topBackendsAPIResponse.body.backends.map((item) => [
(item.location as BackendNode).backendName,
topDependencies: topDependenciesAPIResponse.body.dependencies.map((item) => [
(item.location as DependencyNode).dependencyName,
roundNumber(item.currentStats.throughput.value),
]),
backendThroughputChartMean,
dependencyThroughputChartMean,
upstreamServicesThroughput,
};
}
@ -166,34 +172,34 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
it('returns elasticsearch and postgresql as dependencies', () => {
const { topBackends } = throughputValues;
const topBackendsAsObj = Object.fromEntries(topBackends);
expect(topBackendsAsObj.elasticsearch).to.equal(
const { topDependencies } = throughputValues;
const topDependenciesAsObj = Object.fromEntries(topDependencies);
expect(topDependenciesAsObj.elasticsearch).to.equal(
roundNumber(JAVA_PROD_RATE + GO_PROD_RATE)
);
expect(topBackendsAsObj.postgresql).to.equal(roundNumber(GO_PROD_RATE));
expect(topDependenciesAsObj.postgresql).to.equal(roundNumber(GO_PROD_RATE));
});
});
describe('compare throughput value between top backends, backend throughput chart and upstream services apis', () => {
describe('elasticsearch dependency', () => {
before(async () => {
throughputValues = await getThroughputValues({ backendName: 'elasticsearch' });
throughputValues = await getThroughputValues({ dependencyName: 'elasticsearch' });
});
it('matches throughput values between throughput chart and top dependency', () => {
const { topBackends, backendThroughputChartMean } = throughputValues;
const topBackendsAsObj = Object.fromEntries(topBackends);
const elasticsearchDependency = topBackendsAsObj.elasticsearch;
[elasticsearchDependency, backendThroughputChartMean].forEach((value) =>
const { topDependencies, dependencyThroughputChartMean } = throughputValues;
const topDependenciesAsObj = Object.fromEntries(topDependencies);
const elasticsearchDependency = topDependenciesAsObj.elasticsearch;
[elasticsearchDependency, dependencyThroughputChartMean].forEach((value) =>
expect(value).to.be.equal(roundNumber(JAVA_PROD_RATE + GO_PROD_RATE))
);
});
it('matches throughput values between upstream services and top dependency', () => {
const { topBackends, upstreamServicesThroughput } = throughputValues;
const topBackendsAsObj = Object.fromEntries(topBackends);
const elasticsearchDependency = topBackendsAsObj.elasticsearch;
const { topDependencies, upstreamServicesThroughput } = throughputValues;
const topDependenciesAsObj = Object.fromEntries(topDependencies);
const elasticsearchDependency = topDependenciesAsObj.elasticsearch;
const upstreamServiceThroughputSum = roundNumber(
sumBy(upstreamServicesThroughput, 'throughput')
);
@ -204,22 +210,22 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
describe('postgresql dependency', () => {
before(async () => {
throughputValues = await getThroughputValues({ backendName: 'postgresql' });
throughputValues = await getThroughputValues({ dependencyName: 'postgresql' });
});
it('matches throughput values between throughput chart and top dependency', () => {
const { topBackends, backendThroughputChartMean } = throughputValues;
const topBackendsAsObj = Object.fromEntries(topBackends);
const postgresqlDependency = topBackendsAsObj.postgresql;
[postgresqlDependency, backendThroughputChartMean].forEach((value) =>
const { topDependencies, dependencyThroughputChartMean } = throughputValues;
const topDependenciesAsObj = Object.fromEntries(topDependencies);
const postgresqlDependency = topDependenciesAsObj.postgresql;
[postgresqlDependency, dependencyThroughputChartMean].forEach((value) =>
expect(value).to.be.equal(roundNumber(GO_PROD_RATE))
);
});
it('matches throughput values between upstream services and top dependency', () => {
const { topBackends, upstreamServicesThroughput } = throughputValues;
const topBackendsAsObj = Object.fromEntries(topBackends);
const postgresqlDependency = topBackendsAsObj.postgresql;
const { topDependencies, upstreamServicesThroughput } = throughputValues;
const topDependenciesAsObj = Object.fromEntries(topDependencies);
const postgresqlDependency = topDependenciesAsObj.postgresql;
const upstreamServiceThroughputSum = roundNumber(
sumBy(upstreamServicesThroughput, 'throughput')
);