mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[APM] Make optimised loading of service inventory opt-in (#128471)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
7494a91a1f
commit
56c65bc9ef
9 changed files with 420 additions and 58 deletions
|
@ -18,3 +18,13 @@ export interface ServiceListItem {
|
|||
transactionErrorRate?: number | null;
|
||||
environments?: string[];
|
||||
}
|
||||
|
||||
export enum ServiceInventoryFieldName {
|
||||
ServiceName = 'serviceName',
|
||||
HealthStatus = 'healthStatus',
|
||||
Environments = 'environments',
|
||||
TransactionType = 'transactionType',
|
||||
Throughput = 'throughput',
|
||||
Latency = 'latency',
|
||||
TransactionErrorRate = 'transactionErrorRate',
|
||||
}
|
||||
|
|
|
@ -18,6 +18,10 @@ import { SearchBar } from '../../shared/search_bar';
|
|||
import { ServiceList } from './service_list';
|
||||
import { MLCallout, shouldDisplayMlCallout } from '../../shared/ml_callout';
|
||||
import { joinByKey } from '../../../../common/utils/join_by_key';
|
||||
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
import { apmServiceInventoryOptimizedSorting } from '../../../../../observability/common';
|
||||
import { ServiceInventoryFieldName } from '../../../../common/service_inventory';
|
||||
import { orderServiceItems } from './service_list/order_service_items';
|
||||
|
||||
const initialData = {
|
||||
requestId: '',
|
||||
|
@ -157,10 +161,36 @@ export function ServiceInventory() {
|
|||
/>
|
||||
);
|
||||
|
||||
const mainStatisticsItems = mainStatisticsFetch.data?.items ?? [];
|
||||
const preloadedServices = sortedAndFilteredServicesFetch.data?.services || [];
|
||||
|
||||
const displayHealthStatus = [
|
||||
...mainStatisticsItems,
|
||||
...preloadedServices,
|
||||
].some((item) => 'healthStatus' in item);
|
||||
|
||||
const tiebreakerField = useKibana().services.uiSettings?.get<boolean>(
|
||||
apmServiceInventoryOptimizedSorting
|
||||
)
|
||||
? ServiceInventoryFieldName.ServiceName
|
||||
: ServiceInventoryFieldName.Throughput;
|
||||
|
||||
const initialSortField = displayHealthStatus
|
||||
? ServiceInventoryFieldName.HealthStatus
|
||||
: tiebreakerField;
|
||||
|
||||
const initialSortDirection =
|
||||
initialSortField === ServiceInventoryFieldName.ServiceName ? 'asc' : 'desc';
|
||||
|
||||
const items = joinByKey(
|
||||
[
|
||||
...(sortedAndFilteredServicesFetch.data?.services ?? []),
|
||||
...(mainStatisticsFetch.data?.items ?? []),
|
||||
// only use preloaded services if tiebreaker field is service.name,
|
||||
// otherwise ignore them to prevent re-sorting of the table
|
||||
// once the tiebreaking metric comes in
|
||||
...(tiebreakerField === ServiceInventoryFieldName.ServiceName
|
||||
? preloadedServices
|
||||
: []),
|
||||
...mainStatisticsItems,
|
||||
],
|
||||
'serviceName'
|
||||
);
|
||||
|
@ -187,6 +217,17 @@ export function ServiceInventory() {
|
|||
comparisonFetch.status === FETCH_STATUS.LOADING ||
|
||||
comparisonFetch.status === FETCH_STATUS.NOT_INITIATED
|
||||
}
|
||||
displayHealthStatus={displayHealthStatus}
|
||||
initialSortField={initialSortField}
|
||||
initialSortDirection={initialSortDirection}
|
||||
sortFn={(itemsToSort, sortField, sortDirection) => {
|
||||
return orderServiceItems({
|
||||
items: itemsToSort,
|
||||
primarySortField: sortField,
|
||||
sortDirection,
|
||||
tiebreakerField,
|
||||
});
|
||||
}}
|
||||
comparisonData={comparisonFetch?.data}
|
||||
noItemsMessage={noItemsMessage}
|
||||
/>
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TypeOf } from '@kbn/typed-react-router-config';
|
||||
import { orderBy } from 'lodash';
|
||||
import React, { useMemo } from 'react';
|
||||
import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
|
||||
import { ServiceHealthStatus } from '../../../../../common/service_health_status';
|
||||
|
@ -45,7 +44,10 @@ import {
|
|||
getTimeSeriesColor,
|
||||
} from '../../../shared/charts/helper/get_timeseries_color';
|
||||
import { HealthBadge } from './health_badge';
|
||||
import { ServiceListItem } from '../../../../../common/service_inventory';
|
||||
import {
|
||||
ServiceInventoryFieldName,
|
||||
ServiceListItem,
|
||||
} from '../../../../../common/service_inventory';
|
||||
|
||||
type ServicesDetailedStatisticsAPIResponse =
|
||||
APIReturnType<'GET /internal/apm/services/detailed_statistics'>;
|
||||
|
@ -54,13 +56,6 @@ function formatString(value?: string | null) {
|
|||
return value || NOT_AVAILABLE_LABEL;
|
||||
}
|
||||
|
||||
const SERVICE_HEALTH_STATUS_ORDER = [
|
||||
ServiceHealthStatus.unknown,
|
||||
ServiceHealthStatus.healthy,
|
||||
ServiceHealthStatus.warning,
|
||||
ServiceHealthStatus.critical,
|
||||
];
|
||||
|
||||
export function getServiceColumns({
|
||||
query,
|
||||
showTransactionTypeColumn,
|
||||
|
@ -84,7 +79,7 @@ export function getServiceColumns({
|
|||
...(showHealthStatusColumn
|
||||
? [
|
||||
{
|
||||
field: 'healthStatus',
|
||||
field: ServiceInventoryFieldName.HealthStatus,
|
||||
name: i18n.translate('xpack.apm.servicesTable.healthColumnLabel', {
|
||||
defaultMessage: 'Health',
|
||||
}),
|
||||
|
@ -101,7 +96,7 @@ export function getServiceColumns({
|
|||
]
|
||||
: []),
|
||||
{
|
||||
field: 'serviceName',
|
||||
field: ServiceInventoryFieldName.ServiceName,
|
||||
name: i18n.translate('xpack.apm.servicesTable.nameColumnLabel', {
|
||||
defaultMessage: 'Name',
|
||||
}),
|
||||
|
@ -123,7 +118,7 @@ export function getServiceColumns({
|
|||
...(showWhenSmallOrGreaterThanLarge
|
||||
? [
|
||||
{
|
||||
field: 'environments',
|
||||
field: ServiceInventoryFieldName.Environments,
|
||||
name: i18n.translate(
|
||||
'xpack.apm.servicesTable.environmentColumnLabel',
|
||||
{
|
||||
|
@ -141,7 +136,7 @@ export function getServiceColumns({
|
|||
...(showTransactionTypeColumn && showWhenSmallOrGreaterThanXL
|
||||
? [
|
||||
{
|
||||
field: 'transactionType',
|
||||
field: ServiceInventoryFieldName.TransactionType,
|
||||
name: i18n.translate(
|
||||
'xpack.apm.servicesTable.transactionColumnLabel',
|
||||
{ defaultMessage: 'Transaction type' }
|
||||
|
@ -152,7 +147,7 @@ export function getServiceColumns({
|
|||
]
|
||||
: []),
|
||||
{
|
||||
field: 'latency',
|
||||
field: ServiceInventoryFieldName.Latency,
|
||||
name: i18n.translate('xpack.apm.servicesTable.latencyAvgColumnLabel', {
|
||||
defaultMessage: 'Latency (avg.)',
|
||||
}),
|
||||
|
@ -179,7 +174,7 @@ export function getServiceColumns({
|
|||
align: RIGHT_ALIGNMENT,
|
||||
},
|
||||
{
|
||||
field: 'throughput',
|
||||
field: ServiceInventoryFieldName.Throughput,
|
||||
name: i18n.translate('xpack.apm.servicesTable.throughputColumnLabel', {
|
||||
defaultMessage: 'Throughput',
|
||||
}),
|
||||
|
@ -207,7 +202,7 @@ export function getServiceColumns({
|
|||
align: RIGHT_ALIGNMENT,
|
||||
},
|
||||
{
|
||||
field: 'transactionErrorRate',
|
||||
field: ServiceInventoryFieldName.TransactionErrorRate,
|
||||
name: i18n.translate('xpack.apm.servicesTable.transactionErrorRate', {
|
||||
defaultMessage: 'Failed transaction rate',
|
||||
}),
|
||||
|
@ -246,6 +241,14 @@ interface Props {
|
|||
noItemsMessage?: React.ReactNode;
|
||||
isLoading: boolean;
|
||||
isFailure?: boolean;
|
||||
displayHealthStatus: boolean;
|
||||
initialSortField: ServiceInventoryFieldName;
|
||||
initialSortDirection: 'asc' | 'desc';
|
||||
sortFn: (
|
||||
sortItems: ServiceListItem[],
|
||||
sortField: ServiceInventoryFieldName,
|
||||
sortDirection: 'asc' | 'desc'
|
||||
) => ServiceListItem[];
|
||||
}
|
||||
|
||||
export function ServiceList({
|
||||
|
@ -255,9 +258,12 @@ export function ServiceList({
|
|||
comparisonData,
|
||||
isLoading,
|
||||
isFailure,
|
||||
displayHealthStatus,
|
||||
initialSortField,
|
||||
initialSortDirection,
|
||||
sortFn,
|
||||
}: Props) {
|
||||
const breakpoints = useBreakpoints();
|
||||
const displayHealthStatus = items.some((item) => 'healthStatus' in item);
|
||||
|
||||
const showTransactionTypeColumn = items.some(
|
||||
({ transactionType }) =>
|
||||
|
@ -292,9 +298,6 @@ export function ServiceList({
|
|||
]
|
||||
);
|
||||
|
||||
const initialSortField = displayHealthStatus ? 'healthStatus' : 'serviceName';
|
||||
const initialSortDirection = displayHealthStatus ? 'desc' : 'asc';
|
||||
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="xs" direction="column" responsive={false}>
|
||||
<EuiFlexItem>
|
||||
|
@ -333,7 +336,7 @@ export function ServiceList({
|
|||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<ManagedTable
|
||||
<ManagedTable<ServiceListItem>
|
||||
isLoading={isLoading}
|
||||
error={isFailure}
|
||||
columns={serviceColumns}
|
||||
|
@ -341,41 +344,13 @@ export function ServiceList({
|
|||
noItemsMessage={noItemsMessage}
|
||||
initialSortField={initialSortField}
|
||||
initialSortDirection={initialSortDirection}
|
||||
sortFn={(itemsToSort, sortField, sortDirection) => {
|
||||
// For healthStatus, sort items by healthStatus first, then by name
|
||||
return sortField === 'healthStatus'
|
||||
? orderBy(
|
||||
itemsToSort,
|
||||
[
|
||||
(item) => {
|
||||
return item.healthStatus
|
||||
? SERVICE_HEALTH_STATUS_ORDER.indexOf(item.healthStatus)
|
||||
: -1;
|
||||
},
|
||||
(item) => item.serviceName.toLowerCase(),
|
||||
],
|
||||
[sortDirection, sortDirection === 'asc' ? 'desc' : 'asc']
|
||||
)
|
||||
: orderBy(
|
||||
itemsToSort,
|
||||
(item) => {
|
||||
switch (sortField) {
|
||||
// Use `?? -1` here so `undefined` will appear after/before `0`.
|
||||
// In the table this will make the "N/A" items always at the
|
||||
// bottom/top.
|
||||
case 'latency':
|
||||
return item.latency ?? -1;
|
||||
case 'throughput':
|
||||
return item.throughput ?? -1;
|
||||
case 'transactionErrorRate':
|
||||
return item.transactionErrorRate ?? -1;
|
||||
default:
|
||||
return item[sortField as keyof typeof item];
|
||||
}
|
||||
},
|
||||
sortDirection
|
||||
);
|
||||
}}
|
||||
sortFn={(itemsToSort, sortField, sortDirection) =>
|
||||
sortFn(
|
||||
itemsToSort,
|
||||
sortField as ServiceInventoryFieldName,
|
||||
sortDirection
|
||||
)
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* 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 { ServiceHealthStatus } from '../../../../../common/service_health_status';
|
||||
import { ServiceInventoryFieldName } from '../../../../../common/service_inventory';
|
||||
import { orderServiceItems } from './order_service_items';
|
||||
|
||||
describe('orderServiceItems', () => {
|
||||
describe('when sorting by health status', () => {
|
||||
describe('desc', () => {
|
||||
it('orders from critical to unknown', () => {
|
||||
const sortedItems = orderServiceItems({
|
||||
primarySortField: ServiceInventoryFieldName.HealthStatus,
|
||||
sortDirection: 'desc',
|
||||
tiebreakerField: ServiceInventoryFieldName.Throughput,
|
||||
items: [
|
||||
{
|
||||
serviceName: 'critical-service',
|
||||
healthStatus: ServiceHealthStatus.critical,
|
||||
},
|
||||
{
|
||||
serviceName: 'healthy-service',
|
||||
healthStatus: ServiceHealthStatus.healthy,
|
||||
},
|
||||
{
|
||||
serviceName: 'warning-service',
|
||||
healthStatus: ServiceHealthStatus.warning,
|
||||
},
|
||||
{
|
||||
serviceName: 'unknown-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(sortedItems.map((item) => item.serviceName)).toEqual([
|
||||
'critical-service',
|
||||
'warning-service',
|
||||
'healthy-service',
|
||||
'unknown-service',
|
||||
]);
|
||||
});
|
||||
|
||||
it('sorts by service name ascending as a tie-breaker', () => {
|
||||
const sortedItems = orderServiceItems({
|
||||
primarySortField: ServiceInventoryFieldName.HealthStatus,
|
||||
sortDirection: 'desc',
|
||||
tiebreakerField: ServiceInventoryFieldName.ServiceName,
|
||||
items: [
|
||||
{
|
||||
serviceName: 'b-critical-service',
|
||||
healthStatus: ServiceHealthStatus.critical,
|
||||
},
|
||||
{
|
||||
serviceName: 'a-critical-service',
|
||||
healthStatus: ServiceHealthStatus.critical,
|
||||
},
|
||||
{
|
||||
serviceName: 'a-unknown-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
},
|
||||
{
|
||||
serviceName: 'b-unknown-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(sortedItems.map((item) => item.serviceName)).toEqual([
|
||||
'a-critical-service',
|
||||
'b-critical-service',
|
||||
'a-unknown-service',
|
||||
'b-unknown-service',
|
||||
]);
|
||||
});
|
||||
|
||||
it('sorts by metric descending as a tie-breaker', () => {
|
||||
const sortedItems = orderServiceItems({
|
||||
primarySortField: ServiceInventoryFieldName.HealthStatus,
|
||||
sortDirection: 'desc',
|
||||
tiebreakerField: ServiceInventoryFieldName.Throughput,
|
||||
items: [
|
||||
{
|
||||
serviceName: 'low-throughput-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
throughput: 1,
|
||||
},
|
||||
{
|
||||
serviceName: 'high-throughput-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
throughput: 100,
|
||||
},
|
||||
{
|
||||
serviceName: 'med-throughput-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
throughput: 10,
|
||||
},
|
||||
{
|
||||
serviceName: 'critical-service',
|
||||
healthStatus: ServiceHealthStatus.critical,
|
||||
throughput: 0,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(sortedItems.map((item) => item.serviceName)).toEqual([
|
||||
'critical-service',
|
||||
'high-throughput-service',
|
||||
'med-throughput-service',
|
||||
'low-throughput-service',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('asc', () => {
|
||||
it('orders from unknown to critical', () => {
|
||||
const sortedItems = orderServiceItems({
|
||||
primarySortField: ServiceInventoryFieldName.HealthStatus,
|
||||
sortDirection: 'asc',
|
||||
tiebreakerField: ServiceInventoryFieldName.Throughput,
|
||||
items: [
|
||||
{
|
||||
serviceName: 'critical-service',
|
||||
healthStatus: ServiceHealthStatus.critical,
|
||||
},
|
||||
{
|
||||
serviceName: 'healthy-service',
|
||||
healthStatus: ServiceHealthStatus.healthy,
|
||||
},
|
||||
{
|
||||
serviceName: 'warning-service',
|
||||
healthStatus: ServiceHealthStatus.warning,
|
||||
},
|
||||
{
|
||||
serviceName: 'unknown-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(sortedItems.map((item) => item.serviceName)).toEqual([
|
||||
'unknown-service',
|
||||
'healthy-service',
|
||||
'warning-service',
|
||||
'critical-service',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when sorting by metric fields', () => {
|
||||
it('sorts correctly', () => {
|
||||
const sortedItems = orderServiceItems({
|
||||
primarySortField: ServiceInventoryFieldName.Throughput,
|
||||
sortDirection: 'desc',
|
||||
tiebreakerField: ServiceInventoryFieldName.Throughput,
|
||||
items: [
|
||||
{
|
||||
serviceName: 'low-throughput-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
throughput: 1,
|
||||
},
|
||||
{
|
||||
serviceName: 'high-throughput-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
throughput: 100,
|
||||
},
|
||||
{
|
||||
serviceName: 'med-throughput-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
throughput: 10,
|
||||
},
|
||||
{
|
||||
serviceName: 'critical-service',
|
||||
healthStatus: ServiceHealthStatus.critical,
|
||||
throughput: 0,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(sortedItems.map((item) => item.serviceName)).toEqual([
|
||||
'high-throughput-service',
|
||||
'med-throughput-service',
|
||||
'low-throughput-service',
|
||||
'critical-service',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when sorting by alphabetical fields', () => {
|
||||
const sortedItems = orderServiceItems({
|
||||
primarySortField: ServiceInventoryFieldName.ServiceName,
|
||||
sortDirection: 'asc',
|
||||
tiebreakerField: ServiceInventoryFieldName.ServiceName,
|
||||
items: [
|
||||
{
|
||||
serviceName: 'd-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
},
|
||||
{
|
||||
serviceName: 'a-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
},
|
||||
{
|
||||
serviceName: 'b-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
},
|
||||
{
|
||||
serviceName: 'c-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
},
|
||||
{
|
||||
serviceName: '0-service',
|
||||
healthStatus: ServiceHealthStatus.unknown,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(sortedItems.map((item) => item.serviceName)).toEqual([
|
||||
'0-service',
|
||||
'a-service',
|
||||
'b-service',
|
||||
'c-service',
|
||||
'd-service',
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 { orderBy } from 'lodash';
|
||||
import { ServiceHealthStatus } from '../../../../../common/service_health_status';
|
||||
import {
|
||||
ServiceListItem,
|
||||
ServiceInventoryFieldName,
|
||||
} from '../../../../../common/service_inventory';
|
||||
|
||||
type SortValueGetter = (item: ServiceListItem) => string | number;
|
||||
|
||||
const SERVICE_HEALTH_STATUS_ORDER = [
|
||||
ServiceHealthStatus.unknown,
|
||||
ServiceHealthStatus.healthy,
|
||||
ServiceHealthStatus.warning,
|
||||
ServiceHealthStatus.critical,
|
||||
];
|
||||
|
||||
const sorts: Record<ServiceInventoryFieldName, SortValueGetter> = {
|
||||
[ServiceInventoryFieldName.HealthStatus]: (item) =>
|
||||
item.healthStatus
|
||||
? SERVICE_HEALTH_STATUS_ORDER.indexOf(item.healthStatus)
|
||||
: -1,
|
||||
[ServiceInventoryFieldName.ServiceName]: (item) =>
|
||||
item.serviceName.toLowerCase(),
|
||||
[ServiceInventoryFieldName.Environments]: (item) =>
|
||||
item.environments?.join(', ').toLowerCase() ?? '',
|
||||
[ServiceInventoryFieldName.TransactionType]: (item) =>
|
||||
item.transactionType ?? '',
|
||||
[ServiceInventoryFieldName.Latency]: (item) => item.latency ?? -1,
|
||||
[ServiceInventoryFieldName.Throughput]: (item) => item.throughput ?? -1,
|
||||
[ServiceInventoryFieldName.TransactionErrorRate]: (item) =>
|
||||
item.transactionErrorRate ?? -1,
|
||||
};
|
||||
|
||||
function reverseSortDirection(sortDirection: 'asc' | 'desc') {
|
||||
return sortDirection === 'asc' ? 'desc' : 'asc';
|
||||
}
|
||||
|
||||
export function orderServiceItems({
|
||||
items,
|
||||
primarySortField,
|
||||
tiebreakerField,
|
||||
sortDirection,
|
||||
}: {
|
||||
items: ServiceListItem[];
|
||||
primarySortField: ServiceInventoryFieldName;
|
||||
tiebreakerField: ServiceInventoryFieldName;
|
||||
sortDirection: 'asc' | 'desc';
|
||||
}): ServiceListItem[] {
|
||||
// For healthStatus, sort items by healthStatus first, then by tie-breaker
|
||||
|
||||
const sortFn = sorts[primarySortField as ServiceInventoryFieldName];
|
||||
|
||||
if (primarySortField === ServiceInventoryFieldName.HealthStatus) {
|
||||
const tiebreakerSortDirection =
|
||||
tiebreakerField === ServiceInventoryFieldName.ServiceName
|
||||
? reverseSortDirection(sortDirection)
|
||||
: sortDirection;
|
||||
|
||||
const tiebreakerSortFn = sorts[tiebreakerField];
|
||||
|
||||
return orderBy(
|
||||
items,
|
||||
[sortFn, tiebreakerSortFn],
|
||||
[sortDirection, tiebreakerSortDirection]
|
||||
);
|
||||
}
|
||||
|
||||
return orderBy(items, sortFn, sortDirection);
|
||||
}
|
|
@ -11,6 +11,7 @@ import { MemoryRouter } from 'react-router-dom';
|
|||
import { CoreStart } from '../../../../../../../../src/core/public';
|
||||
import { createKibanaReactContext } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { ServiceHealthStatus } from '../../../../../common/service_health_status';
|
||||
import { ServiceInventoryFieldName } from '../../../../../common/service_inventory';
|
||||
import type { ApmPluginContextValue } from '../../../../context/apm_plugin/apm_plugin_context';
|
||||
import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context';
|
||||
import { ServiceList } from './';
|
||||
|
@ -59,6 +60,10 @@ export const Example: Story<Args> = (args) => {
|
|||
Example.args = {
|
||||
isLoading: false,
|
||||
items,
|
||||
displayHealthStatus: true,
|
||||
initialSortField: ServiceInventoryFieldName.HealthStatus,
|
||||
initialSortDirection: 'desc',
|
||||
sortFn: (sortItems) => sortItems,
|
||||
};
|
||||
|
||||
export const EmptyState: Story<Args> = (args) => {
|
||||
|
@ -67,6 +72,10 @@ export const EmptyState: Story<Args> = (args) => {
|
|||
EmptyState.args = {
|
||||
isLoading: false,
|
||||
items: [],
|
||||
displayHealthStatus: true,
|
||||
initialSortField: ServiceInventoryFieldName.HealthStatus,
|
||||
initialSortDirection: 'desc',
|
||||
sortFn: (sortItems) => sortItems,
|
||||
};
|
||||
|
||||
export const WithHealthWarnings: Story<Args> = (args) => {
|
||||
|
|
|
@ -15,6 +15,7 @@ export {
|
|||
enableComparisonByDefault,
|
||||
enableInfrastructureView,
|
||||
defaultApmServiceEnvironment,
|
||||
apmServiceInventoryOptimizedSorting,
|
||||
} from './ui_settings_keys';
|
||||
|
||||
export const casesFeatureId = 'observabilityCases';
|
||||
|
|
|
@ -11,3 +11,5 @@ export const enableComparisonByDefault = 'observability:enableComparisonByDefaul
|
|||
export const enableInfrastructureView = 'observability:enableInfrastructureView';
|
||||
export const defaultApmServiceEnvironment = 'observability:apmDefaultServiceEnvironment';
|
||||
export const enableServiceGroups = 'observability:enableServiceGroups';
|
||||
export const apmServiceInventoryOptimizedSorting =
|
||||
'observability:apmServiceInventoryOptimizedSorting';
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
enableInfrastructureView,
|
||||
defaultApmServiceEnvironment,
|
||||
enableServiceGroups,
|
||||
apmServiceInventoryOptimizedSorting,
|
||||
} from '../common/ui_settings_keys';
|
||||
|
||||
const technicalPreviewLabel = i18n.translate(
|
||||
|
@ -98,4 +99,22 @@ export const uiSettings: Record<string, UiSettingsParams<boolean | number | stri
|
|||
schema: schema.boolean(),
|
||||
requiresPageReload: true,
|
||||
},
|
||||
[apmServiceInventoryOptimizedSorting]: {
|
||||
category: [observabilityFeatureId],
|
||||
name: i18n.translate('xpack.observability.apmServiceInventoryOptimizedSorting', {
|
||||
defaultMessage: 'Optimize APM Service Inventory page load performance',
|
||||
}),
|
||||
description: i18n.translate(
|
||||
'xpack.observability.apmServiceInventoryOptimizedSortingDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'{technicalPreviewLabel} Default APM Service Inventory page sort (for Services without Machine Learning applied) to sort by Service Name',
|
||||
values: { technicalPreviewLabel: `<em>[${technicalPreviewLabel}]</em>` },
|
||||
}
|
||||
),
|
||||
schema: schema.boolean(),
|
||||
value: false,
|
||||
requiresPageReload: false,
|
||||
type: 'boolean',
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue