mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[SLO Form] Refactor to use kibana data view component (#173513)
## Summary Fixes https://github.com/elastic/kibana/issues/171687 Refactor to use kibana data view component !! <img width="1726" alt="image" src="d314a300
-f157-4857-80f1-fd71bf7e3e23"> user can also save a new data or use ad-hoc data view <img width="1728" alt="image" src="fe81b0a1
-24e7-418c-aa0d-714c4924c938"> This also avoid so many unnecessary requests being loaded, notice the difference almost avoid 15 extra requests ### After <img width="1725" alt="image" src="7f2bff43
-4298-4251-91da-b767a4c9d336"> ### Before <img width="1728" alt="image" src="a6448b57
-0bd3-4b6c-9440-d1d629e656de">
This commit is contained in:
parent
c44ae67e20
commit
d56e22101b
12 changed files with 123 additions and 174 deletions
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { UseFetchDataViewsResponse } from '../use_fetch_data_views';
|
||||
|
||||
export const useFetchDataViews = (): UseFetchDataViewsResponse => {
|
||||
|
@ -16,10 +15,9 @@ export const useFetchDataViews = (): UseFetchDataViewsResponse => {
|
|||
data: Array(20)
|
||||
.fill(0)
|
||||
.map((_, i) => ({
|
||||
id: `dataview-${i}`,
|
||||
title: `dataview-${i}`,
|
||||
type: 'foo',
|
||||
getName: () => `dataview-${i}`,
|
||||
getIndexPattern: () => `.index-pattern-dataview-${i}`,
|
||||
})) as DataView[],
|
||||
})),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,24 +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 { Index, UseFetchIndicesResponse } from '../use_fetch_indices';
|
||||
|
||||
export const useFetchIndices = (): UseFetchIndicesResponse => {
|
||||
return {
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
isSuccess: true,
|
||||
data: [
|
||||
...Array(10)
|
||||
.fill(0)
|
||||
.map((_, i) => `.index-${i}`),
|
||||
...Array(10)
|
||||
.fill(0)
|
||||
.map((_, i) => `.some-other-index-${i}`),
|
||||
] as Index[],
|
||||
};
|
||||
};
|
|
@ -22,6 +22,7 @@ export function useCreateDataView({ indexPatternString }: UseCreateDataViewProps
|
|||
useEffect(() => {
|
||||
const createDataView = () =>
|
||||
dataViews.create({
|
||||
id: `${indexPatternString}-id`,
|
||||
title: indexPatternString,
|
||||
allowNoIndex: true,
|
||||
});
|
||||
|
|
|
@ -6,29 +6,23 @@
|
|||
*/
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { DataViewListItem } from '@kbn/data-views-plugin/public';
|
||||
import { useKibana } from '../utils/kibana_react';
|
||||
|
||||
export interface UseFetchDataViewsResponse {
|
||||
isLoading: boolean;
|
||||
isSuccess: boolean;
|
||||
isError: boolean;
|
||||
data: DataView[] | undefined;
|
||||
data: DataViewListItem[] | undefined;
|
||||
}
|
||||
|
||||
interface Params {
|
||||
name?: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export function useFetchDataViews({ name = '', size = 10 }: Params): UseFetchDataViewsResponse {
|
||||
export function useFetchDataViews(): UseFetchDataViewsResponse {
|
||||
const { dataViews } = useKibana().services;
|
||||
const search = name.endsWith('*') ? name : `${name}*`;
|
||||
|
||||
const { isLoading, isError, isSuccess, data } = useQuery({
|
||||
queryKey: ['fetchDataViews', search],
|
||||
queryKey: ['fetchDataViewsList'],
|
||||
queryFn: async () => {
|
||||
return dataViews.find(search, size);
|
||||
return dataViews.getIdsWithTitle();
|
||||
},
|
||||
retry: false,
|
||||
keepPreviousData: true,
|
||||
|
|
|
@ -6,16 +6,16 @@
|
|||
*/
|
||||
|
||||
import { EuiComboBox, EuiComboBoxOptionOption, EuiFlexItem, EuiFormRow } from '@elastic/eui';
|
||||
import { FieldSpec } from '@kbn/data-views-plugin/common';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState, ReactNode } from 'react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { FieldSpec } from '@kbn/data-views-plugin/common';
|
||||
import { createOptionsFromFields, Option } from '../../helpers/create_options';
|
||||
import { CreateSLOForm } from '../../types';
|
||||
|
||||
interface Props {
|
||||
indexFields: FieldSpec[];
|
||||
name: 'groupBy' | 'indicator.params.timestampField';
|
||||
label: React.ReactNode | string;
|
||||
label: ReactNode | string;
|
||||
placeholder: string;
|
||||
isDisabled: boolean;
|
||||
isLoading: boolean;
|
||||
|
|
|
@ -32,11 +32,14 @@ export function QueryBuilder({
|
|||
required,
|
||||
tooltip,
|
||||
}: Props) {
|
||||
const { data, dataViews, docLinks, http, notifications, storage, uiSettings, unifiedSearch } =
|
||||
const { data, docLinks, dataViews, http, notifications, storage, uiSettings, unifiedSearch } =
|
||||
useKibana().services;
|
||||
|
||||
const { control, getFieldState } = useFormContext<CreateSLOForm>();
|
||||
const { dataView } = useCreateDataView({ indexPatternString });
|
||||
|
||||
const { dataView } = useCreateDataView({
|
||||
indexPatternString,
|
||||
});
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
|
@ -77,7 +80,7 @@ export function QueryBuilder({
|
|||
disableAutoFocus
|
||||
disableLanguageSwitcher
|
||||
indexPatterns={dataView ? [dataView] : []}
|
||||
isDisabled={!indexPatternString}
|
||||
isDisabled={!dataView}
|
||||
isInvalid={fieldState.invalid}
|
||||
languageSwitcherPopoverAnchorPosition="rightDown"
|
||||
placeholder={placeholder}
|
||||
|
|
|
@ -5,93 +5,110 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui';
|
||||
import { EuiFormRow } from '@elastic/eui';
|
||||
import { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { debounce } from 'lodash';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { DataViewPicker } from '@kbn/unified-search-plugin/public';
|
||||
import { useKibana } from '../../../../utils/kibana_react';
|
||||
import { ObservabilityPublicPluginsStart } from '../../../..';
|
||||
import { useFetchDataViews } from '../../../../hooks/use_fetch_data_views';
|
||||
import { useFetchIndices } from '../../../../hooks/use_fetch_indices';
|
||||
import { CreateSLOForm } from '../../types';
|
||||
|
||||
interface Option {
|
||||
label: string;
|
||||
options: Array<{ value: string; label: string }>;
|
||||
}
|
||||
|
||||
export function IndexSelection() {
|
||||
const { control, getFieldState } = useFormContext<CreateSLOForm>();
|
||||
const [searchValue, setSearchValue] = useState<string>('');
|
||||
const { control, getFieldState, setValue, watch } = useFormContext<CreateSLOForm>();
|
||||
const { dataViews: dataViewsService } = useKibana().services;
|
||||
|
||||
const { isLoading: isIndicesLoading, data: indices = [] } = useFetchIndices({
|
||||
search: searchValue,
|
||||
});
|
||||
const { isLoading: isDataViewsLoading, data: dataViews = [] } = useFetchDataViews({
|
||||
name: searchValue,
|
||||
});
|
||||
const { isLoading: isDataViewsLoading, data: dataViews = [] } = useFetchDataViews();
|
||||
|
||||
const options: Option[] = [];
|
||||
if (!isDataViewsLoading && dataViews.length > 0) {
|
||||
options.push(createDataViewsOption(dataViews));
|
||||
}
|
||||
if (!isIndicesLoading && !!searchValue) {
|
||||
options.push(createIndexPatternOption(searchValue, indices));
|
||||
}
|
||||
const { dataViewEditor } = useKibana<ObservabilityPublicPluginsStart>().services;
|
||||
|
||||
const onSearchChange = debounce((value: string) => setSearchValue(value), 300);
|
||||
const [adHocDataViews, setAdHocDataViews] = useState<DataView[]>([]);
|
||||
|
||||
const placeholder = i18n.translate('xpack.observability.slo.sloEdit.indexSelection.placeholder', {
|
||||
defaultMessage: 'Select an index pattern',
|
||||
});
|
||||
const currentIndexPattern = watch('indicator.params.index');
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDataViewsLoading) {
|
||||
const missingAdHocDataView =
|
||||
dataViews.find((dataView) => dataView.title === currentIndexPattern) ||
|
||||
adHocDataViews.find((dataView) => dataView.getIndexPattern() === currentIndexPattern);
|
||||
|
||||
if (!missingAdHocDataView && currentIndexPattern) {
|
||||
async function loadMissingDataView() {
|
||||
const dataView = await dataViewsService.create(
|
||||
{
|
||||
title: currentIndexPattern,
|
||||
allowNoIndex: true,
|
||||
},
|
||||
true
|
||||
);
|
||||
if (dataView.getIndexPattern() === currentIndexPattern) {
|
||||
setAdHocDataViews((prev) => [...prev, dataView]);
|
||||
}
|
||||
}
|
||||
|
||||
loadMissingDataView();
|
||||
}
|
||||
}
|
||||
}, [adHocDataViews, currentIndexPattern, dataViews, dataViewsService, isDataViewsLoading]);
|
||||
|
||||
const getDataViewPatternById = (id?: string) => {
|
||||
return (
|
||||
dataViews.find((dataView) => dataView.id === id)?.title ||
|
||||
adHocDataViews.find((dataView) => dataView.id === id)?.getIndexPattern()
|
||||
);
|
||||
};
|
||||
|
||||
const getDataViewIdByIndexPattern = (indexPattern: string) => {
|
||||
return (
|
||||
dataViews.find((dataView) => dataView.title === indexPattern) ||
|
||||
adHocDataViews.find((dataView) => dataView.getIndexPattern() === indexPattern)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.observability.slo.sloEdit.customKql.indexSelection.label', {
|
||||
defaultMessage: 'Index',
|
||||
})}
|
||||
helpText={i18n.translate(
|
||||
'xpack.observability.slo.sloEdit.customKql.indexSelection.helpText',
|
||||
{ defaultMessage: 'Use * to broaden your query.' }
|
||||
)}
|
||||
isInvalid={getFieldState('indicator.params.index').invalid}
|
||||
>
|
||||
<EuiFormRow label={INDEX_LABEL} isInvalid={getFieldState('indicator.params.index').invalid}>
|
||||
<Controller
|
||||
defaultValue=""
|
||||
name="indicator.params.index"
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field, fieldState }) => (
|
||||
<EuiComboBox
|
||||
{...field}
|
||||
aria-label={placeholder}
|
||||
async
|
||||
data-test-subj="indexSelection"
|
||||
isClearable
|
||||
isInvalid={fieldState.invalid}
|
||||
isLoading={isIndicesLoading && isDataViewsLoading}
|
||||
placeholder={placeholder}
|
||||
onChange={(selected: EuiComboBoxOptionOption[]) => {
|
||||
if (selected.length) {
|
||||
return field.onChange(selected[0].value);
|
||||
}
|
||||
|
||||
field.onChange('');
|
||||
<DataViewPicker
|
||||
adHocDataViews={adHocDataViews}
|
||||
trigger={{
|
||||
label: field.value || SELECT_DATA_VIEW,
|
||||
fullWidth: true,
|
||||
color: 'text',
|
||||
isLoading: isDataViewsLoading,
|
||||
'data-test-subj': 'indexSelection',
|
||||
}}
|
||||
onChangeDataView={(newId: string) => {
|
||||
field.onChange(getDataViewPatternById(newId));
|
||||
dataViewsService.get(newId).then((dataView) => {
|
||||
if (dataView.timeFieldName) {
|
||||
setValue('indicator.params.timestampField', dataView.timeFieldName);
|
||||
}
|
||||
});
|
||||
}}
|
||||
currentDataViewId={getDataViewIdByIndexPattern(field.value)?.id}
|
||||
onDataViewCreated={() => {
|
||||
dataViewEditor.openEditor({
|
||||
allowAdHocDataView: true,
|
||||
onSave: (dataView: DataView) => {
|
||||
if (!dataView.isPersisted()) {
|
||||
setAdHocDataViews([...adHocDataViews, dataView]);
|
||||
field.onChange(dataView.getIndexPattern());
|
||||
} else {
|
||||
field.onChange(getDataViewPatternById(dataView.id));
|
||||
}
|
||||
if (dataView.timeFieldName) {
|
||||
setValue('indicator.params.timestampField', dataView.timeFieldName);
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
options={options}
|
||||
onSearchChange={onSearchChange}
|
||||
selectedOptions={
|
||||
!!field.value
|
||||
? [
|
||||
{
|
||||
value: field.value,
|
||||
label: field.value,
|
||||
'data-test-subj': 'indexSelectionSelectedValue',
|
||||
},
|
||||
]
|
||||
: []
|
||||
}
|
||||
singleSelection
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@ -99,50 +116,16 @@ export function IndexSelection() {
|
|||
);
|
||||
}
|
||||
|
||||
function createDataViewLabel(dataView: DataView) {
|
||||
return `${dataView.getName()} (${dataView.getIndexPattern()})`;
|
||||
}
|
||||
const SELECT_DATA_VIEW = i18n.translate(
|
||||
'xpack.observability.slo.sloEdit.customKql.dataViewSelection.label',
|
||||
{
|
||||
defaultMessage: 'Select a Data view',
|
||||
}
|
||||
);
|
||||
|
||||
function createDataViewsOption(dataViews: DataView[]): Option {
|
||||
return {
|
||||
label: i18n.translate('xpack.observability.slo.sloEdit.indexSelection.dataViewOptionsLabel', {
|
||||
defaultMessage: 'Select an index pattern from an existing Data View',
|
||||
}),
|
||||
options: dataViews
|
||||
.map((view) => ({
|
||||
label: createDataViewLabel(view),
|
||||
value: view.getIndexPattern(),
|
||||
}))
|
||||
.sort((a, b) => String(a.label).localeCompare(b.label)),
|
||||
};
|
||||
}
|
||||
|
||||
function createIndexPatternOption(searchValue: string, indices: string[]): Option {
|
||||
const indexPattern = searchValue.endsWith('*') ? searchValue : `${searchValue}*`;
|
||||
const hasMatchingIndices = indices.length > 0;
|
||||
|
||||
return {
|
||||
label: i18n.translate(
|
||||
'xpack.observability.slo.sloEdit.customKql.indexSelection.indexPatternLabel',
|
||||
{ defaultMessage: 'Use the index pattern' }
|
||||
),
|
||||
options: [
|
||||
{
|
||||
value: indexPattern,
|
||||
label: hasMatchingIndices
|
||||
? i18n.translate(
|
||||
'xpack.observability.slo.sloEdit.customKql.indexSelection.indexPatternFoundLabel',
|
||||
{
|
||||
defaultMessage:
|
||||
'{searchPattern} (match {num, plural, one {# index} other {# indices}})',
|
||||
values: { searchPattern: indexPattern, num: indices.length },
|
||||
}
|
||||
)
|
||||
: i18n.translate(
|
||||
'xpack.observability.slo.sloEdit.indexSelection.indexPatternNoMatchLabel',
|
||||
{ defaultMessage: '{searchPattern}', values: { searchPattern: indexPattern } }
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
const INDEX_LABEL = i18n.translate(
|
||||
'xpack.observability.slo.sloEdit.customKql.indexSelection.label',
|
||||
{
|
||||
defaultMessage: 'Index',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -10,8 +10,8 @@ import { i18n } from '@kbn/i18n';
|
|||
import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common';
|
||||
import React from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useCreateDataView } from '../../../../hooks/use_create_data_view';
|
||||
import { useFetchGroupByCardinality } from '../../../../hooks/slo/use_fetch_group_by_cardinality';
|
||||
import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
|
||||
import { CreateSLOForm } from '../../types';
|
||||
import { DataPreviewChart } from '../common/data_preview_chart';
|
||||
import { IndexFieldSelector } from '../common/index_field_selector';
|
||||
|
@ -24,10 +24,11 @@ export function CustomKqlIndicatorTypeForm() {
|
|||
const timestampField = watch('indicator.params.timestampField');
|
||||
const groupByField = watch('groupBy');
|
||||
|
||||
const { isLoading: isIndexFieldsLoading, data: indexFields = [] } =
|
||||
useFetchIndexPatternFields(index);
|
||||
const timestampFields = indexFields.filter((field) => field.type === 'date');
|
||||
const groupByFields = indexFields.filter((field) => field.aggregatable);
|
||||
const { dataView, loading: isIndexFieldsLoading } = useCreateDataView({
|
||||
indexPatternString: index,
|
||||
});
|
||||
const timestampFields = dataView?.fields?.filter((field) => field.type === 'date') ?? [];
|
||||
const groupByFields = dataView?.fields?.filter((field) => field.aggregatable) ?? [];
|
||||
|
||||
const { isLoading: isGroupByCardinalityLoading, data: groupByCardinality } =
|
||||
useFetchGroupByCardinality(index, timestampField, groupByField);
|
||||
|
|
|
@ -82,10 +82,13 @@ const mockKibana = () => {
|
|||
dataViews: {
|
||||
find: jest.fn().mockReturnValue([]),
|
||||
get: jest.fn().mockReturnValue([]),
|
||||
getDefault: jest.fn(),
|
||||
},
|
||||
},
|
||||
dataViews: {
|
||||
create: jest.fn().mockResolvedValue(42),
|
||||
create: jest.fn().mockResolvedValue({
|
||||
getIndexPattern: jest.fn().mockReturnValue('some-index'),
|
||||
}),
|
||||
},
|
||||
docLinks: {
|
||||
links: {
|
||||
|
@ -110,7 +113,6 @@ const mockKibana = () => {
|
|||
triggersActionsUi: {
|
||||
getAddRuleFlyout: jest
|
||||
.fn()
|
||||
|
||||
.mockReturnValue(<div data-test-subj="add-rule-flyout">Add Rule Flyout</div>),
|
||||
},
|
||||
uiSettings: {
|
||||
|
|
|
@ -28787,7 +28787,6 @@
|
|||
"xpack.observability.slo.sloDetails.overview.rollingTimeWindow": "{duration} en cours",
|
||||
"xpack.observability.slo.sloDetails.overview.timeslicesBudgetingMethodDetails": "{duration} sections, {target} cible",
|
||||
"xpack.observability.slo.sloDetails.sliHistoryChartPanel.duration": "{duration}",
|
||||
"xpack.observability.slo.sloEdit.customKql.indexSelection.indexPatternFoundLabel": "{searchPattern} (correspond à {num, plural, one {# index} many {# index} other {# index}})",
|
||||
"xpack.observability.slo.sloEdit.rollingTimeWindow.days": "{number} jours",
|
||||
"xpack.observability.slo.update.errorNotification": "Un problème est survenu lors de la mise à jour de {name}",
|
||||
"xpack.observability.slo.update.successNotification": "Mise à jour réussie de {name}",
|
||||
|
@ -29250,8 +29249,6 @@
|
|||
"xpack.observability.slo.sloEdit.createAlert.ruleName": "Règle d'alerte de taux d'avancement du SLO",
|
||||
"xpack.observability.slo.sloEdit.createAlert.title": "Créer",
|
||||
"xpack.observability.slo.sloEdit.createSloButton": "Créer un SLO",
|
||||
"xpack.observability.slo.sloEdit.customKql.indexSelection.helpText": "Utilisez le caractère * pour élargir votre recherche.",
|
||||
"xpack.observability.slo.sloEdit.customKql.indexSelection.indexPatternLabel": "Utiliser le modèle d'indexation",
|
||||
"xpack.observability.slo.sloEdit.customKql.indexSelection.label": "Index",
|
||||
"xpack.observability.slo.sloEdit.dataPreviewChart.errorMessage": "Les paramètres d'indicateur actuels ne sont pas valides",
|
||||
"xpack.observability.slo.sloEdit.dataPreviewChart.explanationMessage": "Remplir les champs d'indicateur pour visualiser les indicateurs actuels",
|
||||
|
|
|
@ -28787,7 +28787,6 @@
|
|||
"xpack.observability.slo.sloDetails.overview.rollingTimeWindow": "{duration}ローリング",
|
||||
"xpack.observability.slo.sloDetails.overview.timeslicesBudgetingMethodDetails": "{duration}スライス、{target}ターゲット",
|
||||
"xpack.observability.slo.sloDetails.sliHistoryChartPanel.duration": "過去{duration}",
|
||||
"xpack.observability.slo.sloEdit.customKql.indexSelection.indexPatternFoundLabel": "{searchPattern}({num, plural, other {#個のインデックス}}と一致)",
|
||||
"xpack.observability.slo.sloEdit.rollingTimeWindow.days": "{number}日",
|
||||
"xpack.observability.slo.update.errorNotification": "{name}の更新中にエラーが発生しました",
|
||||
"xpack.observability.slo.update.successNotification": "正常に{name}を更新しました",
|
||||
|
@ -29250,8 +29249,6 @@
|
|||
"xpack.observability.slo.sloEdit.createAlert.ruleName": "SLOバーンレートアラートルール",
|
||||
"xpack.observability.slo.sloEdit.createAlert.title": "作成",
|
||||
"xpack.observability.slo.sloEdit.createSloButton": "SLOの作成",
|
||||
"xpack.observability.slo.sloEdit.customKql.indexSelection.helpText": "* で検索クエリの範囲を広げます。",
|
||||
"xpack.observability.slo.sloEdit.customKql.indexSelection.indexPatternLabel": "インデックスパターンを使用",
|
||||
"xpack.observability.slo.sloEdit.customKql.indexSelection.label": "インデックス",
|
||||
"xpack.observability.slo.sloEdit.dataPreviewChart.errorMessage": "現在のインジケーター設定は無効です",
|
||||
"xpack.observability.slo.sloEdit.dataPreviewChart.explanationMessage": "インジケーターフィールドに入力すると、現在のメトリックが可視化されます。",
|
||||
|
|
|
@ -28771,7 +28771,6 @@
|
|||
"xpack.observability.slo.sloDetails.overview.rollingTimeWindow": "{duration} 滚动",
|
||||
"xpack.observability.slo.sloDetails.overview.timeslicesBudgetingMethodDetails": "{duration} 切片,{target} 目标",
|
||||
"xpack.observability.slo.sloDetails.sliHistoryChartPanel.duration": "过去 {duration}",
|
||||
"xpack.observability.slo.sloEdit.customKql.indexSelection.indexPatternFoundLabel": "{searchPattern}(匹配 {num, plural, other {# 个索引}})",
|
||||
"xpack.observability.slo.sloEdit.rollingTimeWindow.days": "{number} 天",
|
||||
"xpack.observability.slo.update.errorNotification": "更新 {name} 时出现问题",
|
||||
"xpack.observability.slo.update.successNotification": "成功更新 {name}",
|
||||
|
@ -29234,8 +29233,6 @@
|
|||
"xpack.observability.slo.sloEdit.createAlert.ruleName": "SLO 消耗速度告警规则",
|
||||
"xpack.observability.slo.sloEdit.createAlert.title": "创建",
|
||||
"xpack.observability.slo.sloEdit.createSloButton": "创建 SLO",
|
||||
"xpack.observability.slo.sloEdit.customKql.indexSelection.helpText": "使用 * 可扩大您的查询范围。",
|
||||
"xpack.observability.slo.sloEdit.customKql.indexSelection.indexPatternLabel": "使用索引模式",
|
||||
"xpack.observability.slo.sloEdit.customKql.indexSelection.label": "索引",
|
||||
"xpack.observability.slo.sloEdit.dataPreviewChart.errorMessage": "当前指标设置无效",
|
||||
"xpack.observability.slo.sloEdit.dataPreviewChart.explanationMessage": "填写指标字段以查看当前指标的可视化",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue