Skip to content

Commit

Permalink
[8.x] fix(slo): timestamp field was allowing mutliple values (elastic…
Browse files Browse the repository at this point in the history
…#194311) (elastic#194562)

# Backport

This will backport the following commits from `main` to `8.x`:
- [fix(slo): timestamp field was allowing mutliple values
(elastic#194311)](elastic#194311)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Kevin
Delemme","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-01T12:22:24Z","message":"fix(slo):
timestamp field was allowing mutliple values
(elastic#194311)","sha":"c4e2a7256b76cc94c08f34bb6dc3faeff58777f1","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","v9.0.0","backport:prev-major","ci:project-deploy-observability","Team:obs-ux-management","v8.16.0"],"title":"fix(slo):
timestamp field was allowing mutliple
values","number":194311,"url":"https://github.com/elastic/kibana/pull/194311","mergeCommit":{"message":"fix(slo):
timestamp field was allowing mutliple values
(elastic#194311)","sha":"c4e2a7256b76cc94c08f34bb6dc3faeff58777f1"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/194311","number":194311,"mergeCommit":{"message":"fix(slo):
timestamp field was allowing mutliple values
(elastic#194311)","sha":"c4e2a7256b76cc94c08f34bb6dc3faeff58777f1"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Kevin Delemme <[email protected]>
  • Loading branch information
kibanamachine and kdelemme authored Oct 1, 2024
1 parent e032b89 commit 6bcd396
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,38 @@
* 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));

useEffect(() => {
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 });
}
Expand All @@ -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>
Expand All @@ -75,17 +82,15 @@ export function IndexFieldSelector({
return field.onChange(selected.map((selection) => selection.value));
}

field.onChange(defaultValue);
field.onChange([ALL_VALUE]);
}}
options={options}
onSearchChange={(searchValue: string) => {
setOptions(
createOptionsFromFields(indexFields, ({ value }) => value.includes(searchValue))
);
}}
selectedOptions={
!!indexFields && !!field.value ? getSelectedItems(field.value, indexFields) : []
}
selectedOptions={!!indexFields && !!field.value ? getSelectedItems(field.value) : []}
/>
);
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -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>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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>
Expand Down

0 comments on commit 6bcd396

Please sign in to comment.