Skip to content

Commit 1d3fc74

Browse files
[7.x] [APM] Service overview: Dependencies table (#83416) (#85522)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
1 parent 48a2063 commit 1d3fc74

48 files changed

Lines changed: 1953 additions & 228 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/plugins/apm/common/elasticsearch_fieldnames.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ export const SPAN_NAME = 'span.name';
6161
export const SPAN_ID = 'span.id';
6262
export const SPAN_DESTINATION_SERVICE_RESOURCE =
6363
'span.destination.service.resource';
64+
export const SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT =
65+
'span.destination.service.response_time.count';
66+
67+
export const SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM =
68+
'span.destination.service.response_time.sum.us';
6469

6570
// Parent ID for a transaction or span
6671
export const PARENT_ID = 'parent.id';

x-pack/plugins/apm/common/utils/formatters/duration.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n';
88
import moment from 'moment';
99
import { memoize } from 'lodash';
1010
import { NOT_AVAILABLE_LABEL } from '../../../common/i18n';
11-
import { asDecimal, asDecimalOrInteger, asInteger } from './formatters';
11+
import { asDecimalOrInteger, asInteger, asDecimal } from './formatters';
1212
import { TimeUnit } from './datetime';
1313
import { Maybe } from '../../../typings/common';
1414
import { isFiniteNumber } from '../is_finite_number';
@@ -181,7 +181,6 @@ export function asDuration(
181181
const formatter = getDurationFormatter(value);
182182
return formatter(value, { defaultValue }).formatted;
183183
}
184-
185184
/**
186185
* Convert a microsecond value to decimal milliseconds. Normally we use
187186
* `asDuration`, but this is used in places like tables where we always want

x-pack/plugins/apm/common/utils/join_by_key/index.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,65 @@ describe('joinByKey', () => {
101101
},
102102
]);
103103
});
104+
105+
it('uses the custom merge fn to replace items', () => {
106+
const joined = joinByKey(
107+
[
108+
{
109+
serviceName: 'opbeans-java',
110+
values: ['a'],
111+
},
112+
{
113+
serviceName: 'opbeans-node',
114+
values: ['a'],
115+
},
116+
{
117+
serviceName: 'opbeans-node',
118+
values: ['b'],
119+
},
120+
{
121+
serviceName: 'opbeans-node',
122+
values: ['c'],
123+
},
124+
],
125+
'serviceName',
126+
(a, b) => ({
127+
...a,
128+
...b,
129+
values: a.values.concat(b.values),
130+
})
131+
);
132+
133+
expect(
134+
joined.find((item) => item.serviceName === 'opbeans-node')?.values
135+
).toEqual(['a', 'b', 'c']);
136+
});
137+
138+
it('deeply merges objects', () => {
139+
const joined = joinByKey(
140+
[
141+
{
142+
serviceName: 'opbeans-node',
143+
properties: {
144+
foo: '',
145+
},
146+
},
147+
{
148+
serviceName: 'opbeans-node',
149+
properties: {
150+
bar: '',
151+
},
152+
},
153+
],
154+
'serviceName'
155+
);
156+
157+
expect(joined[0]).toEqual({
158+
serviceName: 'opbeans-node',
159+
properties: {
160+
foo: '',
161+
bar: '',
162+
},
163+
});
164+
});
104165
});

x-pack/plugins/apm/common/utils/join_by_key/index.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66
import { UnionToIntersection, ValuesType } from 'utility-types';
7-
import { isEqual } from 'lodash';
7+
import { isEqual, pull, merge, castArray } from 'lodash';
88

99
/**
1010
* Joins a list of records by a given key. Key can be any type of value, from
@@ -23,24 +23,48 @@ import { isEqual } from 'lodash';
2323
*/
2424

2525
type JoinedReturnType<
26+
T extends Record<string, any>,
27+
U extends UnionToIntersection<T>
28+
> = Array<
29+
Partial<U> &
30+
{
31+
[k in keyof T]: T[k];
32+
}
33+
>;
34+
35+
type ArrayOrSingle<T> = T | T[];
36+
37+
export function joinByKey<
2638
T extends Record<string, any>,
2739
U extends UnionToIntersection<T>,
28-
V extends keyof T & keyof U
29-
> = Array<Partial<U> & Record<V, U[V]>>;
40+
V extends ArrayOrSingle<keyof T & keyof U>
41+
>(items: T[], key: V): JoinedReturnType<T, U>;
3042

3143
export function joinByKey<
3244
T extends Record<string, any>,
3345
U extends UnionToIntersection<T>,
34-
V extends keyof T & keyof U
35-
>(items: T[], key: V): JoinedReturnType<T, U, V> {
36-
return items.reduce<JoinedReturnType<T, U, V>>((prev, current) => {
37-
let item = prev.find((prevItem) => isEqual(prevItem[key], current[key]));
46+
V extends ArrayOrSingle<keyof T & keyof U>,
47+
W extends JoinedReturnType<T, U>,
48+
X extends (a: T, b: T) => ValuesType<W>
49+
>(items: T[], key: V, mergeFn: X): W;
50+
51+
export function joinByKey(
52+
items: Array<Record<string, any>>,
53+
key: string | string[],
54+
mergeFn: Function = (a: Record<string, any>, b: Record<string, any>) =>
55+
merge({}, a, b)
56+
) {
57+
const keys = castArray(key);
58+
return items.reduce<Array<Record<string, any>>>((prev, current) => {
59+
let item = prev.find((prevItem) =>
60+
keys.every((k) => isEqual(prevItem[k], current[k]))
61+
);
3862

3963
if (!item) {
40-
item = { ...current } as ValuesType<JoinedReturnType<T, U, V>>;
64+
item = { ...current };
4165
prev.push(item);
4266
} else {
43-
Object.assign(item, current);
67+
pull(prev, item).push(mergeFn(item, current));
4468
}
4569

4670
return prev;

x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape_options.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
ServiceHealthStatus,
1717
} from '../../../../common/service_health_status';
1818
import { FETCH_STATUS } from '../../../hooks/use_fetcher';
19-
import { defaultIcon, iconForNode } from './icons';
19+
import { iconForNode } from './icons';
2020

2121
export const popoverWidth = 280;
2222

@@ -116,9 +116,7 @@ const getStyle = (theme: EuiTheme): cytoscape.Stylesheet[] => {
116116
'background-color': theme.eui.euiColorGhost,
117117
// The DefinitelyTyped definitions don't specify that a function can be
118118
// used here.
119-
'background-image': isIE11
120-
? undefined
121-
: (el: cytoscape.NodeSingular) => iconForNode(el) ?? defaultIcon,
119+
'background-image': (el: cytoscape.NodeSingular) => iconForNode(el),
122120
'background-height': (el: cytoscape.NodeSingular) =>
123121
isService(el) ? '60%' : '40%',
124122
'background-width': (el: cytoscape.NodeSingular) =>

x-pack/plugins/apm/public/components/app/ServiceMap/icons.ts

Lines changed: 1 addition & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -10,73 +10,8 @@ import {
1010
SPAN_SUBTYPE,
1111
SPAN_TYPE,
1212
} from '../../../../common/elasticsearch_fieldnames';
13-
import awsIcon from './icons/aws.svg';
14-
import cassandraIcon from './icons/cassandra.svg';
15-
import databaseIcon from './icons/database.svg';
16-
import defaultIconImport from './icons/default.svg';
17-
import documentsIcon from './icons/documents.svg';
18-
import elasticsearchIcon from './icons/elasticsearch.svg';
19-
import globeIcon from './icons/globe.svg';
20-
import graphqlIcon from './icons/graphql.svg';
21-
import grpcIcon from './icons/grpc.svg';
22-
import handlebarsIcon from './icons/handlebars.svg';
23-
import kafkaIcon from './icons/kafka.svg';
24-
import mongodbIcon from './icons/mongodb.svg';
25-
import mysqlIcon from './icons/mysql.svg';
26-
import postgresqlIcon from './icons/postgresql.svg';
27-
import redisIcon from './icons/redis.svg';
28-
import websocketIcon from './icons/websocket.svg';
29-
import javaIcon from '../../shared/AgentIcon/icons/java.svg';
3013
import { getAgentIcon } from '../../shared/AgentIcon/get_agent_icon';
31-
32-
export const defaultIcon = defaultIconImport;
33-
34-
const defaultTypeIcons: { [key: string]: string } = {
35-
cache: databaseIcon,
36-
db: databaseIcon,
37-
ext: globeIcon,
38-
external: globeIcon,
39-
messaging: documentsIcon,
40-
resource: globeIcon,
41-
};
42-
43-
const typeIcons: { [key: string]: { [key: string]: string } } = {
44-
aws: {
45-
servicename: awsIcon,
46-
},
47-
db: {
48-
cassandra: cassandraIcon,
49-
elasticsearch: elasticsearchIcon,
50-
mongodb: mongodbIcon,
51-
mysql: mysqlIcon,
52-
postgresql: postgresqlIcon,
53-
redis: redisIcon,
54-
},
55-
external: {
56-
graphql: graphqlIcon,
57-
grpc: grpcIcon,
58-
websocket: websocketIcon,
59-
},
60-
messaging: {
61-
jms: javaIcon,
62-
kafka: kafkaIcon,
63-
},
64-
template: {
65-
handlebars: handlebarsIcon,
66-
},
67-
};
68-
69-
function getSpanIcon(type?: string, subtype?: string) {
70-
if (!type) {
71-
return;
72-
}
73-
74-
const types = type ? typeIcons[type] : {};
75-
if (subtype && types && subtype in types) {
76-
return types[subtype];
77-
}
78-
return defaultTypeIcons[type] || defaultIcon;
79-
}
14+
import { defaultIcon, getSpanIcon } from '../../shared/span_icon/get_span_icon';
8015

8116
// IE 11 does not properly load some SVGs, which causes a runtime error and the
8217
// map to not work at all. We would prefer to do some kind of feature detection

x-pack/plugins/apm/public/components/app/service_overview/index.tsx

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ import { isRumAgentName } from '../../../../common/agent_name';
1818
import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context';
1919
import { TransactionBreakdownChart } from '../../shared/charts/transaction_breakdown_chart';
2020
import { TransactionErrorRateChart } from '../../shared/charts/transaction_error_rate_chart';
21-
import { ServiceMapLink } from '../../shared/Links/apm/ServiceMapLink';
2221
import { SearchBar } from '../../shared/search_bar';
2322
import { ServiceOverviewErrorsTable } from './service_overview_errors_table';
23+
import { ServiceOverviewDependenciesTable } from './service_overview_dependencies_table';
2424
import { ServiceOverviewThroughputChart } from './service_overview_throughput_chart';
2525
import { ServiceOverviewTransactionsTable } from './service_overview_transactions_table';
26-
import { TableLinkFlexItem } from './table_link_flex_item';
2726

2827
/**
2928
* The height a chart should be if it's next to a table with 5 rows and a title.
@@ -98,30 +97,7 @@ export function ServiceOverview({
9897
</EuiFlexItem>
9998
<EuiFlexItem grow={6}>
10099
<EuiPanel>
101-
<EuiFlexGroup>
102-
<EuiFlexItem>
103-
<EuiTitle size="xs">
104-
<h2>
105-
{i18n.translate(
106-
'xpack.apm.serviceOverview.dependenciesTableTitle',
107-
{
108-
defaultMessage: 'Dependencies',
109-
}
110-
)}
111-
</h2>
112-
</EuiTitle>
113-
</EuiFlexItem>
114-
<TableLinkFlexItem>
115-
<ServiceMapLink serviceName={serviceName}>
116-
{i18n.translate(
117-
'xpack.apm.serviceOverview.dependenciesTableLinkText',
118-
{
119-
defaultMessage: 'View service map',
120-
}
121-
)}
122-
</ServiceMapLink>
123-
</TableLinkFlexItem>
124-
</EuiFlexGroup>
100+
<ServiceOverviewDependenciesTable serviceName={serviceName} />
125101
</EuiPanel>
126102
</EuiFlexItem>
127103
</EuiFlexGroup>

0 commit comments

Comments
 (0)