fix(slo): timestamp field was allowing mutliple values (#194311)

This commit is contained in:
Kevin Delemme 2024-10-01 08:22:24 -04:00 committed by GitHub
parent ed9f9883d8
commit c4e2a7256b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 124 additions and 71 deletions

View file

@ -5,16 +5,13 @@
* 2.0.
*/
import { ALL_VALUE, QuerySchema } from '@kbn/slo-schema';
import { i18n } from '@kbn/i18n';
import { EuiIconTip } from '@elastic/eui';
import React from 'react';
import { DataView, FieldSpec } from '@kbn/data-views-plugin/common';
import { QuerySchema } from '@kbn/slo-schema';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import { OptionalText } from './optional_text';
import { CreateSLOForm } from '../../types';
import { IndexFieldSelector } from './index_field_selector';
import { GroupByCardinality } from './group_by_cardinality';
import { GroupByFieldSelector } from './group_by_field_selector';
export function GroupByField({
dataView,
@ -32,27 +29,8 @@ export function GroupByField({
return (
<>
<IndexFieldSelector
<GroupByFieldSelector
indexFields={groupByFields}
name="groupBy"
defaultValue={ALL_VALUE}
label={
<span>
{i18n.translate('xpack.slo.sloEdit.groupBy.label', {
defaultMessage: 'Group by',
})}{' '}
<EuiIconTip
content={i18n.translate('xpack.slo.sloEdit.groupBy.tooltip', {
defaultMessage: 'Create individual SLOs for each value of the selected field.',
})}
position="top"
/>
</span>
}
labelAppend={<OptionalText />}
placeholder={i18n.translate('xpack.slo.sloEdit.groupBy.placeholder', {
defaultMessage: 'Select an optional field to group by',
})}
isLoading={!!index && isLoading}
isDisabled={!index}
/>

View file

@ -5,35 +5,27 @@
* 2.0.
*/
import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui';
import React, { useEffect, useState, ReactNode } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow, EuiIconTip } from '@elastic/eui';
import { FieldSpec } from '@kbn/data-views-plugin/common';
import { createOptionsFromFields, Option } from '../../helpers/create_options';
import { i18n } from '@kbn/i18n';
import { ALL_VALUE } from '@kbn/slo-schema';
import React, { useEffect, useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { Option, createOptionsFromFields } from '../../helpers/create_options';
import { CreateSLOForm } from '../../types';
import { OptionalText } from './optional_text';
interface Props {
indexFields: FieldSpec[];
name: 'groupBy' | 'indicator.params.timestampField';
label: ReactNode | string;
placeholder: string;
isDisabled: boolean;
isLoading: boolean;
isRequired?: boolean;
defaultValue?: string;
labelAppend?: ReactNode;
}
export function IndexFieldSelector({
indexFields,
name,
label,
labelAppend,
placeholder,
isDisabled,
isLoading,
isRequired = false,
defaultValue = '',
}: Props) {
const placeholder = i18n.translate('xpack.slo.sloEdit.groupBy.placeholder', {
defaultMessage: 'Select an optional field to group by',
});
export function GroupByFieldSelector({ indexFields, isDisabled, isLoading }: Props) {
const { control, getFieldState } = useFormContext<CreateSLOForm>();
const [options, setOptions] = useState<Option[]>(createOptionsFromFields(indexFields));
@ -41,10 +33,10 @@ export function IndexFieldSelector({
setOptions(createOptionsFromFields(indexFields));
}, [indexFields]);
const getSelectedItems = (value: string | string[], fields: FieldSpec[]) => {
const getSelectedItems = (value: string | string[]) => {
const values = [value].flat();
const selectedItems: Array<EuiComboBoxOptionOption<string>> = [];
fields.forEach((field) => {
indexFields.forEach((field) => {
if (values.includes(field.name)) {
selectedItems.push({ value: field.name, label: field.name });
}
@ -53,12 +45,27 @@ export function IndexFieldSelector({
};
return (
<EuiFormRow label={label} isInvalid={getFieldState(name).invalid} labelAppend={labelAppend}>
<EuiFormRow
label={
<span>
{i18n.translate('xpack.slo.sloEdit.groupBy.label', {
defaultMessage: 'Group by',
})}{' '}
<EuiIconTip
content={i18n.translate('xpack.slo.sloEdit.groupBy.tooltip', {
defaultMessage: 'Create individual SLOs for each value of the selected field.',
})}
position="top"
/>
</span>
}
isInvalid={getFieldState('groupBy').invalid}
labelAppend={<OptionalText />}
>
<Controller
defaultValue={[defaultValue].flat()}
name={name}
defaultValue={[ALL_VALUE]}
name={'groupBy'}
control={control}
rules={{ required: isRequired && !isDisabled }}
render={({ field, fieldState }) => {
return (
<EuiComboBox<string>
@ -75,7 +82,7 @@ export function IndexFieldSelector({
return field.onChange(selected.map((selection) => selection.value));
}
field.onChange(defaultValue);
field.onChange([ALL_VALUE]);
}}
options={options}
onSearchChange={(searchValue: string) => {
@ -83,9 +90,7 @@ export function IndexFieldSelector({
createOptionsFromFields(indexFields, ({ value }) => value.includes(searchValue))
);
}}
selectedOptions={
!!indexFields && !!field.value ? getSelectedItems(field.value, indexFields) : []
}
selectedOptions={!!indexFields && !!field.value ? getSelectedItems(field.value) : []}
/>
);
}}

View file

@ -0,0 +1,79 @@
/*
* 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 { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui';
import { FieldSpec } from '@kbn/data-views-plugin/common';
import { i18n } from '@kbn/i18n';
import React, { useEffect, useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { Option, createOptionsFromFields } from '../../helpers/create_options';
import { CreateSLOForm } from '../../types';
interface Props {
fields: FieldSpec[];
isDisabled: boolean;
isLoading: boolean;
}
const placeholder = i18n.translate('xpack.slo.sloEdit.timestampField.placeholder', {
defaultMessage: 'Select a timestamp field',
});
export function TimestampFieldSelector({ fields, isDisabled, isLoading }: Props) {
const { control, getFieldState } = useFormContext<CreateSLOForm>();
const [options, setOptions] = useState<Option[]>(createOptionsFromFields(fields));
useEffect(() => {
setOptions(createOptionsFromFields(fields));
}, [fields]);
return (
<EuiFormRow
label={i18n.translate('xpack.slo.sloEdit.timestampField.label', {
defaultMessage: 'Timestamp field',
})}
isInvalid={getFieldState('indicator.params.timestampField').invalid}
>
<Controller
name={'indicator.params.timestampField'}
control={control}
rules={{ required: !isDisabled }}
render={({ field, fieldState }) => {
return (
<EuiComboBox<string>
{...field}
async
placeholder={placeholder}
aria-label={placeholder}
isClearable
isDisabled={isLoading || isDisabled}
isInvalid={fieldState.invalid}
isLoading={isLoading}
onChange={(selected: EuiComboBoxOptionOption[]) => {
if (selected.length) {
return field.onChange(selected[0].value);
}
field.onChange('');
}}
singleSelection={{ asPlainText: true }}
options={options}
onSearchChange={(searchValue: string) => {
setOptions(
createOptionsFromFields(fields, ({ value }) => value.includes(searchValue))
);
}}
selectedOptions={
!!fields && !!field.value ? [{ value: field.value, label: field.value }] : []
}
/>
);
}}
/>
</EuiFormRow>
);
}

View file

@ -6,13 +6,12 @@
*/
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { DataView } from '@kbn/data-views-plugin/public';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import { IndexSelection } from './index_selection';
import { IndexFieldSelector } from '../common/index_field_selector';
import { CreateSLOForm } from '../../types';
import { TimestampFieldSelector } from '../common/timestamp_field_selector';
import { IndexSelection } from './index_selection';
export function IndexAndTimestampField({
dataView,
@ -32,18 +31,10 @@ export function IndexAndTimestampField({
<IndexSelection selectedDataView={dataView} />
</EuiFlexItem>
<EuiFlexItem grow={2}>
<IndexFieldSelector
indexFields={timestampFields}
name="indicator.params.timestampField"
label={i18n.translate('xpack.slo.sloEdit.timestampField.label', {
defaultMessage: 'Timestamp field',
})}
placeholder={i18n.translate('xpack.slo.sloEdit.timestampField.placeholder', {
defaultMessage: 'Select a timestamp field',
})}
<TimestampFieldSelector
fields={timestampFields}
isLoading={!!index && isLoading}
isDisabled={!index}
isRequired
/>
</EuiFlexItem>
</EuiFlexGroup>