Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] [APM] Service overview: Dependencies table (#83416) #85522

Merged
merged 1 commit into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions x-pack/plugins/apm/common/elasticsearch_fieldnames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ export const SPAN_NAME = 'span.name';
export const SPAN_ID = 'span.id';
export const SPAN_DESTINATION_SERVICE_RESOURCE =
'span.destination.service.resource';
export const SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT =
'span.destination.service.response_time.count';

export const SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM =
'span.destination.service.response_time.sum.us';

// Parent ID for a transaction or span
export const PARENT_ID = 'parent.id';
Expand Down
3 changes: 1 addition & 2 deletions x-pack/plugins/apm/common/utils/formatters/duration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n';
import moment from 'moment';
import { memoize } from 'lodash';
import { NOT_AVAILABLE_LABEL } from '../../../common/i18n';
import { asDecimal, asDecimalOrInteger, asInteger } from './formatters';
import { asDecimalOrInteger, asInteger, asDecimal } from './formatters';
import { TimeUnit } from './datetime';
import { Maybe } from '../../../typings/common';
import { isFiniteNumber } from '../is_finite_number';
Expand Down Expand Up @@ -181,7 +181,6 @@ export function asDuration(
const formatter = getDurationFormatter(value);
return formatter(value, { defaultValue }).formatted;
}

/**
* Convert a microsecond value to decimal milliseconds. Normally we use
* `asDuration`, but this is used in places like tables where we always want
Expand Down
61 changes: 61 additions & 0 deletions x-pack/plugins/apm/common/utils/join_by_key/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,65 @@ describe('joinByKey', () => {
},
]);
});

it('uses the custom merge fn to replace items', () => {
const joined = joinByKey(
[
{
serviceName: 'opbeans-java',
values: ['a'],
},
{
serviceName: 'opbeans-node',
values: ['a'],
},
{
serviceName: 'opbeans-node',
values: ['b'],
},
{
serviceName: 'opbeans-node',
values: ['c'],
},
],
'serviceName',
(a, b) => ({
...a,
...b,
values: a.values.concat(b.values),
})
);

expect(
joined.find((item) => item.serviceName === 'opbeans-node')?.values
).toEqual(['a', 'b', 'c']);
});

it('deeply merges objects', () => {
const joined = joinByKey(
[
{
serviceName: 'opbeans-node',
properties: {
foo: '',
},
},
{
serviceName: 'opbeans-node',
properties: {
bar: '',
},
},
],
'serviceName'
);

expect(joined[0]).toEqual({
serviceName: 'opbeans-node',
properties: {
foo: '',
bar: '',
},
});
});
});
42 changes: 33 additions & 9 deletions x-pack/plugins/apm/common/utils/join_by_key/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { UnionToIntersection, ValuesType } from 'utility-types';
import { isEqual } from 'lodash';
import { isEqual, pull, merge, castArray } from 'lodash';

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

type JoinedReturnType<
T extends Record<string, any>,
U extends UnionToIntersection<T>
> = Array<
Partial<U> &
{
[k in keyof T]: T[k];
}
>;

type ArrayOrSingle<T> = T | T[];

export function joinByKey<
T extends Record<string, any>,
U extends UnionToIntersection<T>,
V extends keyof T & keyof U
> = Array<Partial<U> & Record<V, U[V]>>;
V extends ArrayOrSingle<keyof T & keyof U>
>(items: T[], key: V): JoinedReturnType<T, U>;

export function joinByKey<
T extends Record<string, any>,
U extends UnionToIntersection<T>,
V extends keyof T & keyof U
>(items: T[], key: V): JoinedReturnType<T, U, V> {
return items.reduce<JoinedReturnType<T, U, V>>((prev, current) => {
let item = prev.find((prevItem) => isEqual(prevItem[key], current[key]));
V extends ArrayOrSingle<keyof T & keyof U>,
W extends JoinedReturnType<T, U>,
X extends (a: T, b: T) => ValuesType<W>
>(items: T[], key: V, mergeFn: X): W;

export function joinByKey(
items: Array<Record<string, any>>,
key: string | string[],
mergeFn: Function = (a: Record<string, any>, b: Record<string, any>) =>
merge({}, a, b)
) {
const keys = castArray(key);
return items.reduce<Array<Record<string, any>>>((prev, current) => {
let item = prev.find((prevItem) =>
keys.every((k) => isEqual(prevItem[k], current[k]))
);

if (!item) {
item = { ...current } as ValuesType<JoinedReturnType<T, U, V>>;
item = { ...current };
prev.push(item);
} else {
Object.assign(item, current);
pull(prev, item).push(mergeFn(item, current));
}

return prev;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
ServiceHealthStatus,
} from '../../../../common/service_health_status';
import { FETCH_STATUS } from '../../../hooks/use_fetcher';
import { defaultIcon, iconForNode } from './icons';
import { iconForNode } from './icons';

export const popoverWidth = 280;

Expand Down Expand Up @@ -116,9 +116,7 @@ const getStyle = (theme: EuiTheme): cytoscape.Stylesheet[] => {
'background-color': theme.eui.euiColorGhost,
// The DefinitelyTyped definitions don't specify that a function can be
// used here.
'background-image': isIE11
? undefined
: (el: cytoscape.NodeSingular) => iconForNode(el) ?? defaultIcon,
'background-image': (el: cytoscape.NodeSingular) => iconForNode(el),
'background-height': (el: cytoscape.NodeSingular) =>
isService(el) ? '60%' : '40%',
'background-width': (el: cytoscape.NodeSingular) =>
Expand Down
67 changes: 1 addition & 66 deletions x-pack/plugins/apm/public/components/app/ServiceMap/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,73 +10,8 @@ import {
SPAN_SUBTYPE,
SPAN_TYPE,
} from '../../../../common/elasticsearch_fieldnames';
import awsIcon from './icons/aws.svg';
import cassandraIcon from './icons/cassandra.svg';
import databaseIcon from './icons/database.svg';
import defaultIconImport from './icons/default.svg';
import documentsIcon from './icons/documents.svg';
import elasticsearchIcon from './icons/elasticsearch.svg';
import globeIcon from './icons/globe.svg';
import graphqlIcon from './icons/graphql.svg';
import grpcIcon from './icons/grpc.svg';
import handlebarsIcon from './icons/handlebars.svg';
import kafkaIcon from './icons/kafka.svg';
import mongodbIcon from './icons/mongodb.svg';
import mysqlIcon from './icons/mysql.svg';
import postgresqlIcon from './icons/postgresql.svg';
import redisIcon from './icons/redis.svg';
import websocketIcon from './icons/websocket.svg';
import javaIcon from '../../shared/AgentIcon/icons/java.svg';
import { getAgentIcon } from '../../shared/AgentIcon/get_agent_icon';

export const defaultIcon = defaultIconImport;

const defaultTypeIcons: { [key: string]: string } = {
cache: databaseIcon,
db: databaseIcon,
ext: globeIcon,
external: globeIcon,
messaging: documentsIcon,
resource: globeIcon,
};

const typeIcons: { [key: string]: { [key: string]: string } } = {
aws: {
servicename: awsIcon,
},
db: {
cassandra: cassandraIcon,
elasticsearch: elasticsearchIcon,
mongodb: mongodbIcon,
mysql: mysqlIcon,
postgresql: postgresqlIcon,
redis: redisIcon,
},
external: {
graphql: graphqlIcon,
grpc: grpcIcon,
websocket: websocketIcon,
},
messaging: {
jms: javaIcon,
kafka: kafkaIcon,
},
template: {
handlebars: handlebarsIcon,
},
};

function getSpanIcon(type?: string, subtype?: string) {
if (!type) {
return;
}

const types = type ? typeIcons[type] : {};
if (subtype && types && subtype in types) {
return types[subtype];
}
return defaultTypeIcons[type] || defaultIcon;
}
import { defaultIcon, getSpanIcon } from '../../shared/span_icon/get_span_icon';

// IE 11 does not properly load some SVGs, which causes a runtime error and the
// map to not work at all. We would prefer to do some kind of feature detection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ import { isRumAgentName } from '../../../../common/agent_name';
import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context';
import { TransactionBreakdownChart } from '../../shared/charts/transaction_breakdown_chart';
import { TransactionErrorRateChart } from '../../shared/charts/transaction_error_rate_chart';
import { ServiceMapLink } from '../../shared/Links/apm/ServiceMapLink';
import { SearchBar } from '../../shared/search_bar';
import { ServiceOverviewErrorsTable } from './service_overview_errors_table';
import { ServiceOverviewDependenciesTable } from './service_overview_dependencies_table';
import { ServiceOverviewThroughputChart } from './service_overview_throughput_chart';
import { ServiceOverviewTransactionsTable } from './service_overview_transactions_table';
import { TableLinkFlexItem } from './table_link_flex_item';

/**
* The height a chart should be if it's next to a table with 5 rows and a title.
Expand Down Expand Up @@ -98,30 +97,7 @@ export function ServiceOverview({
</EuiFlexItem>
<EuiFlexItem grow={6}>
<EuiPanel>
<EuiFlexGroup>
<EuiFlexItem>
<EuiTitle size="xs">
<h2>
{i18n.translate(
'xpack.apm.serviceOverview.dependenciesTableTitle',
{
defaultMessage: 'Dependencies',
}
)}
</h2>
</EuiTitle>
</EuiFlexItem>
<TableLinkFlexItem>
<ServiceMapLink serviceName={serviceName}>
{i18n.translate(
'xpack.apm.serviceOverview.dependenciesTableLinkText',
{
defaultMessage: 'View service map',
}
)}
</ServiceMapLink>
</TableLinkFlexItem>
</EuiFlexGroup>
<ServiceOverviewDependenciesTable serviceName={serviceName} />
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Loading