mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
feat(slo): add group by field selector in forms (#163202)
This commit is contained in:
parent
a038fb09ca
commit
5d813006a3
12 changed files with 117 additions and 17 deletions
|
@ -18,6 +18,8 @@ export interface UseFetchIndexPatternFieldsResponse {
|
|||
export interface Field {
|
||||
name: string;
|
||||
type: string;
|
||||
aggregatable: boolean;
|
||||
searchable: boolean;
|
||||
}
|
||||
|
||||
export function useFetchIndexPatternFields(
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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,
|
||||
EuiFlexItem,
|
||||
EuiFormRow,
|
||||
EuiIconTip,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ALL_VALUE } from '@kbn/slo-schema';
|
||||
import React from 'react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
|
||||
import { createOptionsFromFields } from '../../helpers/create_options';
|
||||
import { CreateSLOForm } from '../../types';
|
||||
|
||||
interface Props {
|
||||
index?: string;
|
||||
}
|
||||
export function GroupByFieldSelector({ index }: Props) {
|
||||
const { control, getFieldState } = useFormContext<CreateSLOForm>();
|
||||
const { isLoading, data: indexFields = [] } = useFetchIndexPatternFields(index);
|
||||
const groupableFields = indexFields.filter((field) => field.aggregatable);
|
||||
|
||||
const label = i18n.translate('xpack.observability.slo.sloEdit.groupBy.placeholder', {
|
||||
defaultMessage: 'Select an optional field to partition by',
|
||||
});
|
||||
|
||||
return (
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
label={
|
||||
<span>
|
||||
{i18n.translate('xpack.observability.slo.sloEdit.groupBy.label', {
|
||||
defaultMessage: 'Partition by',
|
||||
})}{' '}
|
||||
<EuiIconTip
|
||||
content={i18n.translate('xpack.observability.slo.sloEdit.groupBy.tooltip', {
|
||||
defaultMessage: 'Create individual SLOs for each value of the selected field.',
|
||||
})}
|
||||
position="top"
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
isInvalid={getFieldState('groupBy').invalid}
|
||||
>
|
||||
<Controller
|
||||
defaultValue={ALL_VALUE}
|
||||
name="groupBy"
|
||||
control={control}
|
||||
rules={{ required: false }}
|
||||
render={({ field, fieldState }) => (
|
||||
<EuiComboBox
|
||||
{...field}
|
||||
async
|
||||
placeholder={label}
|
||||
aria-label={label}
|
||||
isClearable
|
||||
isDisabled={!index}
|
||||
isInvalid={fieldState.invalid}
|
||||
isLoading={!!index && isLoading}
|
||||
onChange={(selected: EuiComboBoxOptionOption[]) => {
|
||||
if (selected.length) {
|
||||
return field.onChange(selected[0].value);
|
||||
}
|
||||
|
||||
field.onChange(ALL_VALUE);
|
||||
}}
|
||||
options={createOptionsFromFields(groupableFields)}
|
||||
selectedOptions={
|
||||
!!index &&
|
||||
!!field.value &&
|
||||
groupableFields.some((groupableField) => groupableField.name === field.value)
|
||||
? [{ value: field.value, label: field.value }]
|
||||
: []
|
||||
}
|
||||
singleSelection
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
}
|
|
@ -36,7 +36,6 @@ export function QueryBuilder({
|
|||
useKibana().services;
|
||||
|
||||
const { control, getFieldState } = useFormContext<CreateSLOForm>();
|
||||
|
||||
const { dataView } = useCreateDataView({ indexPatternString });
|
||||
|
||||
return (
|
||||
|
|
|
@ -20,6 +20,7 @@ import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_inde
|
|||
import { createOptionsFromFields } from '../../helpers/create_options';
|
||||
import { CreateSLOForm } from '../../types';
|
||||
import { DataPreviewChart } from '../common/data_preview_chart';
|
||||
import { GroupByFieldSelector } from '../common/group_by_field_selector';
|
||||
import { QueryBuilder } from '../common/query_builder';
|
||||
import { IndexSelection } from '../custom_common/index_selection';
|
||||
|
||||
|
@ -81,7 +82,7 @@ export function CustomKqlIndicatorTypeForm() {
|
|||
? [{ value: field.value, label: field.value }]
|
||||
: []
|
||||
}
|
||||
singleSelection={{ asPlainText: true }}
|
||||
singleSelection
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@ -175,6 +176,8 @@ export function CustomKqlIndicatorTypeForm() {
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
<GroupByFieldSelector index={index} />
|
||||
|
||||
<DataPreviewChart />
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
|
|
@ -27,15 +27,15 @@ import { DataPreviewChart } from '../common/data_preview_chart';
|
|||
import { QueryBuilder } from '../common/query_builder';
|
||||
import { IndexSelection } from '../custom_common/index_selection';
|
||||
import { MetricIndicator } from './metric_indicator';
|
||||
import { GroupByFieldSelector } from '../common/group_by_field_selector';
|
||||
|
||||
export { NEW_CUSTOM_METRIC } from './metric_indicator';
|
||||
|
||||
export function CustomMetricIndicatorTypeForm() {
|
||||
const { control, watch, getFieldState } = useFormContext<CreateSLOForm>();
|
||||
|
||||
const { isLoading, data: indexFields } = useFetchIndexPatternFields(
|
||||
watch('indicator.params.index')
|
||||
);
|
||||
const index = watch('indicator.params.index');
|
||||
const { isLoading, data: indexFields } = useFetchIndexPatternFields(index);
|
||||
const timestampFields = (indexFields ?? []).filter((field) => field.type === 'date');
|
||||
|
||||
return (
|
||||
|
@ -181,6 +181,8 @@ export function CustomMetricIndicatorTypeForm() {
|
|||
<EuiHorizontalRule margin="none" />
|
||||
</EuiFlexItem>
|
||||
|
||||
<GroupByFieldSelector index={index} />
|
||||
|
||||
<DataPreviewChart />
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
|
|
|
@ -27,6 +27,7 @@ import { DataPreviewChart } from '../common/data_preview_chart';
|
|||
import { QueryBuilder } from '../common/query_builder';
|
||||
import { IndexSelection } from '../custom_common/index_selection';
|
||||
import { HistogramIndicator } from './histogram_indicator';
|
||||
import { GroupByFieldSelector } from '../common/group_by_field_selector';
|
||||
|
||||
export function HistogramIndicatorTypeForm() {
|
||||
const { control, watch, getFieldState } = useFormContext<CreateSLOForm>();
|
||||
|
@ -163,6 +164,9 @@ export function HistogramIndicatorTypeForm() {
|
|||
<EuiFlexItem>
|
||||
<EuiHorizontalRule margin="none" />
|
||||
</EuiFlexItem>
|
||||
|
||||
<GroupByFieldSelector index={index} />
|
||||
|
||||
<DataPreviewChart />
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { CreateSLOInput, SLOWithSummaryResponse, UpdateSLOInput } from '@kbn/slo-schema';
|
||||
import { CreateSLOInput, SLOWithSummaryResponse, UpdateSLOInput } from '@kbn/slo-schema';
|
||||
import { toDuration } from '../../../utils/slo/duration';
|
||||
import { CreateSLOForm } from '../types';
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ export interface Props {
|
|||
|
||||
export function SloBadges({ activeAlerts, isLoading, rules, slo, onClickRuleBadge }: Props) {
|
||||
return (
|
||||
<EuiFlexGroup direction="row" responsive={false} gutterSize="s" alignItems="center">
|
||||
<EuiFlexGroup direction="row" responsive={false} gutterSize="s" alignItems="center" wrap>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<EuiSkeletonRectangle
|
||||
|
|
|
@ -36,7 +36,7 @@ export function SloSummary({ slo, historicalSummary = [], historicalSummaryLoadi
|
|||
|
||||
return (
|
||||
<EuiFlexGroup direction="row" justifyContent="spaceBetween" gutterSize="l" responsive={false}>
|
||||
<EuiFlexItem grow={false} style={{ width: 200 }}>
|
||||
<EuiFlexItem grow={false} style={{ maxWidth: 200 }}>
|
||||
<EuiFlexGroup
|
||||
direction="row"
|
||||
responsive={false}
|
||||
|
@ -73,7 +73,7 @@ export function SloSummary({ slo, historicalSummary = [], historicalSummaryLoadi
|
|||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false} style={{ width: 220 }}>
|
||||
<EuiFlexItem grow={false} style={{ maxWidth: 200 }}>
|
||||
<EuiFlexGroup
|
||||
direction="row"
|
||||
responsive={false}
|
||||
|
|
|
@ -25,11 +25,13 @@ export class FetchHistoricalSummary {
|
|||
const sloIds = params.list.map((slo) => slo.sloId);
|
||||
const sloList = await this.repository.findAllByIds(sloIds);
|
||||
|
||||
const list: SLOWithInstanceId[] = params.list.map(({ sloId, instanceId }) => ({
|
||||
sloId,
|
||||
instanceId,
|
||||
slo: sloList.find((slo) => slo.id === sloId)!,
|
||||
}));
|
||||
const list: SLOWithInstanceId[] = params.list
|
||||
.filter(({ sloId }) => sloList.find((slo) => slo.id === sloId))
|
||||
.map(({ sloId, instanceId }) => ({
|
||||
sloId,
|
||||
instanceId,
|
||||
slo: sloList.find((slo) => slo.id === sloId)!,
|
||||
}));
|
||||
|
||||
const historicalSummary = await this.historicalSummaryClient.fetch(list);
|
||||
|
||||
|
|
|
@ -20,8 +20,6 @@ import { DateRange, SLO, Summary } from '../../domain/models';
|
|||
import { computeSLI, computeSummaryStatus, toErrorBudget } from '../../domain/services';
|
||||
import { toDateRange } from '../../domain/services/date_range';
|
||||
|
||||
// TODO: Change name of this service...
|
||||
// It does compute a summary but from the rollup data.
|
||||
export interface SummaryClient {
|
||||
computeSummary(slo: SLO, instanceId?: string): Promise<Summary>;
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ export class DefaultSummarySearchClient implements SummarySearchClient {
|
|||
page: pagination.page,
|
||||
results: finalResults.map((doc) => ({
|
||||
id: doc._source!.slo.id,
|
||||
instanceId: doc._source?.slo.instanceId ?? ALL_VALUE,
|
||||
instanceId: doc._source!.slo.instanceId ?? ALL_VALUE,
|
||||
summary: {
|
||||
errorBudget: {
|
||||
initial: toHighPrecision(doc._source!.errorBudgetInitial),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue