Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Shahzad <shahzad.muhammad@elastic.co>
This commit is contained in:
parent
4202cb42dc
commit
54b8d288b3
|
@ -20,6 +20,7 @@ export type {
|
|||
ValueLabelConfig,
|
||||
YAxisMode,
|
||||
XYCurveType,
|
||||
YConfig,
|
||||
} from './xy_visualization/types';
|
||||
export type { DataType, OperationMetadata } from './types';
|
||||
export type {
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
BROWSER_FAMILY_LABEL,
|
||||
BROWSER_VERSION_LABEL,
|
||||
CLS_LABEL,
|
||||
CORE_WEB_VITALS_LABEL,
|
||||
CPU_USAGE_LABEL,
|
||||
DEVICE_LABEL,
|
||||
ENVIRONMENT_LABEL,
|
||||
|
@ -92,6 +93,7 @@ export const DataViewLabels: Record<ReportViewTypeId, string> = {
|
|||
logs: LOGS_FREQUENCY_LABEL,
|
||||
mem: MEMORY_USAGE_LABEL,
|
||||
nwk: NETWORK_ACTIVITY_LABEL,
|
||||
cwv: CORE_WEB_VITALS_LABEL,
|
||||
};
|
||||
|
||||
export const ReportToDataTypeMap: Record<ReportViewTypeId, AppDataType> = {
|
||||
|
@ -105,4 +107,9 @@ export const ReportToDataTypeMap: Record<ReportViewTypeId, AppDataType> = {
|
|||
mem: 'infra_metrics',
|
||||
logs: 'infra_logs',
|
||||
cpu: 'infra_metrics',
|
||||
cwv: 'ux',
|
||||
};
|
||||
|
||||
export const USE_BREAK_DOWN_COLUMN = 'USE_BREAK_DOWN_COLUMN';
|
||||
export const FILTER_RECORDS = 'FILTER_RECORDS';
|
||||
export const OPERATION_COLUMN = 'operation';
|
||||
|
|
|
@ -119,6 +119,7 @@ export const TRANSACTION_URL = 'url.full';
|
|||
export const CLIENT_GEO = 'client.geo';
|
||||
export const USER_AGENT_DEVICE = 'user_agent.device.name';
|
||||
export const USER_AGENT_OS = 'user_agent.os.name';
|
||||
export const USER_AGENT_OS_VERSION = 'user_agent.os.version';
|
||||
|
||||
export const TRANSACTION_TIME_TO_FIRST_BYTE = 'transaction.marks.agent.timeToFirstByte';
|
||||
export const TRANSACTION_DOM_INTERACTIVE = 'transaction.marks.agent.domInteractive';
|
||||
|
|
|
@ -200,6 +200,14 @@ export const NETWORK_ACTIVITY_LABEL = i18n.translate(
|
|||
defaultMessage: 'Network activity',
|
||||
}
|
||||
);
|
||||
|
||||
export const CORE_WEB_VITALS_LABEL = i18n.translate(
|
||||
'xpack.observability.expView.fieldLabels.coreWebVitals',
|
||||
{
|
||||
defaultMessage: 'Core web vitals',
|
||||
}
|
||||
);
|
||||
|
||||
export const MEMORY_USAGE_LABEL = i18n.translate(
|
||||
'xpack.observability.expView.fieldLabels.memoryUsage',
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@ import { getMemoryUsageLensConfig } from './metrics/memory_usage_config';
|
|||
import { getNetworkActivityLensConfig } from './metrics/network_activity_config';
|
||||
import { getLogsFrequencyLensConfig } from './logs/logs_frequency_config';
|
||||
import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/index_patterns';
|
||||
import { getCoreWebVitalsConfig } from './rum/core_web_vitals_config';
|
||||
|
||||
interface Props {
|
||||
reportType: keyof typeof ReportViewTypes;
|
||||
|
@ -30,6 +31,8 @@ export const getDefaultConfigs = ({ reportType, seriesId, indexPattern }: Props)
|
|||
return getPerformanceDistLensConfig({ seriesId, indexPattern });
|
||||
case 'kpi-trends':
|
||||
return getKPITrendsLensConfig({ seriesId, indexPattern });
|
||||
case 'core-web-vitals':
|
||||
return getCoreWebVitalsConfig({ seriesId, indexPattern });
|
||||
case 'uptime-duration':
|
||||
return getMonitorDurationConfig({ seriesId, indexPattern });
|
||||
case 'uptime-pings':
|
||||
|
|
|
@ -342,7 +342,7 @@ describe('Lens Attribute', () => {
|
|||
orderBy: { columnId: 'y-axis-column', type: 'column' },
|
||||
orderDirection: 'desc',
|
||||
otherBucket: true,
|
||||
size: 3,
|
||||
size: 10,
|
||||
},
|
||||
scale: 'ordinal',
|
||||
sourceField: 'user_agent.name',
|
||||
|
|
|
@ -24,14 +24,15 @@ import {
|
|||
OperationMetadata,
|
||||
FieldBasedIndexPatternColumn,
|
||||
SumIndexPatternColumn,
|
||||
TermsIndexPatternColumn,
|
||||
} from '../../../../../../lens/public';
|
||||
import {
|
||||
buildPhraseFilter,
|
||||
buildPhrasesFilter,
|
||||
IndexPattern,
|
||||
} from '../../../../../../../../src/plugins/data/common';
|
||||
import { FieldLabels } from './constants';
|
||||
import { DataSeries, UrlFilter, URLReportDefinition } from '../types';
|
||||
import { FieldLabels, FILTER_RECORDS, USE_BREAK_DOWN_COLUMN } from './constants';
|
||||
import { ColumnFilter, DataSeries, UrlFilter, URLReportDefinition } from '../types';
|
||||
|
||||
function getLayerReferenceName(layerId: string) {
|
||||
return `indexpattern-datasource-layer-${layerId}`;
|
||||
|
@ -53,6 +54,7 @@ export const parseCustomFieldName = (
|
|||
) => {
|
||||
let fieldName = sourceField;
|
||||
let columnType;
|
||||
let columnFilters;
|
||||
|
||||
const rdf = reportViewConfig.reportDefinitions ?? [];
|
||||
|
||||
|
@ -61,17 +63,21 @@ export const parseCustomFieldName = (
|
|||
if (customField) {
|
||||
if (selectedDefinitions[fieldName]) {
|
||||
fieldName = selectedDefinitions[fieldName][0];
|
||||
if (customField?.options)
|
||||
columnType = customField?.options?.find(({ field }) => field === fieldName)?.columnType;
|
||||
} else if (customField.defaultValue) {
|
||||
fieldName = customField.defaultValue;
|
||||
} else if (customField.options?.[0].field) {
|
||||
fieldName = customField.options?.[0].field;
|
||||
if (customField?.options) {
|
||||
const currField = customField?.options?.find(
|
||||
({ field, id }) => field === fieldName || id === fieldName
|
||||
);
|
||||
columnType = currField?.columnType;
|
||||
columnFilters = currField?.columnFilters;
|
||||
}
|
||||
} else if (customField.options?.[0].field || customField.options?.[0].id) {
|
||||
fieldName = customField.options?.[0].field || customField.options?.[0].id;
|
||||
columnType = customField.options?.[0].columnType;
|
||||
columnFilters = customField.options?.[0].columnFilters;
|
||||
}
|
||||
}
|
||||
|
||||
return { fieldName, columnType };
|
||||
return { fieldName, columnType, columnFilters };
|
||||
};
|
||||
|
||||
export class LensAttributes {
|
||||
|
@ -82,6 +88,7 @@ export class LensAttributes {
|
|||
seriesType: SeriesType;
|
||||
reportViewConfig: DataSeries;
|
||||
reportDefinitions: URLReportDefinition;
|
||||
breakdownSource?: string;
|
||||
|
||||
constructor(
|
||||
indexPattern: IndexPattern,
|
||||
|
@ -89,12 +96,14 @@ export class LensAttributes {
|
|||
seriesType?: SeriesType,
|
||||
filters?: UrlFilter[],
|
||||
operationType?: OperationType,
|
||||
reportDefinitions?: URLReportDefinition
|
||||
reportDefinitions?: URLReportDefinition,
|
||||
breakdownSource?: string
|
||||
) {
|
||||
this.indexPattern = indexPattern;
|
||||
this.layers = {};
|
||||
this.filters = filters ?? [];
|
||||
this.reportDefinitions = reportDefinitions ?? {};
|
||||
this.breakdownSource = breakdownSource;
|
||||
|
||||
if (operationType) {
|
||||
reportViewConfig.yAxisColumns.forEach((yAxisColumn) => {
|
||||
|
@ -109,10 +118,10 @@ export class LensAttributes {
|
|||
this.visualization = this.getXyState();
|
||||
}
|
||||
|
||||
addBreakdown(sourceField: string) {
|
||||
getBreakdownColumn(sourceField: string): TermsIndexPatternColumn {
|
||||
const fieldMeta = this.indexPattern.getFieldByName(sourceField);
|
||||
|
||||
this.layers.layer1.columns['break-down-column'] = {
|
||||
return {
|
||||
sourceField,
|
||||
label: `Top values of ${FieldLabels[sourceField]}`,
|
||||
dataType: fieldMeta?.type as DataType,
|
||||
|
@ -120,13 +129,22 @@ export class LensAttributes {
|
|||
scale: 'ordinal',
|
||||
isBucketed: true,
|
||||
params: {
|
||||
size: 3,
|
||||
size: 10,
|
||||
orderBy: { type: 'column', columnId: 'y-axis-column' },
|
||||
orderDirection: 'desc',
|
||||
otherBucket: true,
|
||||
missingBucket: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
addBreakdown(sourceField: string) {
|
||||
const { xAxisColumn } = this.reportViewConfig;
|
||||
if (xAxisColumn?.sourceField === USE_BREAK_DOWN_COLUMN) {
|
||||
// do nothing since this will be used a x axis source
|
||||
return;
|
||||
}
|
||||
this.layers.layer1.columns['break-down-column'] = this.getBreakdownColumn(sourceField);
|
||||
|
||||
this.layers.layer1.columnOrder = [
|
||||
'x-axis-column',
|
||||
|
@ -229,15 +247,27 @@ export class LensAttributes {
|
|||
getXAxis() {
|
||||
const { xAxisColumn } = this.reportViewConfig;
|
||||
|
||||
if (xAxisColumn?.sourceField === USE_BREAK_DOWN_COLUMN) {
|
||||
return this.getBreakdownColumn(this.breakdownSource || this.reportViewConfig.breakdowns[0]);
|
||||
}
|
||||
|
||||
return this.getColumnBasedOnType(xAxisColumn.sourceField!, undefined, xAxisColumn.label);
|
||||
}
|
||||
|
||||
getColumnBasedOnType(sourceField: string, operationType?: OperationType, label?: string) {
|
||||
const { fieldMeta, columnType, fieldName } = this.getFieldMeta(sourceField);
|
||||
getColumnBasedOnType(
|
||||
sourceField: string,
|
||||
operationType?: OperationType,
|
||||
label?: string,
|
||||
colIndex?: number
|
||||
) {
|
||||
const { fieldMeta, columnType, fieldName, columnFilters } = this.getFieldMeta(sourceField);
|
||||
const { type: fieldType } = fieldMeta ?? {};
|
||||
|
||||
if (fieldName === 'Records') {
|
||||
return this.getRecordsColumn();
|
||||
if (fieldName === 'Records' || columnType === FILTER_RECORDS) {
|
||||
return this.getRecordsColumn(
|
||||
label,
|
||||
colIndex !== undefined ? columnFilters?.[colIndex] : undefined
|
||||
);
|
||||
}
|
||||
|
||||
if (fieldType === 'date') {
|
||||
|
@ -256,11 +286,11 @@ export class LensAttributes {
|
|||
}
|
||||
|
||||
getFieldMeta(sourceField: string) {
|
||||
const { fieldName, columnType } = this.getCustomFieldName(sourceField);
|
||||
const { fieldName, columnType, columnFilters } = this.getCustomFieldName(sourceField);
|
||||
|
||||
const fieldMeta = this.indexPattern.getFieldByName(fieldName);
|
||||
|
||||
return { fieldMeta, fieldName, columnType };
|
||||
return { fieldMeta, fieldName, columnType, columnFilters };
|
||||
}
|
||||
|
||||
getMainYAxis() {
|
||||
|
@ -270,7 +300,7 @@ export class LensAttributes {
|
|||
return this.getRecordsColumn(label);
|
||||
}
|
||||
|
||||
return this.getColumnBasedOnType(sourceField!, operationType, label);
|
||||
return this.getColumnBasedOnType(sourceField!, operationType, label, 0);
|
||||
}
|
||||
|
||||
getChildYAxises() {
|
||||
|
@ -286,13 +316,14 @@ export class LensAttributes {
|
|||
lensColumns[`y-axis-column-${i}`] = this.getColumnBasedOnType(
|
||||
sourceField!,
|
||||
operationType,
|
||||
label
|
||||
label,
|
||||
i
|
||||
);
|
||||
}
|
||||
return lensColumns;
|
||||
}
|
||||
|
||||
getRecordsColumn(label?: string): CountIndexPatternColumn {
|
||||
getRecordsColumn(label?: string, columnFilter?: ColumnFilter): CountIndexPatternColumn {
|
||||
return {
|
||||
dataType: 'number',
|
||||
isBucketed: false,
|
||||
|
@ -300,6 +331,7 @@ export class LensAttributes {
|
|||
operationType: 'count',
|
||||
scale: 'ratio',
|
||||
sourceField: 'Records',
|
||||
filter: columnFilter,
|
||||
} as CountIndexPatternColumn;
|
||||
}
|
||||
|
||||
|
@ -331,7 +363,9 @@ export class LensAttributes {
|
|||
layerId: 'layer1',
|
||||
seriesType: this.seriesType ?? 'line',
|
||||
palette: this.reportViewConfig.palette,
|
||||
yConfig: [{ forAccessor: 'y-axis-column', color: 'green' }],
|
||||
yConfig: this.reportViewConfig.yConfig || [
|
||||
{ forAccessor: 'y-axis-column', color: 'green' },
|
||||
],
|
||||
xAccessor: 'x-axis-column',
|
||||
},
|
||||
],
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* 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 { euiPaletteForStatus } from '@elastic/eui';
|
||||
import { ConfigProps, DataSeries } from '../../types';
|
||||
import { FieldLabels, FILTER_RECORDS, USE_BREAK_DOWN_COLUMN } from '../constants';
|
||||
import { buildPhraseFilter } from '../utils';
|
||||
import {
|
||||
CLIENT_GEO_COUNTRY_NAME,
|
||||
CLS_FIELD,
|
||||
FID_FIELD,
|
||||
LCP_FIELD,
|
||||
PROCESSOR_EVENT,
|
||||
SERVICE_NAME,
|
||||
TRANSACTION_TYPE,
|
||||
USER_AGENT_DEVICE,
|
||||
USER_AGENT_NAME,
|
||||
USER_AGENT_OS,
|
||||
USER_AGENT_VERSION,
|
||||
TRANSACTION_URL,
|
||||
USER_AGENT_OS_VERSION,
|
||||
URL_FULL,
|
||||
SERVICE_ENVIRONMENT,
|
||||
} from '../constants/elasticsearch_fieldnames';
|
||||
|
||||
export function getCoreWebVitalsConfig({ seriesId, indexPattern }: ConfigProps): DataSeries {
|
||||
const statusPallete = euiPaletteForStatus(3);
|
||||
|
||||
return {
|
||||
id: seriesId,
|
||||
defaultSeriesType: 'bar_horizontal_percentage_stacked',
|
||||
reportType: 'kpi-trends',
|
||||
seriesTypes: ['bar_horizontal_percentage_stacked'],
|
||||
xAxisColumn: {
|
||||
sourceField: USE_BREAK_DOWN_COLUMN,
|
||||
},
|
||||
yAxisColumns: [
|
||||
{
|
||||
sourceField: 'core.web.vitals',
|
||||
label: 'Good',
|
||||
},
|
||||
{
|
||||
sourceField: 'core.web.vitals',
|
||||
label: 'Average',
|
||||
},
|
||||
{
|
||||
sourceField: 'core.web.vitals',
|
||||
label: 'Poor',
|
||||
},
|
||||
],
|
||||
hasOperationType: false,
|
||||
defaultFilters: [
|
||||
{
|
||||
field: TRANSACTION_URL,
|
||||
isNegated: false,
|
||||
},
|
||||
SERVICE_NAME,
|
||||
{
|
||||
field: USER_AGENT_OS,
|
||||
nested: USER_AGENT_OS_VERSION,
|
||||
},
|
||||
CLIENT_GEO_COUNTRY_NAME,
|
||||
USER_AGENT_DEVICE,
|
||||
{
|
||||
field: USER_AGENT_NAME,
|
||||
nested: USER_AGENT_VERSION,
|
||||
},
|
||||
],
|
||||
breakdowns: [
|
||||
SERVICE_NAME,
|
||||
USER_AGENT_NAME,
|
||||
USER_AGENT_OS,
|
||||
CLIENT_GEO_COUNTRY_NAME,
|
||||
USER_AGENT_DEVICE,
|
||||
URL_FULL,
|
||||
],
|
||||
filters: [
|
||||
...buildPhraseFilter(TRANSACTION_TYPE, 'page-load', indexPattern),
|
||||
...buildPhraseFilter(PROCESSOR_EVENT, 'transaction', indexPattern),
|
||||
],
|
||||
labels: { ...FieldLabels, [SERVICE_NAME]: 'Web Application' },
|
||||
reportDefinitions: [
|
||||
{
|
||||
field: SERVICE_NAME,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: SERVICE_ENVIRONMENT,
|
||||
},
|
||||
{
|
||||
field: 'core.web.vitals',
|
||||
custom: true,
|
||||
options: [
|
||||
{
|
||||
id: LCP_FIELD,
|
||||
label: 'Largest contentful paint',
|
||||
columnType: FILTER_RECORDS,
|
||||
columnFilters: [
|
||||
{
|
||||
language: 'kuery',
|
||||
query: `${LCP_FIELD} < 2500`,
|
||||
},
|
||||
{
|
||||
language: 'kuery',
|
||||
query: `${LCP_FIELD} > 2500 and ${LCP_FIELD} < 4000`,
|
||||
},
|
||||
{
|
||||
language: 'kuery',
|
||||
query: `${LCP_FIELD} > 4000`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'First input delay',
|
||||
id: FID_FIELD,
|
||||
columnType: FILTER_RECORDS,
|
||||
columnFilters: [
|
||||
{
|
||||
language: 'kuery',
|
||||
query: `${FID_FIELD} < 100`,
|
||||
},
|
||||
{
|
||||
language: 'kuery',
|
||||
query: `${FID_FIELD} > 100 and ${FID_FIELD} < 300`,
|
||||
},
|
||||
{
|
||||
language: 'kuery',
|
||||
query: `${FID_FIELD} > 300`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Cumulative layout shift',
|
||||
id: CLS_FIELD,
|
||||
columnType: FILTER_RECORDS,
|
||||
columnFilters: [
|
||||
{
|
||||
language: 'kuery',
|
||||
query: `${CLS_FIELD} < 0.1`,
|
||||
},
|
||||
{
|
||||
language: 'kuery',
|
||||
query: `${CLS_FIELD} > 0.1 and ${CLS_FIELD} < 0.25`,
|
||||
},
|
||||
{
|
||||
language: 'kuery',
|
||||
query: `${CLS_FIELD} > 0.25`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
yConfig: [
|
||||
{ color: statusPallete[0], forAccessor: 'y-axis-column' },
|
||||
{ color: statusPallete[1], forAccessor: 'y-axis-column-1' },
|
||||
{ color: statusPallete[2], forAccessor: 'y-axis-column-2' },
|
||||
],
|
||||
};
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { ConfigProps, DataSeries } from '../../types';
|
||||
import { FieldLabels, RECORDS_FIELD } from '../constants';
|
||||
import { FieldLabels, OPERATION_COLUMN, RECORDS_FIELD } from '../constants';
|
||||
import { buildPhraseFilter } from '../utils';
|
||||
import {
|
||||
CLIENT_GEO_COUNTRY_NAME,
|
||||
|
@ -85,20 +85,25 @@ export function getKPITrendsLensConfig({ seriesId, indexPattern }: ConfigProps):
|
|||
{
|
||||
field: 'business.kpi',
|
||||
custom: true,
|
||||
defaultValue: RECORDS_FIELD,
|
||||
options: [
|
||||
{ field: RECORDS_FIELD, label: PAGE_VIEWS_LABEL },
|
||||
{ label: PAGE_LOAD_TIME_LABEL, field: TRANSACTION_DURATION, columnType: 'operation' },
|
||||
{ field: RECORDS_FIELD, id: RECORDS_FIELD, label: PAGE_VIEWS_LABEL },
|
||||
{
|
||||
label: PAGE_LOAD_TIME_LABEL,
|
||||
field: TRANSACTION_DURATION,
|
||||
id: TRANSACTION_DURATION,
|
||||
columnType: OPERATION_COLUMN,
|
||||
},
|
||||
{
|
||||
label: BACKEND_TIME_LABEL,
|
||||
field: TRANSACTION_TIME_TO_FIRST_BYTE,
|
||||
columnType: 'operation',
|
||||
id: TRANSACTION_TIME_TO_FIRST_BYTE,
|
||||
columnType: OPERATION_COLUMN,
|
||||
},
|
||||
{ label: FCP_LABEL, field: FCP_FIELD, columnType: 'operation' },
|
||||
{ label: TBT_LABEL, field: TBT_FIELD, columnType: 'operation' },
|
||||
{ label: LCP_LABEL, field: LCP_FIELD, columnType: 'operation' },
|
||||
{ label: FID_LABEL, field: FID_FIELD, columnType: 'operation' },
|
||||
{ label: CLS_LABEL, field: CLS_FIELD, columnType: 'operation' },
|
||||
{ label: FCP_LABEL, field: FCP_FIELD, id: FCP_FIELD, columnType: OPERATION_COLUMN },
|
||||
{ label: TBT_LABEL, field: TBT_FIELD, id: TBT_FIELD, columnType: OPERATION_COLUMN },
|
||||
{ label: LCP_LABEL, field: LCP_FIELD, id: LCP_FIELD, columnType: OPERATION_COLUMN },
|
||||
{ label: FID_LABEL, field: FID_FIELD, id: FID_FIELD, columnType: OPERATION_COLUMN },
|
||||
{ label: CLS_LABEL, field: CLS_FIELD, id: CLS_FIELD, columnType: OPERATION_COLUMN },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -80,15 +80,18 @@ export function getPerformanceDistLensConfig({ seriesId, indexPattern }: ConfigP
|
|||
{
|
||||
field: 'performance.metric',
|
||||
custom: true,
|
||||
defaultValue: TRANSACTION_DURATION,
|
||||
options: [
|
||||
{ label: PAGE_LOAD_TIME_LABEL, field: TRANSACTION_DURATION },
|
||||
{ label: BACKEND_TIME_LABEL, field: TRANSACTION_TIME_TO_FIRST_BYTE },
|
||||
{ label: FCP_LABEL, field: FCP_FIELD },
|
||||
{ label: TBT_LABEL, field: TBT_FIELD },
|
||||
{ label: LCP_LABEL, field: LCP_FIELD },
|
||||
{ label: FID_LABEL, field: FID_FIELD },
|
||||
{ label: CLS_LABEL, field: CLS_FIELD },
|
||||
{ label: PAGE_LOAD_TIME_LABEL, id: TRANSACTION_DURATION, field: TRANSACTION_DURATION },
|
||||
{
|
||||
label: BACKEND_TIME_LABEL,
|
||||
id: TRANSACTION_TIME_TO_FIRST_BYTE,
|
||||
field: TRANSACTION_TIME_TO_FIRST_BYTE,
|
||||
},
|
||||
{ label: FCP_LABEL, id: FCP_FIELD, field: FCP_FIELD },
|
||||
{ label: TBT_LABEL, id: TBT_FIELD, field: TBT_FIELD },
|
||||
{ label: LCP_LABEL, id: LCP_FIELD, field: LCP_FIELD },
|
||||
{ label: FID_LABEL, id: FID_FIELD, field: FID_FIELD },
|
||||
{ label: CLS_LABEL, id: CLS_FIELD, field: CLS_FIELD },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -67,7 +67,8 @@ export const useLensAttributes = ({
|
|||
seriesType,
|
||||
filters,
|
||||
operationType,
|
||||
reportDefinitions
|
||||
reportDefinitions,
|
||||
breakdown
|
||||
);
|
||||
|
||||
if (breakdown) {
|
||||
|
|
|
@ -20,9 +20,11 @@ const CHART_TYPE_LABEL = i18n.translate('xpack.observability.expView.chartTypes.
|
|||
|
||||
export function SeriesChartTypesSelect({
|
||||
seriesId,
|
||||
seriesTypes,
|
||||
defaultChartType,
|
||||
}: {
|
||||
seriesId: string;
|
||||
seriesTypes?: SeriesType[];
|
||||
defaultChartType: SeriesType;
|
||||
}) {
|
||||
const { series, setSeries, allSeries } = useUrlStorage(seriesId);
|
||||
|
@ -42,8 +44,18 @@ export function SeriesChartTypesSelect({
|
|||
onChange={onChange}
|
||||
value={seriesType}
|
||||
excludeChartTypes={['bar_percentage_stacked']}
|
||||
includeChartTypes={
|
||||
seriesTypes || [
|
||||
'bar',
|
||||
'bar_horizontal',
|
||||
'line',
|
||||
'area',
|
||||
'bar_stacked',
|
||||
'area_stacked',
|
||||
'bar_horizontal_percentage_stacked',
|
||||
]
|
||||
}
|
||||
label={CHART_TYPE_LABEL}
|
||||
includeChartTypes={['bar', 'bar_horizontal', 'line', 'area', 'bar_stacked', 'area_stacked']}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,5 +16,11 @@ export function ReportBreakdowns({
|
|||
dataViewSeries: DataSeries;
|
||||
seriesId: string;
|
||||
}) {
|
||||
return <Breakdowns breakdowns={dataViewSeries.breakdowns ?? []} seriesId={seriesId} />;
|
||||
return (
|
||||
<Breakdowns
|
||||
reportViewConfig={dataViewSeries}
|
||||
breakdowns={dataViewSeries.breakdowns ?? []}
|
||||
seriesId={seriesId}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui';
|
||||
import styled from 'styled-components';
|
||||
import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern';
|
||||
import { useUrlStorage } from '../../hooks/use_url_storage';
|
||||
|
@ -66,8 +66,9 @@ export function ReportDefinitionCol({
|
|||
<EuiFlexItem>
|
||||
<DatePickerCol seriesId={seriesId} />
|
||||
</EuiFlexItem>
|
||||
<EuiHorizontalRule margin="xs" />
|
||||
{indexPattern &&
|
||||
reportDefinitions.map(({ field, custom, options, defaultValue }) => (
|
||||
reportDefinitions.map(({ field, custom, options }) => (
|
||||
<EuiFlexItem key={field}>
|
||||
{!custom ? (
|
||||
<ReportDefinitionField
|
||||
|
@ -77,12 +78,7 @@ export function ReportDefinitionCol({
|
|||
onChange={onChange}
|
||||
/>
|
||||
) : (
|
||||
<CustomReportField
|
||||
field={field}
|
||||
options={options}
|
||||
defaultValue={defaultValue}
|
||||
seriesId={seriesId}
|
||||
/>
|
||||
<CustomReportField field={field} options={options} seriesId={seriesId} />
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
|
@ -95,7 +91,11 @@ export function ReportDefinitionCol({
|
|||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem>
|
||||
<SeriesChartTypesSelect seriesId={seriesId} defaultChartType={defaultSeriesType} />
|
||||
<SeriesChartTypesSelect
|
||||
seriesId={seriesId}
|
||||
defaultChartType={defaultSeriesType}
|
||||
seriesTypes={dataViewSeries.seriesTypes}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</FlexGroup>
|
||||
);
|
||||
|
|
|
@ -17,7 +17,7 @@ interface Props {
|
|||
options: ReportDefinition['options'];
|
||||
}
|
||||
|
||||
export function CustomReportField({ field, seriesId, options: opts, defaultValue }: Props) {
|
||||
export function CustomReportField({ field, seriesId, options: opts }: Props) {
|
||||
const { series, setSeries } = useUrlStorage(seriesId);
|
||||
|
||||
const { reportDefinitions: rtd = {} } = series;
|
||||
|
@ -35,11 +35,11 @@ export function CustomReportField({ field, seriesId, options: opts, defaultValue
|
|||
fullWidth
|
||||
compressed
|
||||
prepend={'Metric'}
|
||||
options={options.map(({ label, field: fd }) => ({
|
||||
value: fd,
|
||||
options={options.map(({ label, field: fd, id }) => ({
|
||||
value: fd || id,
|
||||
inputDisplay: label,
|
||||
}))}
|
||||
valueOfSelected={reportDefinitions?.[field]?.[0] || defaultValue || options?.[0].field}
|
||||
valueOfSelected={reportDefinitions?.[field]?.[0] || options?.[0].field || options?.[0].id}
|
||||
onChange={(value) => onChange(value)}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -27,6 +27,7 @@ export const ReportTypes: Record<AppDataType, Array<{ id: ReportViewTypeId; labe
|
|||
ux: [
|
||||
{ id: 'pld', label: 'Performance distribution' },
|
||||
{ id: 'kpi', label: 'KPI over time' },
|
||||
{ id: 'cwv', label: 'Core Web Vitals' },
|
||||
],
|
||||
apm: [
|
||||
{ id: 'svl', label: 'Latency' },
|
||||
|
|
|
@ -20,7 +20,7 @@ export function ChartEditOptions({ series, seriesId, breakdowns }: Props) {
|
|||
return (
|
||||
<EuiFlexGroup wrap>
|
||||
<EuiFlexItem>
|
||||
<Breakdowns seriesId={seriesId} breakdowns={breakdowns} />
|
||||
<Breakdowns seriesId={seriesId} breakdowns={breakdowns} reportViewConfig={series} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<ChartOptions series={series} />
|
||||
|
|
|
@ -23,7 +23,13 @@ describe('Breakdowns', function () {
|
|||
it('should render properly', async function () {
|
||||
mockUrlStorage({});
|
||||
|
||||
render(<Breakdowns seriesId={'series-id'} breakdowns={dataViewSeries.breakdowns} />);
|
||||
render(
|
||||
<Breakdowns
|
||||
seriesId={'series-id'}
|
||||
breakdowns={dataViewSeries.breakdowns}
|
||||
reportViewConfig={dataViewSeries}
|
||||
/>
|
||||
);
|
||||
|
||||
screen.getAllByText('Browser family');
|
||||
});
|
||||
|
@ -31,7 +37,13 @@ describe('Breakdowns', function () {
|
|||
it('should call set series on change', function () {
|
||||
const { setSeries } = mockUrlStorage({ breakdown: USER_AGENT_OS });
|
||||
|
||||
render(<Breakdowns seriesId={'series-id'} breakdowns={dataViewSeries.breakdowns} />);
|
||||
render(
|
||||
<Breakdowns
|
||||
seriesId={'series-id'}
|
||||
breakdowns={dataViewSeries.breakdowns}
|
||||
reportViewConfig={dataViewSeries}
|
||||
/>
|
||||
);
|
||||
|
||||
screen.getAllByText('Operating system');
|
||||
|
||||
|
|
|
@ -8,15 +8,17 @@
|
|||
import React from 'react';
|
||||
import { EuiSuperSelect } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FieldLabels } from '../../configurations/constants';
|
||||
import { USE_BREAK_DOWN_COLUMN } from '../../configurations/constants';
|
||||
import { useUrlStorage } from '../../hooks/use_url_storage';
|
||||
import { DataSeries } from '../../types';
|
||||
|
||||
interface Props {
|
||||
seriesId: string;
|
||||
breakdowns: string[];
|
||||
reportViewConfig: DataSeries;
|
||||
}
|
||||
|
||||
export function Breakdowns({ seriesId, breakdowns = [] }: Props) {
|
||||
export function Breakdowns({ reportViewConfig, seriesId, breakdowns = [] }: Props) {
|
||||
const { setSeries, series } = useUrlStorage(seriesId);
|
||||
|
||||
const selectedBreakdown = series.breakdown;
|
||||
|
@ -36,13 +38,21 @@ export function Breakdowns({ seriesId, breakdowns = [] }: Props) {
|
|||
}
|
||||
};
|
||||
|
||||
const items = breakdowns.map((breakdown) => ({ id: breakdown, label: FieldLabels[breakdown] }));
|
||||
items.push({
|
||||
id: NO_BREAKDOWN,
|
||||
label: i18n.translate('xpack.observability.exp.breakDownFilter.noBreakdown', {
|
||||
defaultMessage: 'No breakdown',
|
||||
}),
|
||||
});
|
||||
const hasUseBreakdownColumn = reportViewConfig.xAxisColumn.sourceField === USE_BREAK_DOWN_COLUMN;
|
||||
|
||||
const items = breakdowns.map((breakdown) => ({
|
||||
id: breakdown,
|
||||
label: reportViewConfig.labels[breakdown],
|
||||
}));
|
||||
|
||||
if (!hasUseBreakdownColumn) {
|
||||
items.push({
|
||||
id: NO_BREAKDOWN,
|
||||
label: i18n.translate('xpack.observability.exp.breakDownFilter.noBreakdown', {
|
||||
defaultMessage: 'No breakdown',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
const options = items.map(({ id, label }) => ({
|
||||
inputDisplay: id === NO_BREAKDOWN ? label : <strong>{label}</strong>,
|
||||
|
@ -50,13 +60,16 @@ export function Breakdowns({ seriesId, breakdowns = [] }: Props) {
|
|||
dropdownDisplay: label,
|
||||
}));
|
||||
|
||||
const valueOfSelected =
|
||||
selectedBreakdown || (hasUseBreakdownColumn ? options[0].value : NO_BREAKDOWN);
|
||||
|
||||
return (
|
||||
<div style={{ width: 200 }}>
|
||||
<EuiSuperSelect
|
||||
fullWidth
|
||||
compressed
|
||||
options={options}
|
||||
valueOfSelected={selectedBreakdown ?? NO_BREAKDOWN}
|
||||
valueOfSelected={valueOfSelected}
|
||||
onChange={(value) => onOptionChange(value)}
|
||||
data-test-subj={'seriesBreakdown'}
|
||||
/>
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
FieldBasedIndexPatternColumn,
|
||||
SeriesType,
|
||||
OperationType,
|
||||
YConfig,
|
||||
} from '../../../../../lens/public';
|
||||
|
||||
import { PersistableFilter } from '../../../../../lens/common';
|
||||
|
@ -21,6 +22,7 @@ import { ExistsFilter } from '../../../../../../../src/plugins/data/common/es_qu
|
|||
export const ReportViewTypes = {
|
||||
pld: 'page-load-dist',
|
||||
kpi: 'kpi-trends',
|
||||
cwv: 'core-web-vitals',
|
||||
upd: 'uptime-duration',
|
||||
upp: 'uptime-pings',
|
||||
svl: 'service-latency',
|
||||
|
@ -37,16 +39,22 @@ export type ReportViewTypeId = keyof typeof ReportViewTypes;
|
|||
|
||||
export type ReportViewType = ValueOf<typeof ReportViewTypes>;
|
||||
|
||||
export interface ColumnFilter {
|
||||
language: 'kuery';
|
||||
query: string;
|
||||
}
|
||||
|
||||
export interface ReportDefinition {
|
||||
field: string;
|
||||
required?: boolean;
|
||||
custom?: boolean;
|
||||
defaultValue?: string;
|
||||
options?: Array<{
|
||||
field: string;
|
||||
id: string;
|
||||
field?: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
columnType?: 'range' | 'operation';
|
||||
columnType?: 'range' | 'operation' | 'FILTER_RECORDS';
|
||||
columnFilters?: ColumnFilter[];
|
||||
}>;
|
||||
}
|
||||
|
||||
|
@ -66,6 +74,7 @@ export interface DataSeries {
|
|||
hasOperationType: boolean;
|
||||
palette?: PaletteOutput;
|
||||
yTitle?: string;
|
||||
yConfig?: YConfig[];
|
||||
}
|
||||
|
||||
export type URLReportDefinition = Record<string, string[]>;
|
||||
|
|
Loading…
Reference in a new issue