Skip to content

Commit

Permalink
[APM] Dependencies table for backend inventory/detail views (elastic#…
Browse files Browse the repository at this point in the history
  • Loading branch information
dgieselaar authored and vadimkibana committed Aug 8, 2021
1 parent 42408d1 commit 3340072
Show file tree
Hide file tree
Showing 35 changed files with 1,630 additions and 993 deletions.
67 changes: 67 additions & 0 deletions x-pack/plugins/apm/common/connections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { AgentName } from '../typings/es_schemas/ui/fields/agent';
import { Coordinate } from '../typings/timeseries';

export enum NodeType {
service = 'service',
backend = 'backend',
}

interface NodeBase {
id: string;
}

export interface ServiceNode extends NodeBase {
type: NodeType.service;
serviceName: string;
agentName: AgentName;
environment: string;
}

export interface BackendNode extends NodeBase {
type: NodeType.backend;
backendName: string;
spanType: string;
spanSubtype: string;
}

export type Node = ServiceNode | BackendNode;

export interface ConnectionStatsItem {
location: Node;
stats: {
latency: {
value: number | null;
timeseries: Coordinate[];
};
throughput: {
value: number | null;
timeseries: Coordinate[];
};
errorRate: {
value: number | null;
timeseries: Coordinate[];
};
};
}

export interface ConnectionStatsItemWithImpact extends ConnectionStatsItem {
stats: ConnectionStatsItem['stats'] & {
impact: number;
};
}

export interface ConnectionStatsItemWithComparisonData {
location: Node;
currentStats: ConnectionStatsItemWithImpact['stats'];
previousStats: ConnectionStatsItemWithImpact['stats'] | null;
}

export function getNodeName(node: Node) {
return node.type === NodeType.service ? node.serviceName : node.backendName;
}
22 changes: 19 additions & 3 deletions x-pack/plugins/apm/common/utils/get_offset_in_ms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,21 @@
import moment from 'moment';
import { parseInterval } from '../../../../../src/plugins/data/common';

export function getOffsetInMs(start: number, offset?: string) {
export function getOffsetInMs({
start,
end,
offset,
}: {
start: number;
end: number;
offset?: string;
}) {
if (!offset) {
return 0;
return {
startWithOffset: start,
endWithOffset: end,
offsetInMs: 0,
};
}

const interval = parseInterval(offset);
Expand All @@ -20,5 +32,9 @@ export function getOffsetInMs(start: number, offset?: string) {

const calculatedOffset = start - moment(start).subtract(interval).valueOf();

return calculatedOffset;
return {
startWithOffset: start - calculatedOffset,
endWithOffset: end - calculatedOffset,
offsetInMs: calculatedOffset,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* 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 React from 'react';
import { getNodeName, NodeType } from '../../../../common/connections';
import { useApmParams } from '../../../hooks/use_apm_params';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { useFetcher } from '../../../hooks/use_fetcher';
import { getTimeRangeComparison } from '../../shared/time_comparison/get_time_range_comparison';
import { DependenciesTable } from '../../shared/dependencies_table';
import { useApmBackendContext } from '../../../context/apm_backend/use_apm_backend_context';
import { ServiceLink } from '../../shared/service_link';

export function BackendDetailDependenciesTable() {
const {
urlParams: { start, end, environment, comparisonEnabled, comparisonType },
} = useUrlParams();

const {
query: { rangeFrom, rangeTo, kuery },
} = useApmParams('/backends/:backendName/overview');

const { offset } = getTimeRangeComparison({
start,
end,
comparisonEnabled,
comparisonType,
});

const { backendName } = useApmBackendContext();

const { data, status } = useFetcher(
(callApmApi) => {
if (!start || !end) {
return;
}

return callApmApi({
endpoint: 'GET /api/apm/backends/{backendName}/upstream_services',
params: {
path: {
backendName,
},
query: { start, end, environment, numBuckets: 20, offset },
},
});
},
[start, end, environment, offset, backendName]
);

const dependencies =
data?.services.map((dependency) => {
const { location } = dependency;
const name = getNodeName(location);

if (location.type !== NodeType.service) {
throw new Error('Expected a service node');
}

return {
name,
currentStats: dependency.currentStats,
previousStats: dependency.previousStats,
link: (
<ServiceLink
serviceName={location.serviceName}
agentName={location.agentName}
query={{
comparisonEnabled: comparisonEnabled ? 'true' : 'false',
comparisonType,
environment,
kuery,
rangeFrom,
rangeTo,
latencyAggregationType: undefined,
transactionType: undefined,
}}
/>
),
};
}) ?? [];

return (
<DependenciesTable
dependencies={dependencies}
title={i18n.translate('xpack.apm.backendDetail.dependenciesTableTitle', {
defaultMessage: 'Upstream services',
})}
nameColumnTitle={i18n.translate(
'xpack.apm.backendDetail.dependenciesTableColumnBackend',
{
defaultMessage: 'Service',
}
)}
status={status}
compact={false}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { EuiFlexItem } from '@elastic/eui';
import { EuiPanel } from '@elastic/eui';
import { EuiFlexGroup } from '@elastic/eui';
import React from 'react';
import { EuiSpacer } from '@elastic/eui';
import { ApmBackendContextProvider } from '../../../context/apm_backend/apm_backend_context';
import { useBreadcrumb } from '../../../context/breadcrumbs/use_breadcrumb';
import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context';
Expand All @@ -17,6 +18,7 @@ import { ApmMainTemplate } from '../../routing/templates/apm_main_template';
import { SearchBar } from '../../shared/search_bar';
import { BackendLatencyChart } from './backend_latency_chart';
import { BackendInventoryTitle } from '../../routing/home';
import { BackendDetailDependenciesTable } from './backend_detail_dependencies_table';

export function BackendDetailOverview() {
const {
Expand Down Expand Up @@ -53,6 +55,8 @@ export function BackendDetailOverview() {
</EuiFlexItem>
</EuiFlexGroup>
</ChartPointerEventContextProvider>
<EuiSpacer size="m" />
<BackendDetailDependenciesTable />
</ApmBackendContextProvider>
</ApmMainTemplate>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* 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 React from 'react';
import { getNodeName, NodeType } from '../../../../../common/connections';
import { useApmParams } from '../../../../hooks/use_apm_params';
import { useUrlParams } from '../../../../context/url_params_context/use_url_params';
import { useFetcher } from '../../../../hooks/use_fetcher';
import { getTimeRangeComparison } from '../../../shared/time_comparison/get_time_range_comparison';
import { DependenciesTable } from '../../../shared/dependencies_table';
import { BackendLink } from '../../../shared/backend_link';

export function BackendInventoryDependenciesTable() {
const {
urlParams: { start, end, environment, comparisonEnabled, comparisonType },
} = useUrlParams();

const {
query: { rangeFrom, rangeTo, kuery },
} = useApmParams('/backends');

const { offset } = getTimeRangeComparison({
start,
end,
comparisonEnabled,
comparisonType,
});

const { data, status } = useFetcher(
(callApmApi) => {
if (!start || !end) {
return;
}

return callApmApi({
endpoint: 'GET /api/apm/backends/top_backends',
params: {
query: { start, end, environment, numBuckets: 20, offset },
},
});
},
[start, end, environment, offset]
);

const dependencies =
data?.backends.map((dependency) => {
const { location } = dependency;
const name = getNodeName(location);

if (location.type !== NodeType.backend) {
throw new Error('Expected a backend node');
}
const link = (
<BackendLink
backendName={location.backendName}
type={location.spanType}
subtype={location.spanSubtype}
query={{
comparisonEnabled: comparisonEnabled ? 'true' : 'false',
comparisonType,
environment,
kuery,
rangeFrom,
rangeTo,
}}
/>
);

return {
name,
currentStats: dependency.currentStats,
previousStats: dependency.previousStats,
link,
};
}) ?? [];

return (
<DependenciesTable
dependencies={dependencies}
title={i18n.translate(
'xpack.apm.backendInventory.dependenciesTableTitle',
{
defaultMessage: 'Backends',
}
)}
nameColumnTitle={i18n.translate(
'xpack.apm.backendInventory.dependenciesTableColumnBackend',
{
defaultMessage: 'Backend',
}
)}
status={status}
compact={false}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@

import React from 'react';
import { SearchBar } from '../../shared/search_bar';
import { BackendInventoryDependenciesTable } from './backend_inventory_dependencies_table';

export function BackendInventory() {
return <SearchBar showTimeComparison />;
return (
<>
<SearchBar showTimeComparison />
<BackendInventoryDependenciesTable />
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,7 @@ export function ServiceOverview() {
{!isRumAgent && (
<EuiFlexItem grow={7}>
<EuiPanel hasBorder={true}>
<ServiceOverviewDependenciesTable
serviceName={serviceName}
/>
<ServiceOverviewDependenciesTable />
</EuiPanel>
</EuiFlexItem>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ describe('ServiceOverview', () => {
totalTransactionGroups: 0,
isAggregationAccurate: true,
},
'GET /api/apm/services/{serviceName}/dependencies': [],
'GET /api/apm/services/{serviceName}/dependencies': {
serviceDependencies: [],
},
'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics': [],
'GET /api/apm/services/{serviceName}/transactions/charts/latency': {
currentPeriod: {
Expand Down
Loading

0 comments on commit 3340072

Please sign in to comment.