diff --git a/x-pack/plugins/infra/public/common/visualizations/constants.ts b/x-pack/plugins/infra/public/common/visualizations/constants.ts
index 66f0cb1e1c422..a91a29e2c44e3 100644
--- a/x-pack/plugins/infra/public/common/visualizations/constants.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/constants.ts
@@ -20,7 +20,6 @@ import {
tx,
hostCount,
} from './lens/formulas/host';
-import { LineChart, MetricChart } from './lens/visualization_types';
export const hostLensFormulas = {
cpuUsage,
@@ -38,9 +37,4 @@ export const hostLensFormulas = {
tx,
};
-export const visualizationTypes = {
- lineChart: LineChart,
- metricChart: MetricChart,
-};
-
export const HOST_METRICS_DOC_HREF = 'https://ela.st/docs-infra-host-metrics';
diff --git a/x-pack/plugins/infra/public/common/visualizations/index.ts b/x-pack/plugins/infra/public/common/visualizations/index.ts
index 9be4015a80a73..888bf3d8f8ad6 100644
--- a/x-pack/plugins/infra/public/common/visualizations/index.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/index.ts
@@ -7,16 +7,16 @@
export type {
HostsLensFormulas,
- LineChartOptions,
- LensChartConfig,
- LensLineChartConfig,
- MetricChartOptions,
HostsLensMetricChartFormulas,
HostsLensLineChartFormulas,
- LensOptions,
LensAttributes,
+ FormulaConfig,
+ Chart,
+ LensVisualizationState,
} from './types';
-export { hostLensFormulas, visualizationTypes } from './constants';
+export { hostLensFormulas } from './constants';
+
+export * from './lens/visualization_types';
export { LensAttributesBuilder } from './lens/lens_attributes_builder';
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/README.md b/x-pack/plugins/infra/public/common/visualizations/lens/README.md
new file mode 100644
index 0000000000000..c0fa8340f180e
--- /dev/null
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/README.md
@@ -0,0 +1,166 @@
+
+# Lens Attributes Builder
+
+The Lens Attributes Builder is a utility for creating JSON objects used to render charts with Lens. It simplifies the process of configuring and building the necessary attributes for different chart types.
+
+## Usage
+
+### Creating a Metric Chart
+
+To create a metric chart, use the `MetricChart` class and provide the required configuration. Here's an example:
+
+```ts
+const metricChart = new MetricChart({
+ layers: new MetricLayer({
+ data: {
+ label: 'Disk Read Throughput',
+ value: "counter_rate(max(system.diskio.read.count), kql='system.diskio.read.count: *')",
+ format: {
+ id: 'bytes',
+ params: {
+ decimals: 1,
+ },
+ },
+ },
+ formulaAPI,
+ }),
+ dataView,
+});
+```
+
+### Creating an XY Chart
+
+To create an XY chart, use the `XYChart` class and provide the required configuration. Here's an example:
+
+```ts
+const xyChart = new XYChart({
+ layers: [new XYDataLayer({
+ data: [{
+ label: 'Normalized Load',
+ value: "average(system.load.1) / max(system.load.cores)",
+ format: {
+ id: 'percent',
+ params: {
+ decimals: 1,
+ },
+ },
+ }],
+ formulaAPI,
+ })],
+ dataView,
+});
+```
+
+### Adding Multiple Layers to an XY Chart
+
+An XY chart can have multiple layers. Here's an example of containing a Reference Line Layer:
+
+```ts
+const xyChart = new XYChart({
+ layers: [
+ new XYDataLayer({
+ data: [{
+ label: 'Disk Read Throughput',
+ value: "average(system.load.1) / max(system.load.cores)",
+ format: {
+ id: 'percent',
+ params: {
+ decimals: 1,
+ },
+ },
+ }],
+ formulaAPI,
+ }),
+ new XYReferenceLineLayer({
+ data: [{
+ value: "1",
+ format: {
+ id: 'percent',
+ params: {
+ decimals: 1,
+ },
+ },
+ }],
+ }),
+ ],
+ dataView,
+});
+```
+
+### Adding Multiple Data Sources in the Same Layer
+
+In an XY chart, it's possible to define multiple data sources within the same layer.
+
+To configure multiple data sources in an XY data layer, simply provide an array of data to the same YXDataLayer class:
+
+```ts
+const xyChart = new XYChart({
+ layers: new YXDataLayer({
+ data: [{
+ label: 'RX',
+ value: "average(host.network.ingress.bytes) * 8 / (max(metricset.period, kql='host.network.ingress.bytes: *') / 1000)",
+ format: {
+ id: 'bits',
+ params: {
+ decimals: 1,
+ },
+ },
+ },{
+ label: 'TX',
+ value: "(average(host.network.egresss.bytes) * 8 / (max(metricset.period, kql='host.network.egresss.bytes: *') / 1000)",
+ format: {
+ id: 'bits',
+ params: {
+ decimals: 1,
+ },
+ },
+ }],
+ formulaAPI,
+ }),
+ dataView,
+});
+```
+
+### Building Lens Chart Attributes
+
+The `LensAttributesBuilder` is responsible for creating the full JSON object that combines the attributes returned by the chart classes. Here's an example:
+
+```ts
+const builder = new LensAttributesBuilder({ visualization: xyChart });
+const attributes = builder.build();
+```
+
+The `attributes` object contains the final JSON representation of the chart configuration and can be used to render the chart with Lens.
+
+### Usage with Lens EmbeddableComponent
+
+To display the charts rendered with the Lens Attributes Builder, it's recommended to use the Lens `EmbeddableComponent`. The `EmbeddableComponent` abstracts some of the chart styling and other details that would be challenging to handle directly with the Lens Attributes Builder.
+
+```tsx
+const builder = new LensAttributesBuilder({
+ visualization: new MetricChart({
+ layers: new MetricLayer({
+ data: {
+ label: 'Disk Read Throughput',
+ value: "counter_rate(max(system.diskio.read.count), kql='system.diskio.read.count: *')",
+ format: {
+ id: 'bytes',
+ params: {
+ decimals: 1,
+ },
+ },
+ },
+ formulaAPI,
+ }),
+ dataView,
+ })
+});
+
+const lensAttributes = builder.build();
+
+
+```
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/host/kpi_grid_config.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/host/kpi_grid_config.ts
new file mode 100644
index 0000000000000..cc4f51c8f2d18
--- /dev/null
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/host/kpi_grid_config.ts
@@ -0,0 +1,115 @@
+/*
+ * 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 { TypedLensByValueInput } from '@kbn/lens-plugin/public';
+import { i18n } from '@kbn/i18n';
+import { Layer } from '../../../../../hooks/use_lens_attributes';
+import { hostLensFormulas } from '../../../constants';
+import { FormulaConfig } from '../../../types';
+import { TOOLTIP } from './translations';
+import { MetricLayerOptions } from '../../visualization_types/layers';
+
+export interface KPIChartProps
+ extends Pick {
+ layers: Layer;
+ toolTip: string;
+}
+
+export const KPI_CHARTS: KPIChartProps[] = [
+ {
+ id: 'cpuUsage',
+ title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpuUsage.title', {
+ defaultMessage: 'CPU Usage',
+ }),
+ layers: {
+ data: {
+ ...hostLensFormulas.cpuUsage,
+ format: {
+ ...hostLensFormulas.cpuUsage.format,
+ params: {
+ decimals: 1,
+ },
+ },
+ },
+ layerType: 'data',
+ options: {
+ backgroundColor: '#F1D86F',
+ showTrendLine: true,
+ },
+ },
+ toolTip: TOOLTIP.cpuUsage,
+ },
+ {
+ id: 'normalizedLoad1m',
+ title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.normalizedLoad1m.title', {
+ defaultMessage: 'CPU Usage',
+ }),
+ layers: {
+ data: {
+ ...hostLensFormulas.normalizedLoad1m,
+ format: {
+ ...hostLensFormulas.normalizedLoad1m.format,
+ params: {
+ decimals: 1,
+ },
+ },
+ },
+ layerType: 'data',
+ options: {
+ backgroundColor: '#79AAD9',
+ showTrendLine: true,
+ },
+ },
+ toolTip: TOOLTIP.normalizedLoad1m,
+ },
+ {
+ id: 'memoryUsage',
+ title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.memoryUsage.title', {
+ defaultMessage: 'CPU Usage',
+ }),
+ layers: {
+ data: {
+ ...hostLensFormulas.memoryUsage,
+ format: {
+ ...hostLensFormulas.memoryUsage.format,
+ params: {
+ decimals: 1,
+ },
+ },
+ },
+ layerType: 'data',
+ options: {
+ backgroundColor: '#A987D1',
+ showTrendLine: true,
+ },
+ },
+ toolTip: TOOLTIP.memoryUsage,
+ },
+ {
+ id: 'diskSpaceUsage',
+ title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.diskSpaceUsage.title', {
+ defaultMessage: 'CPU Usage',
+ }),
+ layers: {
+ data: {
+ ...hostLensFormulas.diskSpaceUsage,
+ format: {
+ ...hostLensFormulas.diskSpaceUsage.format,
+ params: {
+ decimals: 1,
+ },
+ },
+ },
+ layerType: 'data',
+ options: {
+ backgroundColor: '#F5A35C',
+ showTrendLine: true,
+ },
+ },
+ toolTip: TOOLTIP.diskSpaceUsage,
+ },
+];
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/translations.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/host/translations.ts
similarity index 100%
rename from x-pack/plugins/infra/public/common/visualizations/lens/translations.ts
rename to x-pack/plugins/infra/public/common/visualizations/lens/dashboards/host/translations.ts
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage.ts
index f0e8813d2aef4..b3b40f585d7e0 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage.ts
@@ -5,32 +5,15 @@
* 2.0.
*/
-import type { LensChartConfig, LensLineChartConfig } from '../../../types';
-import { getFilters } from './utils';
+import type { FormulaConfig } from '../../../types';
-export const cpuLineChart: LensLineChartConfig = {
- extraVisualizationState: {
- yLeftExtent: {
- mode: 'custom',
- lowerBound: 0,
- upperBound: 1,
+export const cpuUsage: FormulaConfig = {
+ label: 'CPU Usage',
+ value: '(average(system.cpu.user.pct) + average(system.cpu.system.pct)) / max(system.cpu.cores)',
+ format: {
+ id: 'percent',
+ params: {
+ decimals: 0,
},
},
};
-
-export const cpuUsage: LensChartConfig = {
- title: 'CPU Usage',
- formula: {
- formula:
- '(average(system.cpu.user.pct) + average(system.cpu.system.pct)) / max(system.cpu.cores)',
- format: {
- id: 'percent',
- params: {
- decimals: 0,
- },
- },
- },
- getFilters,
-
- lineChartConfig: cpuLineChart,
-};
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_iops.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_iops.ts
index 59879681b641c..27b288f3a119e 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_iops.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_iops.ts
@@ -5,19 +5,15 @@
* 2.0.
*/
-import type { LensChartConfig } from '../../../types';
-import { getFilters } from './utils';
+import type { FormulaConfig } from '../../../types';
-export const diskIORead: LensChartConfig = {
- title: 'Disk Read IOPS',
- formula: {
- formula: "counter_rate(max(system.diskio.read.count), kql='system.diskio.read.count: *')",
- format: {
- id: 'number',
- params: {
- decimals: 0,
- },
+export const diskIORead: FormulaConfig = {
+ label: 'Disk Read IOPS',
+ value: "counter_rate(max(system.diskio.read.count), kql='system.diskio.read.count: *')",
+ format: {
+ id: 'number',
+ params: {
+ decimals: 0,
},
},
- getFilters,
};
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_throughput.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_throughput.ts
index 557a6ec0249e7..946e26cec62a1 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_throughput.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_throughput.ts
@@ -5,19 +5,15 @@
* 2.0.
*/
-import type { LensChartConfig } from '../../../types';
-import { getFilters } from './utils';
+import type { FormulaConfig } from '../../../types';
-export const diskReadThroughput: LensChartConfig = {
- title: 'Disk Read Throughput',
- formula: {
- formula: "counter_rate(max(system.diskio.read.count), kql='system.diskio.read.count: *')",
- format: {
- id: 'bytes',
- params: {
- decimals: 1,
- },
+export const diskReadThroughput: FormulaConfig = {
+ label: 'Disk Read Throughput',
+ value: "counter_rate(max(system.diskio.read.count), kql='system.diskio.read.count: *')",
+ format: {
+ id: 'bytes',
+ params: {
+ decimals: 1,
},
},
- getFilters,
};
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_available.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_available.ts
index 77fcabf3c710f..088e28799ce03 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_available.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_available.ts
@@ -5,19 +5,15 @@
* 2.0.
*/
-import type { LensChartConfig } from '../../../types';
-import { getFilters } from './utils';
+import type { FormulaConfig } from '../../../types';
-export const diskSpaceAvailable: LensChartConfig = {
- title: 'Disk Space Available',
- formula: {
- formula: 'average(system.filesystem.free)',
- format: {
- id: 'bytes',
- params: {
- decimals: 0,
- },
+export const diskSpaceAvailable: FormulaConfig = {
+ label: 'Disk Space Available',
+ value: 'average(system.filesystem.free)',
+ format: {
+ id: 'bytes',
+ params: {
+ decimals: 0,
},
},
- getFilters,
};
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_usage.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_usage.ts
index 9599e65d4de9b..e4cb5851d5241 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_usage.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_usage.ts
@@ -5,30 +5,15 @@
* 2.0.
*/
-import type { LensChartConfig, LensLineChartConfig } from '../../../types';
-import { getFilters } from './utils';
+import type { FormulaConfig } from '../../../types';
-export const diskSpaceUsageLineChart: LensLineChartConfig = {
- extraVisualizationState: {
- yLeftExtent: {
- mode: 'custom',
- lowerBound: 0,
- upperBound: 1,
+export const diskSpaceUsage: FormulaConfig = {
+ label: 'Disk Space Usage',
+ value: 'average(system.filesystem.used.pct)',
+ format: {
+ id: 'percent',
+ params: {
+ decimals: 0,
},
},
};
-
-export const diskSpaceUsage: LensChartConfig = {
- title: 'Disk Space Usage',
- formula: {
- formula: 'average(system.filesystem.used.pct)',
- format: {
- id: 'percent',
- params: {
- decimals: 0,
- },
- },
- },
- getFilters,
- lineChartConfig: diskSpaceUsageLineChart,
-};
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_iops.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_iops.ts
index 67d697551c44a..04370c61903ce 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_iops.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_iops.ts
@@ -5,19 +5,15 @@
* 2.0.
*/
-import type { LensChartConfig } from '../../../types';
-import { getFilters } from './utils';
+import type { FormulaConfig } from '../../../types';
-export const diskIOWrite: LensChartConfig = {
- title: 'Disk Write IOPS',
- formula: {
- formula: "counter_rate(max(system.diskio.write.count), kql='system.diskio.write.count: *')",
- format: {
- id: 'number',
- params: {
- decimals: 0,
- },
+export const diskIOWrite: FormulaConfig = {
+ label: 'Disk Write IOPS',
+ value: "counter_rate(max(system.diskio.write.count), kql='system.diskio.write.count: *')",
+ format: {
+ id: 'number',
+ params: {
+ decimals: 0,
},
},
- getFilters,
};
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_throughput.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_throughput.ts
index 97e223036af7f..e391bce4a1151 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_throughput.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_throughput.ts
@@ -5,19 +5,15 @@
* 2.0.
*/
-import type { LensChartConfig } from '../../../types';
-import { getFilters } from './utils';
+import type { FormulaConfig } from '../../../types';
-export const diskWriteThroughput: LensChartConfig = {
- title: 'Disk Write Throughput',
- formula: {
- formula: "counter_rate(max(system.diskio.write.count), kql='system.diskio.write.count: *')",
- format: {
- id: 'bytes',
- params: {
- decimals: 1,
- },
+export const diskWriteThroughput: FormulaConfig = {
+ label: 'Disk Write Throughput',
+ value: "counter_rate(max(system.diskio.write.count), kql='system.diskio.write.count: *')",
+ format: {
+ id: 'bytes',
+ params: {
+ decimals: 1,
},
},
- getFilters,
};
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/host_count.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/host_count.ts
index 4f0d230176368..e642a8cb629f1 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/host_count.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/host_count.ts
@@ -5,19 +5,15 @@
* 2.0.
*/
-import type { LensChartConfig } from '../../../types';
-import { getFilters } from './utils';
+import type { FormulaConfig } from '../../../types';
-export const hostCount: LensChartConfig = {
- title: 'Hosts',
- formula: {
- formula: 'unique_count(host.name)',
- format: {
- id: 'number',
- params: {
- decimals: 0,
- },
+export const hostCount: FormulaConfig = {
+ label: 'Hosts',
+ value: 'unique_count(host.name)',
+ format: {
+ id: 'number',
+ params: {
+ decimals: 0,
},
},
- getFilters,
};
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_free.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_free.ts
index 7821ef7a12ff1..4406ebd1e820c 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_free.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_free.ts
@@ -5,19 +5,15 @@
* 2.0.
*/
-import type { LensChartConfig } from '../../../types';
-import { getFilters } from './utils';
+import type { FormulaConfig } from '../../../types';
-export const memoryFree: LensChartConfig = {
- title: 'Memory Free',
- formula: {
- formula: 'max(system.memory.total) - average(system.memory.actual.used.bytes)',
- format: {
- id: 'bytes',
- params: {
- decimals: 1,
- },
+export const memoryFree: FormulaConfig = {
+ label: 'Memory Free',
+ value: 'max(system.memory.total) - average(system.memory.actual.used.bytes)',
+ format: {
+ id: 'bytes',
+ params: {
+ decimals: 1,
},
},
- getFilters,
};
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_usage.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_usage.ts
index bc76d88053dd9..f95198756d61e 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_usage.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_usage.ts
@@ -5,30 +5,15 @@
* 2.0.
*/
-import type { LensChartConfig, LensLineChartConfig } from '../../../types';
-import { getFilters } from './utils';
+import type { FormulaConfig } from '../../../types';
-const memoryLineChart: LensLineChartConfig = {
- extraVisualizationState: {
- yLeftExtent: {
- mode: 'custom',
- lowerBound: 0,
- upperBound: 1,
+export const memoryUsage: FormulaConfig = {
+ label: 'Memory Usage',
+ value: 'average(system.memory.actual.used.pct)',
+ format: {
+ id: 'percent',
+ params: {
+ decimals: 0,
},
},
};
-
-export const memoryUsage: LensChartConfig = {
- title: 'Memory Usage',
- formula: {
- formula: 'average(system.memory.actual.used.pct)',
- format: {
- id: 'percent',
- params: {
- decimals: 0,
- },
- },
- },
- lineChartConfig: memoryLineChart,
- getFilters,
-};
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/normalized_load_1m.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/normalized_load_1m.ts
index 5e04919740689..32031d07fb858 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/normalized_load_1m.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/normalized_load_1m.ts
@@ -5,72 +5,15 @@
* 2.0.
*/
-import type { ReferenceBasedIndexPatternColumn } from '@kbn/lens-plugin/public/datasources/form_based/operations/definitions/column_types';
-import type { LensChartConfig, LensLineChartConfig } from '../../../types';
-import { getFilters } from './utils';
+import type { FormulaConfig } from '../../../types';
-const REFERENCE_LAYER = 'referenceLayer';
-
-export const loadLineChart: LensLineChartConfig = {
- extraLayers: {
- [REFERENCE_LAYER]: {
- linkToLayers: [],
- columnOrder: ['referenceColumn'],
- columns: {
- referenceColumn: {
- label: 'Reference',
- dataType: 'number',
- operationType: 'static_value',
- isStaticValue: true,
- isBucketed: false,
- scale: 'ratio',
- params: {
- value: 1,
- format: {
- id: 'percent',
- params: {
- decimals: 0,
- },
- },
- },
- references: [],
- customLabel: true,
- } as ReferenceBasedIndexPatternColumn,
- },
- sampling: 1,
- incompleteColumns: {},
- },
- },
- extraVisualizationState: {
- layers: [
- {
- layerId: REFERENCE_LAYER,
- layerType: 'referenceLine',
- accessors: ['referenceColumn'],
- yConfig: [
- {
- forAccessor: 'referenceColumn',
- axisMode: 'left',
- color: '#6092c0',
- },
- ],
- },
- ],
- },
- extraReference: REFERENCE_LAYER,
-};
-
-export const normalizedLoad1m: LensChartConfig = {
- title: 'Normalized Load',
- formula: {
- formula: 'average(system.load.1) / max(system.load.cores)',
- format: {
- id: 'percent',
- params: {
- decimals: 0,
- },
+export const normalizedLoad1m: FormulaConfig = {
+ label: 'Normalized Load',
+ value: 'average(system.load.1) / max(system.load.cores)',
+ format: {
+ id: 'percent',
+ params: {
+ decimals: 0,
},
},
- getFilters,
- lineChartConfig: loadLineChart,
};
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/rx.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/rx.ts
index b396dffb979e6..2c5da5cb83988 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/rx.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/rx.ts
@@ -5,20 +5,16 @@
* 2.0.
*/
-import type { LensChartConfig } from '../../../types';
-import { getFilters } from './utils';
+import type { FormulaConfig } from '../../../types';
-export const rx: LensChartConfig = {
- title: 'Network Inbound (RX)',
- formula: {
- formula:
- "average(host.network.ingress.bytes) * 8 / (max(metricset.period, kql='host.network.ingress.bytes: *') / 1000)",
- format: {
- id: 'bits',
- params: {
- decimals: 1,
- },
+export const rx: FormulaConfig = {
+ label: 'Network Inbound (RX)',
+ value:
+ "average(host.network.ingress.bytes) * 8 / (max(metricset.period, kql='host.network.ingress.bytes: *') / 1000)",
+ format: {
+ id: 'bits',
+ params: {
+ decimals: 1,
},
},
- getFilters,
};
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/tx.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/tx.ts
index f9f97a8ed9112..70aa43a4efaf0 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/tx.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/tx.ts
@@ -5,20 +5,16 @@
* 2.0.
*/
-import type { LensChartConfig } from '../../../types';
-import { getFilters } from './utils';
+import type { FormulaConfig } from '../../../types';
-export const tx: LensChartConfig = {
- title: 'Network Outbound (TX)',
- formula: {
- formula:
- "average(host.network.egress.bytes) * 8 / (max(metricset.period, kql='host.network.egress.bytes: *') / 1000)",
- format: {
- id: 'bits',
- params: {
- decimals: 1,
- },
+export const tx: FormulaConfig = {
+ label: 'Network Outbound (TX)',
+ value:
+ "average(host.network.egress.bytes) * 8 / (max(metricset.period, kql='host.network.egress.bytes: *') / 1000)",
+ format: {
+ id: 'bits',
+ params: {
+ decimals: 1,
},
},
- getFilters,
};
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/utils.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/utils.ts
deleted file mode 100644
index c0b16ca705b13..0000000000000
--- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/utils.ts
+++ /dev/null
@@ -1,21 +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 { DataViewBase } from '@kbn/es-query';
-
-export const getFilters = ({ id }: Pick) => [
- {
- meta: {
- index: id,
- },
- query: {
- exists: {
- field: 'host.name',
- },
- },
- },
-];
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/kpi_grid_config.ts b/x-pack/plugins/infra/public/common/visualizations/lens/kpi_grid_config.ts
deleted file mode 100644
index 6849bceaa7148..0000000000000
--- a/x-pack/plugins/infra/public/common/visualizations/lens/kpi_grid_config.ts
+++ /dev/null
@@ -1,61 +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 { i18n } from '@kbn/i18n';
-import { HostsLensMetricChartFormulas } from '../types';
-import { TOOLTIP } from './translations';
-
-export interface KPIChartProps {
- title: string;
- subtitle?: string;
- trendLine?: boolean;
- backgroundColor: string;
- type: HostsLensMetricChartFormulas;
- decimals?: number;
- toolTip: string;
-}
-
-export const KPI_CHARTS: Array> = [
- {
- type: 'cpuUsage',
- trendLine: true,
- backgroundColor: '#F1D86F',
- title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpuUsage.title', {
- defaultMessage: 'CPU Usage',
- }),
- toolTip: TOOLTIP.cpuUsage,
- },
- {
- type: 'normalizedLoad1m',
- trendLine: true,
- backgroundColor: '#79AAD9',
- title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.normalizedLoad1m.title', {
- defaultMessage: 'Normalized Load',
- }),
- toolTip: TOOLTIP.rx,
- },
- {
- type: 'memoryUsage',
- trendLine: true,
- backgroundColor: '#A987D1',
- title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.memoryUsage.title', {
- defaultMessage: 'Memory Usage',
- }),
- toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.memoryUsage.tooltip', {
- defaultMessage: 'Main memory usage excluding page cache.',
- }),
- },
- {
- type: 'diskSpaceUsage',
- trendLine: true,
- backgroundColor: '#F5A35C',
- title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.diskSpaceUsage.title', {
- defaultMessage: 'Disk Space Usage',
- }),
- toolTip: TOOLTIP.tx,
- },
-];
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/lens_attributes_builder.test.ts b/x-pack/plugins/infra/public/common/visualizations/lens/lens_attributes_builder.test.ts
new file mode 100644
index 0000000000000..14d79c41c3829
--- /dev/null
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/lens_attributes_builder.test.ts
@@ -0,0 +1,359 @@
+/*
+ * 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 'jest-canvas-mock';
+
+import type { DataView } from '@kbn/data-views-plugin/public';
+import { lensPluginMock } from '@kbn/lens-plugin/public/mocks';
+import { LensAttributesBuilder } from './lens_attributes_builder';
+import {
+ MetricChart,
+ MetricLayer,
+ XYChart,
+ XYDataLayer,
+ XYReferenceLinesLayer,
+} from './visualization_types';
+import type { FormulaPublicApi, GenericIndexPatternColumn } from '@kbn/lens-plugin/public';
+import { ReferenceBasedIndexPatternColumn } from '@kbn/lens-plugin/public/datasources/form_based/operations/definitions/column_types';
+import type { FormulaConfig } from '../types';
+
+const mockDataView = {
+ id: 'mock-id',
+ title: 'mock-title',
+ timeFieldName: '@timestamp',
+ isPersisted: () => false,
+ getName: () => 'mock-data-view',
+ toSpec: () => ({}),
+ fields: [],
+ metaFields: [],
+} as unknown as jest.Mocked;
+
+const lensPluginMockStart = lensPluginMock.createStartContract();
+
+const getDataLayer = (formula: string): GenericIndexPatternColumn => ({
+ customLabel: false,
+ dataType: 'number',
+ filter: undefined,
+ isBucketed: false,
+ label: formula,
+ operationType: 'formula',
+ params: {
+ format: {
+ id: 'percent',
+ },
+ formula,
+ isFormulaBroken: true,
+ } as any,
+ reducedTimeRange: undefined,
+ references: [],
+ timeScale: undefined,
+});
+
+const getHistogramLayer = (interval: string, includeEmptyRows?: boolean) => ({
+ dataType: 'date',
+ isBucketed: true,
+ label: '@timestamp',
+ operationType: 'date_histogram',
+ params: includeEmptyRows
+ ? {
+ includeEmptyRows,
+ interval,
+ }
+ : { interval },
+ scale: 'interval',
+ sourceField: '@timestamp',
+});
+
+const REFERENCE_LINE_LAYER: ReferenceBasedIndexPatternColumn = {
+ customLabel: true,
+ dataType: 'number',
+ isBucketed: false,
+ isStaticValue: true,
+ label: 'Reference',
+ operationType: 'static_value',
+ params: {
+ format: {
+ id: 'percent',
+ },
+ value: '1',
+ } as any,
+ references: [],
+ scale: 'ratio',
+};
+
+const getFormula = (value: string): FormulaConfig => ({
+ value,
+ format: {
+ id: 'percent',
+ },
+});
+
+const AVERAGE_CPU_USER_FORMULA = 'average(system.cpu.user.pct)';
+const AVERAGE_CPU_SYSTEM_FORMULA = 'average(system.cpu.system.pct)';
+
+describe('lens_attributes_builder', () => {
+ let formulaAPI: FormulaPublicApi;
+ beforeAll(async () => {
+ formulaAPI = (await lensPluginMockStart.stateHelperApi()).formula;
+ });
+
+ describe('MetricChart', () => {
+ it('should build MetricChart', async () => {
+ const metriChart = new MetricChart({
+ layers: new MetricLayer({
+ data: getFormula(AVERAGE_CPU_USER_FORMULA),
+ formulaAPI,
+ }),
+
+ dataView: mockDataView,
+ });
+ const builder = new LensAttributesBuilder({ visualization: metriChart });
+ const {
+ state: {
+ datasourceStates: {
+ formBased: { layers },
+ },
+ visualization,
+ },
+ } = builder.build();
+
+ expect(layers).toEqual({
+ layer: {
+ columnOrder: ['metric_formula_accessor'],
+ columns: {
+ metric_formula_accessor: getDataLayer(AVERAGE_CPU_USER_FORMULA),
+ },
+ indexPatternId: 'mock-id',
+ },
+ });
+
+ expect(visualization).toEqual({
+ color: undefined,
+ layerId: 'layer',
+ layerType: 'data',
+ metricAccessor: 'metric_formula_accessor',
+ showBar: false,
+ subtitle: undefined,
+ });
+ });
+
+ it('should build MetricChart with trendline', async () => {
+ const metriChart = new MetricChart({
+ layers: new MetricLayer({
+ data: getFormula(AVERAGE_CPU_USER_FORMULA),
+ options: {
+ showTrendLine: true,
+ },
+ formulaAPI,
+ }),
+
+ dataView: mockDataView,
+ });
+ const builder = new LensAttributesBuilder({ visualization: metriChart });
+ const {
+ state: {
+ datasourceStates: {
+ formBased: { layers },
+ },
+ visualization,
+ },
+ } = builder.build();
+
+ expect(layers).toEqual({
+ layer: {
+ columnOrder: ['metric_formula_accessor'],
+ columns: {
+ metric_formula_accessor: getDataLayer(AVERAGE_CPU_USER_FORMULA),
+ },
+ indexPatternId: 'mock-id',
+ },
+ layer_trendline: {
+ columnOrder: ['x_date_histogram', 'metric_formula_accessor_trendline'],
+ columns: {
+ metric_formula_accessor_trendline: getDataLayer(AVERAGE_CPU_USER_FORMULA),
+ x_date_histogram: getHistogramLayer('auto', true),
+ },
+ indexPatternId: 'mock-id',
+ linkToLayers: ['layer'],
+ sampling: 1,
+ },
+ });
+
+ expect(visualization).toEqual({
+ color: undefined,
+ layerId: 'layer',
+ layerType: 'data',
+ metricAccessor: 'metric_formula_accessor',
+ showBar: false,
+ subtitle: undefined,
+ trendlineLayerId: 'layer_trendline',
+ trendlineLayerType: 'metricTrendline',
+ trendlineMetricAccessor: 'metric_formula_accessor_trendline',
+ trendlineTimeAccessor: 'x_date_histogram',
+ });
+ });
+ });
+
+ describe('XYChart', () => {
+ it('should build XYChart', async () => {
+ const xyChart = new XYChart({
+ layers: [
+ new XYDataLayer({
+ data: [getFormula(AVERAGE_CPU_USER_FORMULA)],
+ formulaAPI,
+ }),
+ ],
+ dataView: mockDataView,
+ });
+ const builder = new LensAttributesBuilder({ visualization: xyChart });
+ const {
+ state: {
+ datasourceStates: {
+ formBased: { layers },
+ },
+ visualization,
+ },
+ } = builder.build();
+
+ expect(layers).toEqual({
+ layer_0: {
+ columnOrder: ['x_date_histogram', 'formula_accessor_0_0'],
+ columns: {
+ x_date_histogram: getHistogramLayer('auto'),
+ formula_accessor_0_0: getDataLayer(AVERAGE_CPU_USER_FORMULA),
+ },
+ indexPatternId: 'mock-id',
+ },
+ });
+
+ expect((visualization as any).layers).toEqual([
+ {
+ accessors: ['formula_accessor_0_0'],
+ layerId: 'layer_0',
+ layerType: 'data',
+ seriesType: 'line',
+ splitAccessor: 'aggs_breakdown',
+ xAccessor: 'x_date_histogram',
+ yConfig: [],
+ },
+ ]);
+ });
+
+ it('should build XYChart with Reference Line layer', async () => {
+ const xyChart = new XYChart({
+ layers: [
+ new XYDataLayer({
+ data: [getFormula(AVERAGE_CPU_USER_FORMULA)],
+ formulaAPI,
+ }),
+ new XYReferenceLinesLayer({
+ data: [getFormula('1')],
+ }),
+ ],
+ dataView: mockDataView,
+ });
+ const builder = new LensAttributesBuilder({ visualization: xyChart });
+ const {
+ state: {
+ datasourceStates: {
+ formBased: { layers },
+ },
+ visualization,
+ },
+ } = builder.build();
+
+ expect(layers).toEqual({
+ layer_0: {
+ columnOrder: ['x_date_histogram', 'formula_accessor_0_0'],
+ columns: {
+ x_date_histogram: getHistogramLayer('auto'),
+ formula_accessor_0_0: getDataLayer(AVERAGE_CPU_USER_FORMULA),
+ },
+ indexPatternId: 'mock-id',
+ },
+ layer_1_reference: {
+ columnOrder: ['formula_accessor_1_0_reference_column'],
+ columns: {
+ formula_accessor_1_0_reference_column: REFERENCE_LINE_LAYER,
+ },
+ incompleteColumns: {},
+ linkToLayers: [],
+ sampling: 1,
+ },
+ });
+
+ expect((visualization as any).layers).toEqual([
+ {
+ accessors: ['formula_accessor_0_0'],
+ layerId: 'layer_0',
+ layerType: 'data',
+ seriesType: 'line',
+ splitAccessor: 'aggs_breakdown',
+ xAccessor: 'x_date_histogram',
+ yConfig: [],
+ },
+ {
+ accessors: ['formula_accessor_1_0_reference_column'],
+ layerId: 'layer_1_reference',
+ layerType: 'referenceLine',
+ yConfig: [
+ {
+ axisMode: 'left',
+ color: undefined,
+ forAccessor: 'formula_accessor_1_0_reference_column',
+ },
+ ],
+ },
+ ]);
+ });
+
+ it('should build XYChart with multiple data columns', async () => {
+ const xyChart = new XYChart({
+ layers: [
+ new XYDataLayer({
+ data: [getFormula(AVERAGE_CPU_USER_FORMULA), getFormula(AVERAGE_CPU_SYSTEM_FORMULA)],
+ formulaAPI,
+ }),
+ ],
+ dataView: mockDataView,
+ });
+ const builder = new LensAttributesBuilder({ visualization: xyChart });
+ const {
+ state: {
+ datasourceStates: {
+ formBased: { layers },
+ },
+ visualization,
+ },
+ } = builder.build();
+
+ expect(layers).toEqual({
+ layer_0: {
+ columnOrder: ['x_date_histogram', 'formula_accessor_0_0', 'formula_accessor_0_1'],
+ columns: {
+ x_date_histogram: getHistogramLayer('auto'),
+ formula_accessor_0_0: getDataLayer(AVERAGE_CPU_USER_FORMULA),
+ formula_accessor_0_1: getDataLayer(AVERAGE_CPU_SYSTEM_FORMULA),
+ },
+ indexPatternId: 'mock-id',
+ },
+ });
+
+ expect((visualization as any).layers).toEqual([
+ {
+ accessors: ['formula_accessor_0_0', 'formula_accessor_0_1'],
+ layerId: 'layer_0',
+ layerType: 'data',
+ seriesType: 'line',
+ splitAccessor: 'aggs_breakdown',
+ xAccessor: 'x_date_histogram',
+ yConfig: [],
+ },
+ ]);
+ });
+ });
+});
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/lens_attributes_builder.ts b/x-pack/plugins/infra/public/common/visualizations/lens/lens_attributes_builder.ts
index 90ff3c5cfa268..d873f074ee346 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/lens_attributes_builder.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/lens_attributes_builder.ts
@@ -6,39 +6,38 @@
*/
import type {
LensAttributes,
- TVisualization,
- VisualizationAttributes,
+ LensVisualizationState,
+ Chart,
VisualizationAttributesBuilder,
} from '../types';
import { DataViewCache } from './data_view_cache';
import { getAdhocDataView } from './utils';
-export class LensAttributesBuilder>
+export class LensAttributesBuilder>
implements VisualizationAttributesBuilder
{
private dataViewCache: DataViewCache;
- constructor(private visualization: T) {
+ constructor(private state: { visualization: T }) {
this.dataViewCache = DataViewCache.getInstance();
}
build(): LensAttributes {
+ const { visualization } = this.state;
return {
- title: this.visualization.getTitle(),
- visualizationType: this.visualization.getVisualizationType(),
- references: this.visualization.getReferences(),
+ title: visualization.getTitle(),
+ visualizationType: visualization.getVisualizationType(),
+ references: visualization.getReferences(),
state: {
datasourceStates: {
formBased: {
- layers: this.visualization.getLayers(),
+ layers: visualization.getLayers(),
},
},
- internalReferences: this.visualization.getReferences(),
- filters: this.visualization.getFilters(),
+ internalReferences: visualization.getReferences(),
+ filters: [],
query: { language: 'kuery', query: '' },
- visualization: this.visualization.getVisualizationState(),
- adHocDataViews: getAdhocDataView(
- this.dataViewCache.getSpec(this.visualization.getDataView())
- ),
+ visualization: visualization.getVisualizationState(),
+ adHocDataViews: getAdhocDataView(this.dataViewCache.getSpec(visualization.getDataView())),
},
};
}
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/lens_wrapper.tsx b/x-pack/plugins/infra/public/common/visualizations/lens/lens_wrapper.tsx
index 08948d788fbe3..7925966b75429 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/lens_wrapper.tsx
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/lens_wrapper.tsx
@@ -4,30 +4,28 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import React, { useEffect, useState, useRef, useCallback, CSSProperties } from 'react';
+import React, { useEffect, useState, useRef, useCallback } from 'react';
import { Action } from '@kbn/ui-actions-plugin/public';
import { ViewMode } from '@kbn/embeddable-plugin/public';
-import { BrushTriggerEvent } from '@kbn/charts-plugin/public';
-import { Filter, Query, TimeRange } from '@kbn/es-query';
-import { useIntersectedOnce } from '../../../hooks/use_intersection_once';
+import { TimeRange } from '@kbn/es-query';
+import { TypedLensByValueInput } from '@kbn/lens-plugin/public';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
+import { useIntersectedOnce } from '../../../hooks/use_intersection_once';
import { ChartLoader } from './chart_loader';
import type { LensAttributes } from '../types';
-export interface LensWrapperProps {
- id: string;
+export interface LensWrapperProps
+ extends Pick<
+ TypedLensByValueInput,
+ 'id' | 'overrides' | 'query' | 'filters' | 'style' | 'onBrushEnd' | 'onLoad' | 'disableTriggers'
+ > {
attributes: LensAttributes | null;
dateRange: TimeRange;
- query?: Query;
- filters: Filter[];
extraActions: Action[];
lastReloadRequestTime?: number;
- style?: CSSProperties;
loading?: boolean;
hasTitle?: boolean;
- onBrushEnd?: (data: BrushTriggerEvent['data']) => void;
- onLoad?: () => void;
}
export const LensWrapper = React.memo(
@@ -41,8 +39,10 @@ export const LensWrapper = React.memo(
style,
onBrushEnd,
lastReloadRequestTime,
+ overrides,
loading = false,
hasTitle = false,
+ disableTriggers = false,
}: LensWrapperProps) => {
const intersectionRef = useRef(null);
const [loadedOnce, setLoadedOnce] = useState(false);
@@ -103,12 +103,14 @@ export const LensWrapper = React.memo(
)}
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/utils.ts b/x-pack/plugins/infra/public/common/visualizations/lens/utils.ts
index 8bbfd6da833fc..e97e87380cd0a 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/utils.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/utils.ts
@@ -12,8 +12,9 @@ import {
import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/public';
import type { SavedObjectReference } from '@kbn/core-saved-objects-common';
-export const DEFAULT_LAYER_ID = 'layer1';
+export const DEFAULT_LAYER_ID = 'layer';
export const DEFAULT_AD_HOC_DATA_VIEW_ID = 'infra_lens_ad_hoc_default';
+
const DEFAULT_BREAKDOWN_SIZE = 10;
export const getHistogramColumn = ({
@@ -37,7 +38,7 @@ export const getHistogramColumn = ({
};
};
-export const getBreakdownColumn = ({
+export const getTopValuesColumn = ({
columnName,
overrides,
}: {
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/index.ts b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/index.ts
index 4abfeb3a60c45..b7112840436de 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/index.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/index.ts
@@ -5,5 +5,7 @@
* 2.0.
*/
-export { LineChart } from './line_chart';
+export { XYChart } from './xy_chart';
export { MetricChart } from './metric_chart';
+
+export * from './layers';
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/column/formula.ts b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/column/formula.ts
new file mode 100644
index 0000000000000..b1e30ce0bd225
--- /dev/null
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/column/formula.ts
@@ -0,0 +1,38 @@
+/*
+ * 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 type { FormulaPublicApi, PersistedIndexPatternLayer } from '@kbn/lens-plugin/public';
+import type { DataView } from '@kbn/data-views-plugin/public';
+import type { FormulaConfig, ChartColumn } from '../../../../types';
+
+export class FormulaColumn implements ChartColumn {
+ constructor(private formulaConfig: FormulaConfig, private formulaAPI: FormulaPublicApi) {}
+
+ getFormulaConfig(): FormulaConfig {
+ return this.formulaConfig;
+ }
+
+ getData(
+ id: string,
+ baseLayer: PersistedIndexPatternLayer,
+ dataView: DataView
+ ): PersistedIndexPatternLayer {
+ const { value, ...rest } = this.getFormulaConfig();
+ const formulaLayer = this.formulaAPI.insertOrReplaceFormulaColumn(
+ id,
+ { formula: value, ...rest },
+ baseLayer,
+ dataView
+ );
+
+ if (!formulaLayer) {
+ throw new Error('Error generating the data layer for the chart');
+ }
+
+ return formulaLayer;
+ }
+}
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/column/reference_line.ts b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/column/reference_line.ts
new file mode 100644
index 0000000000000..d9f9c5f270997
--- /dev/null
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/column/reference_line.ts
@@ -0,0 +1,41 @@
+/*
+ * 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 type { PersistedIndexPatternLayer } from '@kbn/lens-plugin/public';
+import type { ReferenceBasedIndexPatternColumn } from '@kbn/lens-plugin/public/datasources/form_based/operations/definitions/column_types';
+import type { FormulaConfig, ChartColumn } from '../../../../types';
+
+export class ReferenceLineColumn implements ChartColumn {
+ constructor(private formulaConfig: FormulaConfig) {}
+
+ getFormulaConfig(): FormulaConfig {
+ return this.formulaConfig;
+ }
+
+ getData(id: string, baseLayer: PersistedIndexPatternLayer): PersistedIndexPatternLayer {
+ const { label, ...params } = this.getFormulaConfig();
+ return {
+ linkToLayers: [],
+ columnOrder: [...baseLayer.columnOrder, id],
+ columns: {
+ [id]: {
+ label: label ?? 'Reference',
+ dataType: 'number',
+ operationType: 'static_value',
+ isStaticValue: true,
+ isBucketed: false,
+ scale: 'ratio',
+ params,
+ references: [],
+ customLabel: true,
+ } as ReferenceBasedIndexPatternColumn,
+ },
+ sampling: 1,
+ incompleteColumns: {},
+ };
+ }
+}
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/index.ts b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/index.ts
new file mode 100644
index 0000000000000..fb8300573b79a
--- /dev/null
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/index.ts
@@ -0,0 +1,13 @@
+/*
+ * 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.
+ */
+
+export { MetricLayer, type MetricLayerOptions } from './metric_layer';
+export { XYDataLayer, type XYLayerOptions } from './xy_data_layer';
+export { XYReferenceLinesLayer } from './xy_reference_lines_layer';
+
+export { FormulaColumn as FormulaDataColumn } from './column/formula';
+export { ReferenceLineColumn } from './column/reference_line';
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/metric_layer.ts b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/metric_layer.ts
new file mode 100644
index 0000000000000..25d05abd0f9c5
--- /dev/null
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/metric_layer.ts
@@ -0,0 +1,112 @@
+/*
+ * 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 type { SavedObjectReference } from '@kbn/core-saved-objects-common';
+import type { DataView } from '@kbn/data-views-plugin/common';
+import type {
+ FormulaPublicApi,
+ FormBasedPersistedState,
+ MetricVisualizationState,
+ PersistedIndexPatternLayer,
+} from '@kbn/lens-plugin/public';
+import type { ChartColumn, ChartLayer, FormulaConfig } from '../../../types';
+import { getDefaultReferences, getHistogramColumn } from '../../utils';
+import { FormulaColumn } from './column/formula';
+
+const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
+
+export interface MetricLayerOptions {
+ backgroundColor?: string;
+ showTitle?: boolean;
+ showTrendLine?: boolean;
+ subtitle?: string;
+}
+
+interface MetricLayerConfig {
+ data: FormulaConfig;
+ options?: MetricLayerOptions;
+ formulaAPI: FormulaPublicApi;
+}
+
+export class MetricLayer implements ChartLayer {
+ private column: ChartColumn;
+ constructor(private layerConfig: MetricLayerConfig) {
+ this.column = new FormulaColumn(layerConfig.data, layerConfig.formulaAPI);
+ }
+
+ getLayer(
+ layerId: string,
+ accessorId: string,
+ dataView: DataView
+ ): FormBasedPersistedState['layers'] {
+ const baseLayer: PersistedIndexPatternLayer = {
+ columnOrder: [HISTOGRAM_COLUMN_NAME],
+ columns: getHistogramColumn({
+ columnName: HISTOGRAM_COLUMN_NAME,
+ overrides: {
+ sourceField: dataView.timeFieldName,
+ params: {
+ interval: 'auto',
+ includeEmptyRows: true,
+ },
+ },
+ }),
+ sampling: 1,
+ };
+
+ return {
+ [layerId]: {
+ ...this.column.getData(
+ accessorId,
+ {
+ columnOrder: [],
+ columns: {},
+ },
+ dataView
+ ),
+ },
+ ...(this.layerConfig.options?.showTrendLine
+ ? {
+ [`${layerId}_trendline`]: {
+ linkToLayers: [layerId],
+ ...this.column.getData(`${accessorId}_trendline`, baseLayer, dataView),
+ },
+ }
+ : {}),
+ };
+ }
+ getReference(layerId: string, dataView: DataView): SavedObjectReference[] {
+ return [
+ ...getDefaultReferences(dataView, layerId),
+ ...getDefaultReferences(dataView, `${layerId}_trendline`),
+ ];
+ }
+
+ getLayerConfig(layerId: string, accessorId: string): MetricVisualizationState {
+ const { subtitle, backgroundColor, showTrendLine } = this.layerConfig.options ?? {};
+
+ return {
+ layerId,
+ layerType: 'data',
+ metricAccessor: accessorId,
+ color: backgroundColor,
+ subtitle,
+ showBar: false,
+ ...(showTrendLine
+ ? {
+ trendlineLayerId: `${layerId}_trendline`,
+ trendlineLayerType: 'metricTrendline',
+ trendlineMetricAccessor: `${accessorId}_trendline`,
+ trendlineTimeAccessor: HISTOGRAM_COLUMN_NAME,
+ }
+ : {}),
+ };
+ }
+ getName(): string | undefined {
+ return this.column.getFormulaConfig().label;
+ }
+}
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/xy_data_layer.ts b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/xy_data_layer.ts
new file mode 100644
index 0000000000000..b6a8428ee0754
--- /dev/null
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/xy_data_layer.ts
@@ -0,0 +1,106 @@
+/*
+ * 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 type { SavedObjectReference } from '@kbn/core-saved-objects-common';
+import type { DataView } from '@kbn/data-views-plugin/common';
+import type {
+ FormulaPublicApi,
+ FormBasedPersistedState,
+ PersistedIndexPatternLayer,
+ XYDataLayerConfig,
+} from '@kbn/lens-plugin/public';
+import type { ChartColumn, ChartLayer, FormulaConfig } from '../../../types';
+import { getDefaultReferences, getHistogramColumn, getTopValuesColumn } from '../../utils';
+import { FormulaColumn } from './column/formula';
+
+const BREAKDOWN_COLUMN_NAME = 'aggs_breakdown';
+const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
+
+export interface XYLayerOptions {
+ breakdown?: {
+ size: number;
+ sourceField: string;
+ };
+}
+
+interface XYLayerConfig {
+ data: FormulaConfig[];
+ options?: XYLayerOptions;
+ formulaAPI: FormulaPublicApi;
+}
+
+export class XYDataLayer implements ChartLayer {
+ private column: ChartColumn[];
+ constructor(private layerConfig: XYLayerConfig) {
+ this.column = layerConfig.data.map((p) => new FormulaColumn(p, layerConfig.formulaAPI));
+ }
+
+ getName(): string | undefined {
+ return this.column[0].getFormulaConfig().label;
+ }
+
+ getBaseColumnColumn(dataView: DataView, options?: XYLayerOptions) {
+ return {
+ ...getHistogramColumn({
+ columnName: HISTOGRAM_COLUMN_NAME,
+ overrides: {
+ sourceField: dataView.timeFieldName,
+ },
+ }),
+ ...(options?.breakdown
+ ? {
+ ...getTopValuesColumn({
+ columnName: BREAKDOWN_COLUMN_NAME,
+ overrides: {
+ sourceField: options?.breakdown.sourceField,
+ breakdownSize: options?.breakdown.size,
+ },
+ }),
+ }
+ : {}),
+ };
+ }
+
+ getLayer(
+ layerId: string,
+ accessorId: string,
+ dataView: DataView
+ ): FormBasedPersistedState['layers'] {
+ const baseLayer: PersistedIndexPatternLayer = {
+ columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME],
+ columns: {
+ ...this.getBaseColumnColumn(dataView, this.layerConfig.options),
+ },
+ };
+
+ return {
+ [layerId]: this.column.reduce(
+ (acc, curr, index) => ({
+ ...acc,
+ ...curr.getData(`${accessorId}_${index}`, acc, dataView),
+ }),
+ baseLayer
+ ),
+ };
+ }
+
+ getReference(layerId: string, dataView: DataView): SavedObjectReference[] {
+ return getDefaultReferences(dataView, layerId);
+ }
+
+ getLayerConfig(layerId: string, accessorId: string): XYDataLayerConfig {
+ return {
+ layerId,
+ seriesType: 'line',
+ accessors: this.column.map((_, index) => `${accessorId}_${index}`),
+ yConfig: [],
+ layerType: 'data',
+ xAccessor: HISTOGRAM_COLUMN_NAME,
+ splitAccessor: BREAKDOWN_COLUMN_NAME,
+ };
+ }
+}
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/xy_reference_lines_layer.ts b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/xy_reference_lines_layer.ts
new file mode 100644
index 0000000000000..6508dcbc2cf49
--- /dev/null
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/layers/xy_reference_lines_layer.ts
@@ -0,0 +1,65 @@
+/*
+ * 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 type { SavedObjectReference } from '@kbn/core-saved-objects-common';
+import type { DataView } from '@kbn/data-views-plugin/common';
+import type {
+ FormBasedPersistedState,
+ PersistedIndexPatternLayer,
+ XYReferenceLineLayerConfig,
+} from '@kbn/lens-plugin/public';
+import type { ChartColumn, ChartLayer, FormulaConfig } from '../../../types';
+import { getDefaultReferences } from '../../utils';
+import { ReferenceLineColumn } from './column/reference_line';
+
+interface XYReferenceLinesLayerConfig {
+ data: FormulaConfig[];
+}
+
+export class XYReferenceLinesLayer implements ChartLayer {
+ private column: ChartColumn[];
+ constructor(layerConfig: XYReferenceLinesLayerConfig) {
+ this.column = layerConfig.data.map((p) => new ReferenceLineColumn(p));
+ }
+
+ getName(): string | undefined {
+ return this.column[0].getFormulaConfig().label;
+ }
+
+ getLayer(
+ layerId: string,
+ accessorId: string,
+ dataView: DataView
+ ): FormBasedPersistedState['layers'] {
+ const baseLayer = { columnOrder: [], columns: {} } as PersistedIndexPatternLayer;
+ return {
+ [`${layerId}_reference`]: this.column.reduce((acc, curr, index) => {
+ return {
+ ...acc,
+ ...curr.getData(`${accessorId}_${index}_reference_column`, acc, dataView),
+ };
+ }, baseLayer),
+ };
+ }
+
+ getReference(layerId: string, dataView: DataView): SavedObjectReference[] {
+ return getDefaultReferences(dataView, `${layerId}_reference`);
+ }
+
+ getLayerConfig(layerId: string, accessorId: string): XYReferenceLineLayerConfig {
+ return {
+ layerId: `${layerId}_reference`,
+ layerType: 'referenceLine',
+ accessors: this.column.map((_, index) => `${accessorId}_${index}_reference_column`),
+ yConfig: this.column.map((layer, index) => ({
+ color: layer.getFormulaConfig().color,
+ forAccessor: `${accessorId}_${index}_reference_column`,
+ axisMode: 'left',
+ })),
+ };
+ }
+}
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/line_chart.ts b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/line_chart.ts
deleted file mode 100644
index bf1f245dd0447..0000000000000
--- a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/line_chart.ts
+++ /dev/null
@@ -1,153 +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 type {
- FormBasedPersistedState,
- FormulaPublicApi,
- PersistedIndexPatternLayer,
- XYState,
-} from '@kbn/lens-plugin/public';
-import type { SavedObjectReference } from '@kbn/core-saved-objects-common';
-import type { DataView } from '@kbn/data-views-plugin/public';
-import { Filter } from '@kbn/es-query';
-import {
- DEFAULT_LAYER_ID,
- getBreakdownColumn,
- getDefaultReferences,
- getHistogramColumn,
-} from '../utils';
-import type { LensChartConfig, VisualizationAttributes, LineChartOptions } from '../../types';
-
-const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown';
-const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
-const ACCESSOR = 'formula_accessor';
-
-export class LineChart implements VisualizationAttributes {
- constructor(
- private chartConfig: LensChartConfig,
- private dataView: DataView,
- private formulaAPI: FormulaPublicApi,
- private options?: LineChartOptions
- ) {}
-
- getVisualizationType(): string {
- return 'lnsXY';
- }
-
- getLayers(): FormBasedPersistedState['layers'] {
- const baseLayer: PersistedIndexPatternLayer = {
- columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME],
- columns: {
- ...getBreakdownColumn({
- columnName: BREAKDOWN_COLUMN_NAME,
- overrides: {
- sourceField: 'host.name',
- breakdownSize: this.options?.breakdownSize,
- },
- }),
- ...getHistogramColumn({
- columnName: HISTOGRAM_COLUMN_NAME,
- overrides: {
- sourceField: this.dataView.timeFieldName,
- },
- }),
- },
- };
-
- const dataLayer = this.formulaAPI.insertOrReplaceFormulaColumn(
- ACCESSOR,
- this.chartConfig.formula,
- baseLayer,
- this.dataView
- );
-
- if (!dataLayer) {
- throw new Error('Error generating the data layer for the chart');
- }
-
- return { [DEFAULT_LAYER_ID]: dataLayer, ...this.chartConfig.lineChartConfig?.extraLayers };
- }
-
- getVisualizationState(): XYState {
- const extraVisualizationState = this.chartConfig.lineChartConfig?.extraVisualizationState;
-
- return getXYVisualizationState({
- ...extraVisualizationState,
- layers: [
- {
- layerId: DEFAULT_LAYER_ID,
- seriesType: 'line',
- accessors: [ACCESSOR],
- yConfig: [],
- layerType: 'data',
- xAccessor: HISTOGRAM_COLUMN_NAME,
- splitAccessor: BREAKDOWN_COLUMN_NAME,
- },
- ...(extraVisualizationState?.layers ? extraVisualizationState?.layers : []),
- ],
- });
- }
-
- getReferences(): SavedObjectReference[] {
- const extraReference = this.chartConfig.lineChartConfig?.extraReference;
- return [
- ...getDefaultReferences(this.dataView, DEFAULT_LAYER_ID),
- ...(extraReference ? getDefaultReferences(this.dataView, extraReference) : []),
- ];
- }
-
- getDataView(): DataView {
- return this.dataView;
- }
-
- getTitle(): string {
- return this.options?.title ?? this.chartConfig.title ?? '';
- }
-
- getFilters(): Filter[] {
- return this.chartConfig.getFilters({ id: this.dataView.id ?? DEFAULT_LAYER_ID });
- }
-}
-
-export const getXYVisualizationState = (
- custom: Omit, 'layers'> & { layers: XYState['layers'] }
-): XYState => ({
- legend: {
- isVisible: false,
- position: 'right',
- showSingleSeries: false,
- },
- valueLabels: 'show',
- fittingFunction: 'Zero',
- curveType: 'LINEAR',
- yLeftScale: 'linear',
- axisTitlesVisibilitySettings: {
- x: false,
- yLeft: false,
- yRight: true,
- },
- tickLabelsVisibilitySettings: {
- x: true,
- yLeft: true,
- yRight: true,
- },
- labelsOrientation: {
- x: 0,
- yLeft: 0,
- yRight: 0,
- },
- gridlinesVisibilitySettings: {
- x: true,
- yLeft: true,
- yRight: true,
- },
- preferredSeriesType: 'line',
- valuesInLegend: false,
- emphasizeFitting: true,
- hideEndzones: true,
- ...custom,
-});
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/metric_chart.ts b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/metric_chart.ts
index 67b944992e566..ecdb8fbb50480 100644
--- a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/metric_chart.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/metric_chart.ts
@@ -5,152 +5,39 @@
* 2.0.
*/
-import {
- FormBasedPersistedState,
- FormulaPublicApi,
- MetricVisualizationState,
- PersistedIndexPatternLayer,
-} from '@kbn/lens-plugin/public';
+import type { FormBasedPersistedState, MetricVisualizationState } from '@kbn/lens-plugin/public';
import type { SavedObjectReference } from '@kbn/core-saved-objects-common';
import type { DataView } from '@kbn/data-views-plugin/public';
-import type { Filter } from '@kbn/es-query';
-import { DEFAULT_LAYER_ID, getDefaultReferences, getHistogramColumn } from '../utils';
+import { DEFAULT_LAYER_ID } from '../utils';
-import type {
- VisualizationAttributes,
- LensChartConfig,
- MetricChartOptions,
- Formula,
-} from '../../types';
+import type { Chart, ChartConfig, ChartLayer } from '../../types';
-const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
-const TRENDLINE_LAYER_ID = 'trendline_layer';
-const TRENDLINE_ACCESSOR = 'metric_trendline_formula_accessor';
const ACCESSOR = 'metric_formula_accessor';
-export class MetricChart implements VisualizationAttributes {
- constructor(
- private chartConfig: LensChartConfig,
- private dataView: DataView,
- private formulaAPI: FormulaPublicApi,
- private options?: MetricChartOptions
- ) {}
+export class MetricChart implements Chart {
+ constructor(private chartConfig: ChartConfig>) {}
getVisualizationType(): string {
return 'lnsMetric';
}
- getTrendLineLayer(baseLayer: PersistedIndexPatternLayer): FormBasedPersistedState['layers'] {
- const trendLineLayer = this.formulaAPI.insertOrReplaceFormulaColumn(
- TRENDLINE_ACCESSOR,
- this.getFormulaWithOverride(),
- baseLayer,
- this.dataView
- );
-
- if (!trendLineLayer) {
- throw new Error('Error generating the data layer for the chart');
- }
-
- return {
- [TRENDLINE_LAYER_ID]: {
- linkToLayers: [DEFAULT_LAYER_ID],
- ...trendLineLayer,
- },
- };
- }
-
- getFormulaWithOverride(): Formula {
- const { formula } = this.chartConfig;
- const { decimals = formula.format?.params?.decimals, title = this.chartConfig.title } =
- this.options ?? {};
- return {
- ...this.chartConfig.formula,
- ...(formula.format && decimals
- ? {
- format: {
- ...formula.format,
- params: {
- decimals,
- },
- },
- }
- : {}),
- label: title,
- };
- }
-
getLayers(): FormBasedPersistedState['layers'] {
- const { showTrendLine = true } = this.options ?? {};
- const baseLayer: PersistedIndexPatternLayer = {
- columnOrder: [HISTOGRAM_COLUMN_NAME],
- columns: getHistogramColumn({
- columnName: HISTOGRAM_COLUMN_NAME,
- overrides: {
- sourceField: this.dataView.timeFieldName,
- params: {
- interval: 'auto',
- includeEmptyRows: true,
- },
- },
- }),
- sampling: 1,
- };
-
- const baseLayerDetails = this.formulaAPI.insertOrReplaceFormulaColumn(
- ACCESSOR,
- this.getFormulaWithOverride(),
- { columnOrder: [], columns: {} },
- this.dataView
- );
-
- if (!baseLayerDetails) {
- throw new Error('Error generating the data layer for the chart');
- }
-
- return {
- [DEFAULT_LAYER_ID]: baseLayerDetails,
- ...(showTrendLine ? this.getTrendLineLayer(baseLayer) : {}),
- };
+ return this.chartConfig.layers.getLayer(DEFAULT_LAYER_ID, ACCESSOR, this.chartConfig.dataView);
}
getVisualizationState(): MetricVisualizationState {
- const { subtitle, backgroundColor, showTrendLine = true } = this.options ?? {};
- return {
- layerId: DEFAULT_LAYER_ID,
- layerType: 'data',
- metricAccessor: ACCESSOR,
- color: backgroundColor,
- subtitle,
- showBar: false,
- ...(showTrendLine
- ? {
- trendlineLayerId: TRENDLINE_LAYER_ID,
- trendlineLayerType: 'metricTrendline',
- trendlineMetricAccessor: TRENDLINE_ACCESSOR,
- trendlineTimeAccessor: HISTOGRAM_COLUMN_NAME,
- }
- : {}),
- };
+ return this.chartConfig.layers.getLayerConfig(DEFAULT_LAYER_ID, ACCESSOR);
}
getReferences(): SavedObjectReference[] {
- const { showTrendLine = true } = this.options ?? {};
- return [
- ...getDefaultReferences(this.dataView, DEFAULT_LAYER_ID),
- ...(showTrendLine ? getDefaultReferences(this.dataView, TRENDLINE_LAYER_ID) : []),
- ];
+ return this.chartConfig.layers.getReference(DEFAULT_LAYER_ID, this.chartConfig.dataView);
}
getDataView(): DataView {
- return this.dataView;
+ return this.chartConfig.dataView;
}
getTitle(): string {
- return this.options?.showTitle ? this.options?.title ?? this.chartConfig.title : '';
- }
-
- getFilters(): Filter[] {
- return this.chartConfig.getFilters({ id: this.dataView.id ?? DEFAULT_LAYER_ID });
+ return this.chartConfig.title ?? '';
}
}
diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/xy_chart.ts b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/xy_chart.ts
new file mode 100644
index 0000000000000..2794a86b3499b
--- /dev/null
+++ b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/xy_chart.ts
@@ -0,0 +1,99 @@
+/*
+ * 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 type { FormBasedPersistedState, XYLayerConfig, XYState } from '@kbn/lens-plugin/public';
+import type { DataView } from '@kbn/data-views-plugin/public';
+import type { SavedObjectReference } from '@kbn/core-saved-objects-common';
+import { DEFAULT_LAYER_ID } from '../utils';
+import type { Chart, ChartConfig, ChartLayer } from '../../types';
+
+const ACCESSOR = 'formula_accessor';
+
+export class XYChart implements Chart {
+ constructor(private chartConfig: ChartConfig>>) {}
+
+ getVisualizationType(): string {
+ return 'lnsXY';
+ }
+
+ getLayers(): FormBasedPersistedState['layers'] {
+ return this.chartConfig.layers.reduce((acc, curr, index) => {
+ const layerId = `${DEFAULT_LAYER_ID}_${index}`;
+ const accessorId = `${ACCESSOR}_${index}`;
+ return {
+ ...acc,
+ ...curr.getLayer(layerId, accessorId, this.chartConfig.dataView),
+ };
+ }, {});
+ }
+
+ getVisualizationState(): XYState {
+ return getXYVisualizationState({
+ layers: [
+ ...this.chartConfig.layers.map((layerItem, index) => {
+ const layerId = `${DEFAULT_LAYER_ID}_${index}`;
+ const accessorId = `${ACCESSOR}_${index}`;
+ return layerItem.getLayerConfig(layerId, accessorId);
+ }),
+ ],
+ });
+ }
+
+ getReferences(): SavedObjectReference[] {
+ return this.chartConfig.layers.flatMap((p, index) => {
+ const layerId = `${DEFAULT_LAYER_ID}_${index}`;
+ return p.getReference(layerId, this.chartConfig.dataView);
+ });
+ }
+
+ getDataView(): DataView {
+ return this.chartConfig.dataView;
+ }
+
+ getTitle(): string {
+ return this.chartConfig.title ?? this.chartConfig.layers[0].getName() ?? '';
+ }
+}
+
+export const getXYVisualizationState = (
+ custom: Omit, 'layers'> & { layers: XYState['layers'] }
+): XYState => ({
+ legend: {
+ isVisible: false,
+ position: 'right',
+ showSingleSeries: false,
+ },
+ valueLabels: 'show',
+ fittingFunction: 'Zero',
+ curveType: 'LINEAR',
+ yLeftScale: 'linear',
+ axisTitlesVisibilitySettings: {
+ x: false,
+ yLeft: false,
+ yRight: true,
+ },
+ tickLabelsVisibilitySettings: {
+ x: true,
+ yLeft: true,
+ yRight: true,
+ },
+ labelsOrientation: {
+ x: 0,
+ yLeft: 0,
+ yRight: 0,
+ },
+ gridlinesVisibilitySettings: {
+ x: true,
+ yLeft: true,
+ yRight: true,
+ },
+ preferredSeriesType: 'line',
+ valuesInLegend: false,
+ emphasizeFitting: true,
+ hideEndzones: true,
+ ...custom,
+});
diff --git a/x-pack/plugins/infra/public/common/visualizations/types.ts b/x-pack/plugins/infra/public/common/visualizations/types.ts
index 969016f0b1bcf..b3d2a3815ac6d 100644
--- a/x-pack/plugins/infra/public/common/visualizations/types.ts
+++ b/x-pack/plugins/infra/public/common/visualizations/types.ts
@@ -7,62 +7,75 @@
import type { SavedObjectReference } from '@kbn/core-saved-objects-common';
import type { DataView } from '@kbn/data-views-plugin/common';
-import { DataViewBase, Filter } from '@kbn/es-query';
-import {
+import type {
FormBasedPersistedState,
- FormulaPublicApi,
MetricVisualizationState,
+ PersistedIndexPatternLayer,
TypedLensByValueInput,
XYState,
+ XYDataLayerConfig,
+ FormulaPublicApi,
} from '@kbn/lens-plugin/public';
-import { hostLensFormulas, visualizationTypes } from './constants';
-
+import { hostLensFormulas } from './constants';
export type LensAttributes = TypedLensByValueInput['attributes'];
-export interface LensOptions {
- title: string;
-}
-export interface LineChartOptions extends LensOptions {
- breakdownSize?: number;
-}
-export interface MetricChartOptions extends LensOptions {
- subtitle?: string;
- showTitle?: boolean;
- showTrendLine?: boolean;
- backgroundColor?: string;
- decimals?: number;
+// Attributes
+export type LensVisualizationState = XYState | MetricVisualizationState;
+
+export interface VisualizationAttributesBuilder {
+ build(): LensAttributes;
}
-export interface LensLineChartConfig {
- extraVisualizationState?: Partial & { layers: XYState['layers'] }>;
- extraLayers?: FormBasedPersistedState['layers'];
- extraReference?: string;
+// Column
+export interface ChartColumn {
+ getData(
+ id: string,
+ baseLayer: PersistedIndexPatternLayer,
+ dataView: DataView
+ ): PersistedIndexPatternLayer;
+ getFormulaConfig(): FormulaConfig;
}
-export interface LensChartConfig {
- title: string;
- formula: Formula;
- lineChartConfig?: LensLineChartConfig;
- getFilters: ({ id }: Pick) => Filter[];
+
+// Layer
+export type LensLayerConfig = XYDataLayerConfig | MetricVisualizationState;
+
+export interface ChartLayer {
+ getName(): string | undefined;
+ getLayer(
+ layerId: string,
+ accessorId: string,
+ dataView: DataView
+ ): FormBasedPersistedState['layers'];
+ getReference(layerId: string, dataView: DataView): SavedObjectReference[];
+ getLayerConfig(layerId: string, acessorId: string): TLayerConfig;
}
-export type TVisualization = XYState | MetricVisualizationState;
-export interface VisualizationAttributes {
+// Chart
+export interface Chart {
getTitle(): string;
getVisualizationType(): string;
getLayers(): FormBasedPersistedState['layers'];
- getVisualizationState(): T;
+ getVisualizationState(): TVisualizationState;
getReferences(): SavedObjectReference[];
- getFilters(): Filter[];
getDataView(): DataView;
}
-
-export interface VisualizationAttributesBuilder {
- build(): LensAttributes;
+export interface ChartConfig<
+ TLayer extends ChartLayer | Array>
+> {
+ dataView: DataView;
+ layers: TLayer;
+ title?: string;
}
-export type Formula = Parameters[1];
+// Formula
+type LensFormula = Parameters[1];
+export interface FormulaConfig {
+ label?: string;
+ color?: string;
+ format: NonNullable;
+ value: string;
+}
-export type VisualizationTypes = keyof typeof visualizationTypes;
export type HostsLensFormulas = keyof typeof hostLensFormulas;
export type HostsLensMetricChartFormulas = Exclude;
export type HostsLensLineChartFormulas = Exclude;
diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpi_grid.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpi_grid.tsx
index 89bb6b636a98e..c368413ba2864 100644
--- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpi_grid.tsx
+++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpi_grid.tsx
@@ -8,7 +8,7 @@ import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { Tile } from './tile';
-import { KPI_CHARTS } from '../../../../common/visualizations/lens/kpi_grid_config';
+import { KPI_CHARTS } from '../../../../common/visualizations/lens/dashboards/host/kpi_grid_config';
import type { KPIProps } from './overview';
import type { StringDateRange } from '../../types';
@@ -27,8 +27,8 @@ export const KPIGrid = React.memo(({ nodeName, dataView, dateRange }: KPIGridPro
style={{ flexGrow: 0 }}
data-test-subj="assetDetailsKPIGrid"
>
- {KPI_CHARTS.map(({ ...chartProp }) => (
-
+ {KPI_CHARTS.map((chartProp, index) => (
+
))}
diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/tile.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/tile.tsx
index 18ae64ad8ee6a..25dc51b221d4f 100644
--- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/tile.tsx
+++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/tile.tsx
@@ -18,25 +18,23 @@ import {
} from '@elastic/eui';
import styled from 'styled-components';
import type { Action } from '@kbn/ui-actions-plugin/public';
-import type { KPIChartProps } from '../../../../common/visualizations/lens/kpi_grid_config';
+import type { KPIChartProps } from '../../../../common/visualizations/lens/dashboards/host/kpi_grid_config';
import { useLensAttributes } from '../../../../hooks/use_lens_attributes';
import { LensWrapper } from '../../../../common/visualizations/lens/lens_wrapper';
-import { buildCombinedHostsFilter } from '../../../../utils/filters/build';
+import { buildCombinedHostsFilter, buildExistsHostsFilter } from '../../../../utils/filters/build';
import { TooltipContent } from '../../../../common/visualizations/metric_explanation/tooltip_content';
import type { KPIGridProps } from './kpi_grid';
const MIN_HEIGHT = 150;
export const Tile = ({
+ id,
+ layers,
title,
- type,
- backgroundColor,
toolTip,
- decimals = 1,
- trendLine = false,
+ dataView,
nodeName,
dateRange,
- dataView,
}: KPIChartProps & KPIGridProps) => {
const getSubtitle = () =>
i18n.translate('xpack.infra.assetDetailsEmbeddable.overview.metricTrend.subtitle.average', {
@@ -44,17 +42,10 @@ export const Tile = ({
});
const { formula, attributes, getExtraActions, error } = useLensAttributes({
- type,
dataView,
- options: {
- backgroundColor,
- decimals,
- subtitle: getSubtitle(),
- showTrendLine: trendLine,
- showTitle: false,
- title,
- },
- visualizationType: 'metricChart',
+ title,
+ layers: { ...layers, options: { ...layers.options, subtitle: getSubtitle() } },
+ visualizationType: 'lnsMetric',
});
const filters = useMemo(() => {
@@ -64,6 +55,7 @@ export const Tile = ({
values: [nodeName],
dataView,
}),
+ buildExistsHostsFilter({ field: 'host.name', dataView }),
];
}, [dataView, nodeName]);
@@ -83,7 +75,7 @@ export const Tile = ({
hasShadow={false}
paddingSize={error ? 'm' : 'none'}
style={{ minHeight: MIN_HEIGHT }}
- data-test-subj={`assetDetailsKPI-${type}`}
+ data-test-subj={`assetDetailsKPI-${id}`}
>
{error ? (
;
@@ -30,6 +31,8 @@ const mockDataView = {
metaFields: [],
} as unknown as jest.Mocked;
+const normalizedLoad1m = hostLensFormulas.normalizedLoad1m;
+
const lensPluginMockStart = lensPluginMock.createStartContract();
const mockUseKibana = () => {
useKibanaMock.mockReturnValue({
@@ -48,27 +51,50 @@ describe('useHostTable hook', () => {
it('should return the basic lens attributes', async () => {
const { result, waitForNextUpdate } = renderHook(() =>
useLensAttributes({
- visualizationType: 'lineChart',
- type: 'normalizedLoad1m',
- options: {
- title: 'Injected Normalized Load',
- },
+ visualizationType: 'lnsXY',
+ layers: [
+ {
+ data: [normalizedLoad1m],
+ layerType: 'data',
+ options: {
+ breakdown: {
+ size: 10,
+ sourceField: 'host.name',
+ },
+ },
+ },
+ {
+ data: [
+ {
+ value: '1',
+ format: {
+ id: 'percent',
+ params: {
+ decimals: 0,
+ },
+ },
+ },
+ ],
+ layerType: 'referenceLine',
+ },
+ ],
+ title: 'Injected Normalized Load',
dataView: mockDataView,
})
);
await waitForNextUpdate();
const { state, title } = result.current.attributes ?? {};
- const { datasourceStates, filters } = state ?? {};
+ const { datasourceStates } = state ?? {};
expect(title).toBe('Injected Normalized Load');
expect(datasourceStates).toEqual({
formBased: {
layers: {
- layer1: {
- columnOrder: ['hosts_aggs_breakdown', 'x_date_histogram', 'formula_accessor'],
+ layer_0: {
+ columnOrder: ['aggs_breakdown', 'x_date_histogram', 'formula_accessor_0_0'],
columns: {
- hosts_aggs_breakdown: {
+ aggs_breakdown: {
dataType: 'string',
isBucketed: true,
label: 'Top 10 values of host.name',
@@ -104,12 +130,12 @@ describe('useHostTable hook', () => {
scale: 'interval',
sourceField: '@timestamp',
},
- formula_accessor: {
- customLabel: false,
+ formula_accessor_0_0: {
+ customLabel: true,
dataType: 'number',
filter: undefined,
isBucketed: false,
- label: 'average(system.load.1) / max(system.load.cores)',
+ label: 'Normalized Load',
operationType: 'formula',
params: {
format: {
@@ -128,10 +154,10 @@ describe('useHostTable hook', () => {
},
indexPatternId: 'mock-id',
},
- referenceLayer: {
- columnOrder: ['referenceColumn'],
+ layer_1_reference: {
+ columnOrder: ['formula_accessor_1_0_reference_column'],
columns: {
- referenceColumn: {
+ formula_accessor_1_0_reference_column: {
customLabel: true,
dataType: 'number',
isBucketed: false,
@@ -145,7 +171,7 @@ describe('useHostTable hook', () => {
decimals: 0,
},
},
- value: 1,
+ value: '1',
},
references: [],
scale: 'ratio',
@@ -158,25 +184,18 @@ describe('useHostTable hook', () => {
},
},
});
- expect(filters).toEqual([
- {
- meta: {
- index: 'mock-id',
- },
- query: {
- exists: {
- field: 'host.name',
- },
- },
- },
- ]);
});
it('should return extra actions', async () => {
const { result, waitForNextUpdate } = renderHook(() =>
useLensAttributes({
- visualizationType: 'lineChart',
- type: 'normalizedLoad1m',
+ visualizationType: 'lnsXY',
+ layers: [
+ {
+ data: [normalizedLoad1m],
+ layerType: 'data',
+ },
+ ],
dataView: mockDataView,
})
);
diff --git a/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts b/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts
index ac7ec3387e8e7..721bf5669d203 100644
--- a/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts
+++ b/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts
@@ -12,45 +12,68 @@ import { useKibana } from '@kbn/kibana-react-plugin/public';
import type { Action, ActionExecutionContext } from '@kbn/ui-actions-plugin/public';
import { i18n } from '@kbn/i18n';
import useAsync from 'react-use/lib/useAsync';
+import { FormulaPublicApi, LayerType as LensLayerType } from '@kbn/lens-plugin/public';
import { InfraClientSetupDeps } from '../types';
import {
- type HostsLensFormulas,
- type HostsLensMetricChartFormulas,
- type HostsLensLineChartFormulas,
- type LineChartOptions,
- type MetricChartOptions,
+ type XYLayerOptions,
+ type MetricLayerOptions,
+ type FormulaConfig,
+ type LensAttributes,
LensAttributesBuilder,
- LensAttributes,
- hostLensFormulas,
- visualizationTypes,
+ XYDataLayer,
+ MetricLayer,
+ XYChart,
+ MetricChart,
+ XYReferenceLinesLayer,
+ Chart,
+ LensVisualizationState,
} from '../common/visualizations';
import { useLazyRef } from './use_lazy_ref';
-type Options = LineChartOptions | MetricChartOptions;
-interface UseLensAttributesBaseParams {
+type Options = XYLayerOptions | MetricLayerOptions;
+type ChartType = 'lnsXY' | 'lnsMetric';
+export type LayerType = Exclude;
+export interface Layer<
+ TOptions extends Options,
+ TFormulaConfig extends FormulaConfig | FormulaConfig[],
+ TLayerType extends LayerType = LayerType
+> {
+ layerType: TLayerType;
+ data: TFormulaConfig;
+ options?: TOptions;
+}
+
+interface UseLensAttributesBaseParams<
+ TOptions extends Options,
+ TLayers extends Array> | Layer
+> {
dataView?: DataView;
- type: T;
- options?: O;
+ layers: TLayers;
+ title?: string;
}
-interface UseLensAttributesLineChartParams
- extends UseLensAttributesBaseParams {
- visualizationType: 'lineChart';
+interface UseLensAttributesXYChartParams
+ extends UseLensAttributesBaseParams<
+ XYLayerOptions,
+ Array>
+ > {
+ visualizationType: 'lnsXY';
}
interface UseLensAttributesMetricChartParams
- extends UseLensAttributesBaseParams {
- visualizationType: 'metricChart';
+ extends UseLensAttributesBaseParams<
+ MetricLayerOptions,
+ Layer
+ > {
+ visualizationType: 'lnsMetric';
}
-type UseLensAttributesParams =
- | UseLensAttributesLineChartParams
- | UseLensAttributesMetricChartParams;
+type UseLensAttributesParams = UseLensAttributesXYChartParams | UseLensAttributesMetricChartParams;
export const useLensAttributes = ({
- type,
dataView,
- options,
+ layers,
+ title,
visualizationType,
}: UseLensAttributesParams) => {
const {
@@ -60,29 +83,26 @@ export const useLensAttributes = ({
const { value, error } = useAsync(lens.stateHelperApi, [lens]);
const { formula: formulaAPI } = value ?? {};
- const lensChartConfig = hostLensFormulas[type];
- const Chart = visualizationTypes[visualizationType];
-
const attributes = useLazyRef(() => {
if (!dataView || !formulaAPI) {
return null;
}
- const builder = new LensAttributesBuilder(
- new Chart(lensChartConfig, dataView, formulaAPI, options)
- );
+ const builder = new LensAttributesBuilder({
+ visualization: chartFactory({
+ dataView,
+ formulaAPI,
+ layers,
+ title,
+ visualizationType,
+ }),
+ });
return builder.build();
});
const injectFilters = useCallback(
- ({
- filters,
- query = { language: 'kuery', query: '' },
- }: {
- filters: Filter[];
- query?: Query;
- }): LensAttributes | null => {
+ ({ filters, query }: { filters: Filter[]; query: Query }): LensAttributes | null => {
if (!attributes.current) {
return null;
}
@@ -99,7 +119,7 @@ export const useLensAttributes = ({
);
const openInLensAction = useCallback(
- ({ timeRange, filters, query }: { timeRange: TimeRange; filters: Filter[]; query?: Query }) =>
+ ({ timeRange, query, filters }: { timeRange: TimeRange; filters: Filter[]; query: Query }) =>
() => {
const injectedAttributes = injectFilters({ filters, query });
if (injectedAttributes) {
@@ -119,18 +139,105 @@ export const useLensAttributes = ({
);
const getExtraActions = useCallback(
- ({ timeRange, filters, query }: { timeRange: TimeRange; filters: Filter[]; query?: Query }) => {
+ ({
+ timeRange,
+ filters = [],
+ query = { language: 'kuery', query: '' },
+ }: {
+ timeRange: TimeRange;
+ filters?: Filter[];
+ query?: Query;
+ }) => {
const openInLens = getOpenInLensAction(openInLensAction({ timeRange, filters, query }));
return [openInLens];
},
[openInLensAction]
);
- const {
- formula: { formula },
- } = lensChartConfig;
+ const getFormula = () => {
+ const firstDataLayer = [...(Array.isArray(layers) ? layers : [layers])].find(
+ (p) => p.layerType === 'data'
+ );
+
+ if (!firstDataLayer) {
+ return '';
+ }
+
+ const mainFormulaConfig = Array.isArray(firstDataLayer.data)
+ ? firstDataLayer.data[0]
+ : firstDataLayer.data;
- return { formula, attributes: attributes.current, getExtraActions, error };
+ return mainFormulaConfig.value;
+ };
+
+ return { formula: getFormula(), attributes: attributes.current, getExtraActions, error };
+};
+
+const chartFactory = <
+ TOptions,
+ TLayers extends Array> | Layer
+>({
+ dataView,
+ formulaAPI,
+ layers,
+ title,
+ visualizationType,
+}: {
+ dataView: DataView;
+ formulaAPI: FormulaPublicApi;
+ visualizationType: ChartType;
+ layers: TLayers;
+ title?: string;
+}): Chart => {
+ switch (visualizationType) {
+ case 'lnsXY':
+ if (!Array.isArray(layers)) {
+ throw new Error(`Invalid layers type. Expected an array of layers.`);
+ }
+
+ const getLayerClass = (layerType: LayerType) => {
+ switch (layerType) {
+ case 'data': {
+ return XYDataLayer;
+ }
+ case 'referenceLine': {
+ return XYReferenceLinesLayer;
+ }
+ default:
+ throw new Error(`Invalid layerType: ${layerType}`);
+ }
+ };
+
+ return new XYChart({
+ dataView,
+ layers: layers.map((layerItem) => {
+ const Layer = getLayerClass(layerItem.layerType);
+ return new Layer({
+ data: layerItem.data,
+ formulaAPI,
+ options: layerItem.options,
+ });
+ }),
+ title,
+ });
+
+ case 'lnsMetric':
+ if (Array.isArray(layers)) {
+ throw new Error(`Invalid layers type. Expected a single layer object.`);
+ }
+
+ return new MetricChart({
+ dataView,
+ layers: new MetricLayer({
+ data: layers.data,
+ formulaAPI,
+ options: layers.options,
+ }),
+ title,
+ });
+ default:
+ throw new Error(`Unsupported chart type: ${visualizationType}`);
+ }
};
const getOpenInLensAction = (onExecute: () => void): Action => {
diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/hosts_tile.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/hosts_tile.tsx
index 9cfb67a88c3b0..806570ebac349 100644
--- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/hosts_tile.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/hosts_tile.tsx
@@ -6,14 +6,14 @@
*/
import { i18n } from '@kbn/i18n';
import React from 'react';
+import { KPIChartProps } from '../../../../../common/visualizations/lens/dashboards/host/kpi_grid_config';
import { hostLensFormulas } from '../../../../../common/visualizations';
import { useHostCountContext } from '../../hooks/use_host_count';
import { useUnifiedSearchContext } from '../../hooks/use_unified_search';
-import { TOOLTIP } from '../../../../../common/visualizations/lens/translations';
+import { TOOLTIP } from '../../../../../common/visualizations/lens/dashboards/host/translations';
import { type Props, MetricChartWrapper } from '../chart/metric_chart_wrapper';
import { TooltipContent } from '../../../../../common/visualizations/metric_explanation/tooltip_content';
-import { KPIChartProps } from './tile';
const HOSTS_CHART: Omit = {
id: `metric-hostCount`,
@@ -47,7 +47,7 @@ export const HostsTile = ({ style }: Pick) => {
subtitle={getSubtitle()}
toolTip={
}
diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi_grid.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi_grid.tsx
index 5914be028ab8b..01fd3ccef909f 100644
--- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi_grid.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi_grid.tsx
@@ -13,7 +13,7 @@ import { Tile } from './tile';
import { HostCountProvider } from '../../hooks/use_host_count';
import { HostsTile } from './hosts_tile';
import { KPI_CHART_MIN_HEIGHT } from '../../constants';
-import { KPI_CHARTS } from '../../../../../common/visualizations/lens/kpi_grid_config';
+import { KPI_CHARTS } from '../../../../../common/visualizations/lens/dashboards/host/kpi_grid_config';
const lensStyle: CSSProperties = {
height: KPI_CHART_MIN_HEIGHT,
@@ -33,8 +33,8 @@ export const KPIGrid = () => {
- {KPI_CHARTS.map(({ ...chartProp }) => (
-
+ {KPI_CHARTS.map((chartProp, index) => (
+
))}
diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx
index 5890442358113..e9d031401f7d1 100644
--- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx
@@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import React, { CSSProperties, useMemo, useCallback } from 'react';
+import React, { useMemo, useCallback } from 'react';
import { i18n } from '@kbn/i18n';
import { BrushTriggerEvent } from '@kbn/charts-plugin/public';
@@ -19,38 +19,22 @@ import {
} from '@elastic/eui';
import styled from 'styled-components';
import { Action } from '@kbn/ui-actions-plugin/public';
+import { KPIChartProps } from '../../../../../common/visualizations/lens/dashboards/host/kpi_grid_config';
+import {
+ buildCombinedHostsFilter,
+ buildExistsHostsFilter,
+} from '../../../../../utils/filters/build';
import { useLensAttributes } from '../../../../../hooks/use_lens_attributes';
import { useMetricsDataViewContext } from '../../hooks/use_data_view';
import { useUnifiedSearchContext } from '../../hooks/use_unified_search';
-import { HostsLensMetricChartFormulas } from '../../../../../common/visualizations';
import { useHostsViewContext } from '../../hooks/use_hosts_view';
import { LensWrapper } from '../../../../../common/visualizations/lens/lens_wrapper';
-import { buildCombinedHostsFilter } from '../../../../../utils/filters/build';
import { useHostCountContext } from '../../hooks/use_host_count';
import { useAfterLoadedState } from '../../hooks/use_after_loaded_state';
import { TooltipContent } from '../../../../../common/visualizations/metric_explanation/tooltip_content';
import { KPI_CHART_MIN_HEIGHT } from '../../constants';
-export interface KPIChartProps {
- title: string;
- subtitle?: string;
- trendLine?: boolean;
- backgroundColor: string;
- type: HostsLensMetricChartFormulas;
- decimals?: number;
- toolTip: string;
- style?: CSSProperties;
-}
-
-export const Tile = ({
- title,
- type,
- backgroundColor,
- toolTip,
- style,
- decimals = 1,
- trendLine = false,
-}: KPIChartProps) => {
+export const Tile = ({ id, title, layers, style, toolTip, ...props }: KPIChartProps) => {
const { searchCriteria, onSubmit } = useUnifiedSearchContext();
const { dataView } = useMetricsDataViewContext();
const { requestTs, hostNodes, loading: hostsLoading } = useHostsViewContext();
@@ -70,17 +54,10 @@ export const Tile = ({
};
const { formula, attributes, getExtraActions, error } = useLensAttributes({
- type,
dataView,
- options: {
- backgroundColor,
- decimals,
- subtitle: getSubtitle(),
- showTrendLine: trendLine,
- showTitle: false,
- title,
- },
- visualizationType: 'metricChart',
+ title,
+ layers: { ...layers, options: { ...layers.options, subtitle: getSubtitle() } },
+ visualizationType: 'lnsMetric',
});
const filters = useMemo(() => {
@@ -91,6 +68,7 @@ export const Tile = ({
values: hostNodes.map((p) => p.name),
dataView,
}),
+ buildExistsHostsFilter({ field: 'host.name', dataView }),
];
}, [searchCriteria.filters, hostNodes, dataView]);
@@ -133,7 +111,7 @@ export const Tile = ({
{error ? (
{
title: string;
- type: HostsLensLineChartFormulas;
- breakdownSize: number;
- render?: boolean;
+ layers: Array>;
}
const lensStyle: CSSProperties = {
height: METRIC_CHART_MIN_HEIGHT,
};
-export const MetricChart = ({ title, type, breakdownSize }: MetricChartProps) => {
+export const MetricChart = ({ id, title, layers, overrides }: MetricChartProps) => {
const { euiTheme } = useEuiTheme();
const { searchCriteria, onSubmit } = useUnifiedSearchContext();
const { dataView } = useMetricsDataViewContext();
@@ -54,13 +56,10 @@ export const MetricChart = ({ title, type, breakdownSize }: MetricChartProps) =>
});
const { attributes, getExtraActions, error } = useLensAttributes({
- type,
dataView,
- options: {
- title,
- breakdownSize,
- },
- visualizationType: 'lineChart',
+ layers,
+ title,
+ visualizationType: 'lnsXY',
});
const filters = useMemo(() => {
@@ -71,6 +70,7 @@ export const MetricChart = ({ title, type, breakdownSize }: MetricChartProps) =>
values: currentPage.map((p) => p.name),
dataView,
}),
+ buildExistsHostsFilter({ field: 'host.name', dataView }),
];
}, [currentPage, dataView, searchCriteria.filters]);
@@ -108,7 +108,7 @@ export const MetricChart = ({ title, type, breakdownSize }: MetricChartProps) =>
min-height: calc(${METRIC_CHART_MIN_HEIGHT}px + ${euiTheme.size.l});
position: relative;
`}
- data-test-subj={`hostsView-metricChart-${type}`}
+ data-test-subj={`hostsView-metricChart-${id}`}
>
{error ? (
) : (
query={afterLoadedState.query}
onBrushEnd={handleBrushEnd}
loading={loading}
+ overrides={overrides}
hasTitle
/>
)}
diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx
index 4b2ec7a6867e4..47103365fc099 100644
--- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx
@@ -9,82 +9,201 @@ import React from 'react';
import { EuiFlexGrid, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { EuiSpacer } from '@elastic/eui';
+import { hostLensFormulas, type XYLayerOptions } from '../../../../../../common/visualizations';
import { HostMetricsDocsLink } from '../../../../../../common/visualizations/metric_explanation/host_metrics_docs_link';
import { MetricChart, MetricChartProps } from './metric_chart';
const DEFAULT_BREAKDOWN_SIZE = 20;
-const CHARTS_IN_ORDER: Array & { fullRow?: boolean }> = [
+const XY_LAYER_OPTIONS: XYLayerOptions = {
+ breakdown: {
+ size: DEFAULT_BREAKDOWN_SIZE,
+ sourceField: 'host.name',
+ },
+};
+
+const PERCENT_LEFT_AXIS: Pick['overrides'] = {
+ axisLeft: {
+ domain: {
+ min: 0,
+ max: 1,
+ },
+ },
+};
+
+const CHARTS_IN_ORDER: MetricChartProps[] = [
{
+ id: 'cpuUsage',
title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.cpuUsage', {
defaultMessage: 'CPU Usage',
}),
- type: 'cpuUsage',
+ layers: [
+ {
+ data: [hostLensFormulas.cpuUsage],
+ layerType: 'data',
+ options: XY_LAYER_OPTIONS,
+ },
+ ],
+ overrides: PERCENT_LEFT_AXIS,
},
{
+ id: 'normalizedLoad1m',
title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.normalizedLoad1m', {
defaultMessage: 'Normalized Load',
}),
- type: 'normalizedLoad1m',
+ layers: [
+ {
+ data: [hostLensFormulas.normalizedLoad1m],
+ layerType: 'data',
+ options: XY_LAYER_OPTIONS,
+ },
+ {
+ data: [
+ {
+ value: '1',
+ format: {
+ id: 'percent',
+ params: {
+ decimals: 0,
+ },
+ },
+ color: '#6092c0',
+ },
+ ],
+ layerType: 'referenceLine',
+ },
+ ],
},
{
+ id: 'memoryUsage',
title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.memoryUsage', {
defaultMessage: 'Memory Usage',
}),
- type: 'memoryUsage',
+ layers: [
+ {
+ data: [hostLensFormulas.memoryUsage],
+ layerType: 'data',
+ options: XY_LAYER_OPTIONS,
+ },
+ ],
+ overrides: PERCENT_LEFT_AXIS,
},
{
+ id: 'memoryFree',
title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.memoryFree', {
defaultMessage: 'Memory Free',
}),
- type: 'memoryFree',
+ layers: [
+ {
+ data: [hostLensFormulas.memoryFree],
+ layerType: 'data',
+ options: XY_LAYER_OPTIONS,
+ },
+ ],
},
{
+ id: 'diskSpaceUsed',
title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.diskSpaceUsed', {
defaultMessage: 'Disk Space Usage',
}),
- type: 'diskSpaceUsage',
+ layers: [
+ {
+ data: [hostLensFormulas.diskSpaceUsage],
+ layerType: 'data',
+ options: XY_LAYER_OPTIONS,
+ },
+ ],
+ overrides: PERCENT_LEFT_AXIS,
},
{
+ id: 'diskSpaceAvailable',
title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.diskSpaceAvailable', {
defaultMessage: 'Disk Space Available',
}),
- type: 'diskSpaceAvailable',
+ layers: [
+ {
+ data: [hostLensFormulas.diskSpaceAvailable],
+ layerType: 'data',
+ options: XY_LAYER_OPTIONS,
+ },
+ ],
},
{
+ id: 'diskIORead',
title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.diskIORead', {
defaultMessage: 'Disk Read IOPS',
}),
- type: 'diskIORead',
+ layers: [
+ {
+ data: [hostLensFormulas.diskIORead],
+ layerType: 'data',
+ options: XY_LAYER_OPTIONS,
+ },
+ ],
},
{
+ id: 'diskIOWrite',
title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.diskIOWrite', {
defaultMessage: 'Disk Write IOPS',
}),
- type: 'diskIOWrite',
+ layers: [
+ {
+ data: [hostLensFormulas.diskIOWrite],
+ layerType: 'data',
+ options: XY_LAYER_OPTIONS,
+ },
+ ],
},
{
+ id: 'diskReadThroughput',
title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.diskReadThroughput', {
defaultMessage: 'Disk Read Throughput',
}),
- type: 'diskReadThroughput',
+ layers: [
+ {
+ data: [hostLensFormulas.diskReadThroughput],
+ layerType: 'data',
+ options: XY_LAYER_OPTIONS,
+ },
+ ],
},
{
+ id: 'diskWriteThroughput',
title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.diskWriteThroughput', {
defaultMessage: 'Disk Write Throughput',
}),
- type: 'diskWriteThroughput',
+ layers: [
+ {
+ data: [hostLensFormulas.diskWriteThroughput],
+ layerType: 'data',
+ options: XY_LAYER_OPTIONS,
+ },
+ ],
},
{
+ id: 'rx',
title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.rx', {
defaultMessage: 'Network Inbound (RX)',
}),
- type: 'rx',
+ layers: [
+ {
+ data: [hostLensFormulas.rx],
+ layerType: 'data',
+ options: XY_LAYER_OPTIONS,
+ },
+ ],
},
{
+ id: 'tx',
title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.tx', {
defaultMessage: 'Network Outbound (TX)',
}),
- type: 'tx',
+ layers: [
+ {
+ data: [hostLensFormulas.tx],
+ layerType: 'data',
+ options: XY_LAYER_OPTIONS,
+ },
+ ],
},
];
@@ -94,9 +213,9 @@ export const MetricsGrid = React.memo(() => {
- {CHARTS_IN_ORDER.map(({ fullRow, ...chartProp }) => (
-
-
+ {CHARTS_IN_ORDER.map((chartProp, index) => (
+
+
))}
diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx
index 18310ad2317c0..19c0bcdaa1f47 100644
--- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx
@@ -31,7 +31,7 @@ import { useUnifiedSearchContext } from './use_unified_search';
import { useMetricsDataViewContext } from './use_data_view';
import { ColumnHeader } from '../components/table/column_header';
import { TABLE_COLUMN_LABEL } from '../translations';
-import { TOOLTIP } from '../../../../common/visualizations/lens/translations';
+import { TOOLTIP } from '../../../../common/visualizations/lens/dashboards/host/translations';
import { buildCombinedHostsFilter } from '../../../../utils/filters/build';
/**
@@ -255,7 +255,7 @@ export const useHostsTable = () => {
),
@@ -270,7 +270,7 @@ export const useHostsTable = () => {
),
@@ -285,7 +285,7 @@ export const useHostsTable = () => {
),
@@ -300,7 +300,7 @@ export const useHostsTable = () => {
),
@@ -315,7 +315,7 @@ export const useHostsTable = () => {
),
@@ -330,7 +330,7 @@ export const useHostsTable = () => {
),
@@ -346,7 +346,7 @@ export const useHostsTable = () => {
),
diff --git a/x-pack/plugins/infra/public/utils/filters/build.ts b/x-pack/plugins/infra/public/utils/filters/build.ts
index eba7b4d8ba032..4a71070ed2c5b 100644
--- a/x-pack/plugins/infra/public/utils/filters/build.ts
+++ b/x-pack/plugins/infra/public/utils/filters/build.ts
@@ -9,11 +9,33 @@ import {
BooleanRelation,
buildCombinedFilter,
buildPhraseFilter,
+ buildExistsFilter,
Filter,
isCombinedFilter,
} from '@kbn/es-query';
import type { DataView } from '@kbn/data-views-plugin/common';
+export const buildExistsHostsFilter = ({
+ field,
+ dataView,
+}: {
+ field: string;
+ dataView?: DataView;
+}) => {
+ if (!dataView) {
+ return {
+ meta: {},
+ query: {
+ exists: {
+ field,
+ },
+ },
+ };
+ }
+ const indexField = dataView.getFieldByName(field)!;
+ return buildExistsFilter(indexField, dataView);
+};
+
export const buildCombinedHostsFilter = ({
field,
values,
@@ -27,7 +49,7 @@ export const buildCombinedHostsFilter = ({
return {
query: {
terms: {
- 'host.name': values,
+ [field]: values,
},
},
meta: {},