Add dateHistogramInterval utility (#39091) (#39563)

* Add dateHistogramInterval utility

* Fix editor bug

* Fix tests

* FIx imports to temporary solution

* Remove wrongly merged translations

* Remove old static.ts file
This commit is contained in:
Tim Roes 2019-06-25 12:28:11 +02:00 committed by GitHub
parent 4d79f43e05
commit b2f028a8b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 299 additions and 45 deletions

View file

@ -53,7 +53,7 @@ export default {
'!src/legacy/core_plugins/**/__snapshots__/**/*',
],
moduleNameMapper: {
'^plugins/([^\/.]*)/(.*)': '<rootDir>/src/legacy/core_plugins/$1/public/$2',
'^plugins/([^\/.]*)(.*)': '<rootDir>/src/legacy/core_plugins/$1/public$2',
'^ui/(.*)': '<rootDir>/src/legacy/ui/public/$1',
'^uiExports/(.*)': '<rootDir>/src/dev/jest/mocks/file_mock.js',
'^test_utils/(.*)': '<rootDir>/src/test_utils/public/$1',

View file

@ -0,0 +1,50 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { dateHistogramInterval } from './date_histogram_interval';
describe('dateHistogramInterval', () => {
it('should return calender_interval key for calendar intervals', () => {
expect(dateHistogramInterval('1m')).toEqual({ calendar_interval: '1m' });
expect(dateHistogramInterval('1h')).toEqual({ calendar_interval: '1h' });
expect(dateHistogramInterval('1d')).toEqual({ calendar_interval: '1d' });
expect(dateHistogramInterval('1w')).toEqual({ calendar_interval: '1w' });
expect(dateHistogramInterval('1M')).toEqual({ calendar_interval: '1M' });
expect(dateHistogramInterval('1y')).toEqual({ calendar_interval: '1y' });
});
it('should return fixed_interval key for fixed intervals', () => {
expect(dateHistogramInterval('1ms')).toEqual({ fixed_interval: '1ms' });
expect(dateHistogramInterval('42ms')).toEqual({ fixed_interval: '42ms' });
expect(dateHistogramInterval('1s')).toEqual({ fixed_interval: '1s' });
expect(dateHistogramInterval('42s')).toEqual({ fixed_interval: '42s' });
expect(dateHistogramInterval('42m')).toEqual({ fixed_interval: '42m' });
expect(dateHistogramInterval('42h')).toEqual({ fixed_interval: '42h' });
expect(dateHistogramInterval('42d')).toEqual({ fixed_interval: '42d' });
});
it('should throw an error on invalid intervals', () => {
expect(() => dateHistogramInterval('2w')).toThrow();
expect(() => dateHistogramInterval('2M')).toThrow();
expect(() => dateHistogramInterval('2y')).toThrow();
expect(() => dateHistogramInterval('2')).toThrow();
expect(() => dateHistogramInterval('y')).toThrow();
expect(() => dateHistogramInterval('0.5h')).toThrow();
});
});

View file

@ -0,0 +1,49 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { parseEsInterval } from './parse_es_interval';
type Interval = { fixed_interval: string } | { calendar_interval: string };
/**
* Checks whether a given Elasticsearch interval is a calendar or fixed interval
* and returns an object containing the appropriate date_histogram property for that
* interval. So it will return either an object containing the fixed_interval key for
* that interval or a calendar_interval. If the specified interval was not a valid Elasticsearch
* interval this method will throw an error.
*
* You can simply spread the returned value of this method into your date_histogram.
* @example
* const aggregation = {
* date_histogram: {
* field: 'date',
* ...dateHistogramInterval('24h'),
* }
* };
*
* @param interval The interval string to return the appropriate date_histogram key for.
*/
export function dateHistogramInterval(interval: string): Interval {
const { type } = parseEsInterval(interval);
if (type === 'calendar') {
return { calendar_interval: interval };
} else {
return { fixed_interval: interval };
}
}

View file

@ -0,0 +1,29 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/** @public static code */
export { dateHistogramInterval } from './date_histogram_interval';
/** @public static code */
export {
isValidEsInterval,
InvalidEsCalendarIntervalError,
InvalidEsIntervalFormatError,
parseEsInterval,
ParsedInterval,
} from './parse_es_interval';

View file

@ -20,3 +20,4 @@
export { parseEsInterval, ParsedInterval } from './parse_es_interval';
export { InvalidEsCalendarIntervalError } from './invalid_es_calendar_interval_error';
export { InvalidEsIntervalFormatError } from './invalid_es_interval_format_error';
export { isValidEsInterval } from './is_valid_es_interval';

View file

@ -28,7 +28,7 @@ export class InvalidEsCalendarIntervalError extends Error {
public readonly type: string
) {
super(
i18n.translate('common.ui.parseEsInterval.invalidEsCalendarIntervalErrorMessage', {
i18n.translate('data.parseEsInterval.invalidEsCalendarIntervalErrorMessage', {
defaultMessage: 'Invalid calendar interval: {interval}, value must be 1',
values: { interval },
})

View file

@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n';
export class InvalidEsIntervalFormatError extends Error {
constructor(public readonly interval: string) {
super(
i18n.translate('common.ui.parseEsInterval.invalidEsIntervalFormatErrorMessage', {
i18n.translate('data.parseEsInterval.invalidEsIntervalFormatErrorMessage', {
defaultMessage: 'Invalid interval format: {interval}',
values: { interval },
})

View file

@ -0,0 +1,38 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { parseEsInterval } from './parse_es_interval';
/**
* Checks whether a given interval string (e.g. 1w, 24h, ...) is a valid Elasticsearch interval.
* Will return false if the interval is not valid in Elasticsearch, otherwise true.
* Invalid intervals might be: 2f, since there is no unit 'f'; 2w, since weeks and month intervals
* are only allowed with a value of 1, etc.
*
* @param interval The interval string like 1w, 24h
* @returns True if the interval is valid for Elasticsearch
*/
export function isValidEsInterval(interval: string): boolean {
try {
parseEsInterval(interval);
return true;
} catch {
return false;
}
}

View file

@ -87,3 +87,14 @@ export { ExpressionRenderer, ExpressionRendererProps, ExpressionRunner } from '.
/** @public types */
export { IndexPattern, StaticIndexPattern, StaticIndexPatternField, Field } from './index_patterns';
export { Query } from './query';
/** @public static code */
export { dateHistogramInterval } from '../common/date_histogram_interval';
/** @public static code */
export {
isValidEsInterval,
InvalidEsCalendarIntervalError,
InvalidEsIntervalFormatError,
parseEsInterval,
ParsedInterval,
} from '../common/parse_es_interval';

View file

@ -0,0 +1,30 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/** @public static code */
export { dateHistogramInterval } from '../common/date_histogram_interval';
/** @public static code */
export {
isValidEsInterval,
InvalidEsCalendarIntervalError,
InvalidEsIntervalFormatError,
parseEsInterval,
ParsedInterval,
} from '../common/parse_es_interval';

View file

@ -48,7 +48,7 @@ describe('get columns', function () {
const vis = new Vis(indexPattern, {
type: 'pie',
aggs: [
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } }
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } }
]
});
@ -63,10 +63,10 @@ describe('get columns', function () {
const vis = new Vis(indexPattern, {
type: 'pie',
aggs: [
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } }
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } }
]
});
@ -83,12 +83,12 @@ describe('get columns', function () {
const vis = new Vis(indexPattern, {
type: 'pie',
aggs: [
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } },
{ type: 'avg', schema: 'metric', params: { field: 'bytes' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } },
{ type: 'sum', schema: 'metric', params: { field: 'bytes' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } }
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } }
]
});
@ -119,12 +119,12 @@ describe('get columns', function () {
const vis = new Vis(indexPattern, {
type: 'histogram',
aggs: [
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } },
{ type: 'avg', schema: 'metric', params: { field: 'bytes' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } },
{ type: 'sum', schema: 'metric', params: { field: 'bytes' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } }
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } }
]
});

View file

@ -35,6 +35,7 @@ describe('date_histogram params', function () {
let paramWriter;
let writeInterval;
let write;
let getTimeBounds;
let timeField;
@ -50,6 +51,9 @@ describe('date_histogram params', function () {
writeInterval = function (interval, timeRange) {
return paramWriter.write({ interval: interval, field: timeField, timeRange: timeRange });
};
write = (params) => {
return paramWriter.write({ interval: '10s', ...params });
};
const now = moment();
getTimeBounds = function (n, units) {
@ -63,9 +67,14 @@ describe('date_histogram params', function () {
}));
describe('interval', function () {
it('accepts a valid interval', function () {
it('accepts a valid calendar interval', function () {
const output = writeInterval('d');
expect(output.params).to.have.property('interval', '1d');
expect(output.params).to.have.property('calendar_interval', '1d');
});
it('accepts a valid fixed interval', () => {
const output = writeInterval('100s');
expect(output.params).to.have.property('fixed_interval', '100s');
});
it('throws error when interval is invalid', function () {
@ -75,13 +84,13 @@ describe('date_histogram params', function () {
it('automatically picks an interval', function () {
const timeBounds = getTimeBounds(15, 'm');
const output = writeInterval('auto', timeBounds);
expect(output.params.interval).to.be('30s');
expect(output.params).to.have.property('fixed_interval', '30s');
});
it('scales up the interval if it will make too many buckets', function () {
const timeBounds = getTimeBounds(30, 'm');
const output = writeInterval('s', timeBounds);
expect(output.params.interval).to.be('10s');
expect(output.params).to.have.property('fixed_interval', '10s');
expect(output.metricScaleText).to.be('second');
expect(output.metricScale).to.be(0.1);
});
@ -89,7 +98,7 @@ describe('date_histogram params', function () {
it('does not scale down the interval', function () {
const timeBounds = getTimeBounds(1, 'm');
const output = writeInterval('h', timeBounds);
expect(output.params.interval).to.be('1h');
expect(output.params).to.have.property('calendar_interval', '1h');
expect(output.metricScaleText).to.be(undefined);
expect(output.metricScale).to.be(undefined);
});
@ -140,20 +149,20 @@ describe('date_histogram params', function () {
});
it('should use the specified time_zone', () => {
const output = paramWriter.write({ time_zone: 'Europe/Kiev' });
const output = write({ time_zone: 'Europe/Kiev' });
expect(output.params).to.have.property('time_zone', 'Europe/Kiev');
});
it('should use the Kibana time_zone if no parameter specified', () => {
config.isDefault.withArgs('dateFormat:tz').returns(false);
config.get.withArgs('dateFormat:tz').returns('Europe/Riga');
const output = paramWriter.write({});
const output = write({});
expect(output.params).to.have.property('time_zone', 'Europe/Riga');
});
it('should use the fixed time_zone from the index pattern typeMeta', () => {
_.set(paramWriter.indexPattern, ['typeMeta', 'aggs', 'date_histogram', timeField, 'time_zone'], 'Europe/Rome');
const output = paramWriter.write({ field: timeField });
const output = write({ field: timeField });
expect(output.params).to.have.property('time_zone', 'Europe/Rome');
});
@ -167,7 +176,7 @@ describe('date_histogram params', function () {
it('should write a long value if a moment passed in', function () {
const then = moment(0);
const now = moment(500);
const output = paramWriter.write({
const output = write({
extended_bounds: {
min: then,
max: now
@ -185,7 +194,7 @@ describe('date_histogram params', function () {
it('should write a long if a long is passed', function () {
const then = 0;
const now = 500;
const output = paramWriter.write({
const output = write({
extended_bounds: {
min: then,
max: now

View file

@ -60,7 +60,7 @@ describe('sibling pipeline aggs', function () {
id: '6',
type: 'date_histogram',
schema: 'bucket',
params: { field: '@timestamp' }
params: { field: '@timestamp', interval: '10s' }
}
};
@ -115,7 +115,7 @@ describe('sibling pipeline aggs', function () {
id: '6',
type: 'date_histogram',
schema: 'bucket',
params: { field: '@timestamp' },
params: { field: '@timestamp', interval: '10s', },
}
});
expect(aggDsl[metric.name].buckets_path).to.be('2-bucket>2-metric');
@ -135,7 +135,7 @@ describe('sibling pipeline aggs', function () {
id: '6',
type: 'date_histogram',
schema: 'bucket',
params: { field: '@timestamp' },
params: { field: '@timestamp', interval: '10s' },
}
});
expect(metricAgg.getFormat(aggConfig).type.id).to.be('bytes');

View file

@ -27,6 +27,7 @@ import { intervalOptions } from './_interval_options';
import { TimeIntervalParamEditor } from '../controls/time_interval';
import { timefilter } from '../../timefilter';
import { DropPartialsParamEditor } from '../controls/drop_partials';
import { dateHistogramInterval } from '../../../../core_plugins/data/common';
import { i18n } from '@kbn/i18n';
const config = chrome.getUiSettingsClient();
@ -140,7 +141,19 @@ export const dateHistogramBucketAgg = new BucketAggType({
const { useNormalizedEsInterval } = agg.params;
const interval = agg.buckets.getInterval(useNormalizedEsInterval);
output.bucketInterval = interval;
output.params.interval = interval.expression;
if (interval.expression === '0ms') {
// We are hitting this code a couple of times while configuring in editor
// with an interval of 0ms because the overall time range has not yet been
// set. Since 0ms is not a valid ES interval, we cannot pass it through dateHistogramInterval
// below, since it would throw an exception. So in the cases we still have an interval of 0ms
// here we simply skip the rest of the method and never write an interval into the DSL, since
// this DSL will anyway not be used before we're passing this code with an actual interval.
return;
}
output.params = {
...output.params,
...dateHistogramInterval(interval.expression),
};
const scaleMetrics = interval.scaled && interval.scale < 1;
if (scaleMetrics && aggs) {

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { parseInterval } from '../utils/parse_interval';
import { isValidEsInterval } from '../../../core_plugins/data/common';
import { leastCommonInterval } from '../vis/lib/least_common_interval';
/**
@ -53,7 +53,7 @@ function isValidInterval(value: string, baseInterval: string) {
if (baseInterval) {
return _parseWithBase(value, baseInterval);
} else {
return parseInterval(value) !== null;
return isValidEsInterval(value);
}
}

View file

@ -24,10 +24,8 @@ import {
isAngularHttpError,
} from './lib/format_angular_http_error';
const newPlatformFatalErrors = npSetup.core.fatalErrors;
export function addFatalErrorCallback(callback: () => void) {
newPlatformFatalErrors.get$().subscribe(() => {
npSetup.core.fatalErrors.get$().subscribe(() => {
callback();
});
}
@ -38,5 +36,5 @@ export function fatalError(error: AngularHttpError | Error | string, location?:
error = formatAngularHttpError(error);
}
newPlatformFatalErrors.add(error, location);
npSetup.core.fatalErrors.add(error, location);
}

View file

@ -0,0 +1,27 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// Those exports are kept here for now, so we can move over imports
// step by step into the data plugin.
export {
parseEsInterval,
ParsedInterval,
InvalidEsCalendarIntervalError,
InvalidEsIntervalFormatError,
} from '../../../core_plugins/data/common';

View file

@ -276,7 +276,7 @@ describe('AggConfigs', function () {
const vis = new Vis(indexPattern, {
type: 'histogram',
aggs: [
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } },
{ type: 'count', schema: 'metric' }
]
});
@ -295,7 +295,7 @@ describe('AggConfigs', function () {
const vis = new Vis(indexPattern, {
type: 'histogram',
aggs: [
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } },
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp', interval: '10s' } },
{ type: 'avg', schema: 'metric', params: { field: 'bytes' } },
{ type: 'sum', schema: 'metric', params: { field: 'bytes' } },
{ type: 'min', schema: 'metric', params: { field: 'bytes' } },
@ -368,7 +368,8 @@ describe('AggConfigs', function () {
type: 'date_histogram',
schema: 'bucketAgg',
params: {
field: '@timestamp'
field: '@timestamp',
interval: '10s',
}
},
customMetric: {

View file

@ -22,7 +22,9 @@ export function createJestConfig({
],
moduleNameMapper: {
'^ui/(.*)': `${kibanaDirectory}/src/legacy/ui/public/$1`,
'uiExports/(.*)': `${kibanaDirectory}/src/dev/jest/mocks/file_mocks.js`,
'^src/core/(.*)': `${kibanaDirectory}/src/core/$1`,
'^plugins/([^\/.]*)(.*)': `${kibanaDirectory}/src/legacy/core_plugins/$1/public$2`,
'^plugins/xpack_main/(.*);': `${xPackKibanaDirectory}/legacy/plugins/xpack_main/public/$1`,
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
`${kibanaDirectory}/src/dev/jest/mocks/file_mock.js`,

View file

@ -509,8 +509,6 @@
"common.ui.paginateSelectableList.sortByButtonLabeDescendingScreenReaderOnly": "降順",
"common.ui.paginateSelectableList.sortByButtonLabel": "名前",
"common.ui.paginateSelectableList.sortByButtonLabelScreenReaderOnly": "並べ替え基準",
"common.ui.parseEsInterval.invalidEsCalendarIntervalErrorMessage": "無効なカレンダー間隔: {interval}、1 よりも大きな値が必要です",
"common.ui.parseEsInterval.invalidEsIntervalFormatErrorMessage": "無効な間隔のフォーマット: {interval}",
"common.ui.recentLinks.linkItem.screenReaderLabel": "{recentlyAccessedItemLinklabel}、タイプ: {pageType}",
"common.ui.savedObjectFinder.addNewItemButtonLabel": "新規 {item} を追加",
"common.ui.savedObjectFinder.manageItemsButtonLabel": "{items} の管理",
@ -9921,4 +9919,4 @@
"xpack.watcher.watchActionsTitle": "条件が満たされた際に {watchActionsCount, plural, one{# アクション} other {# アクション}} を実行します",
"xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。"
}
}
}

View file

@ -508,8 +508,6 @@
"common.ui.paginateSelectableList.sortByButtonLabeDescendingScreenReaderOnly": "降序",
"common.ui.paginateSelectableList.sortByButtonLabel": "名称",
"common.ui.paginateSelectableList.sortByButtonLabelScreenReaderOnly": "排序依据",
"common.ui.parseEsInterval.invalidEsCalendarIntervalErrorMessage": "无效的日历时间间隔:{interval},值必须为 1",
"common.ui.parseEsInterval.invalidEsIntervalFormatErrorMessage": "无效的时间间隔格式:{interval}",
"common.ui.recentLinks.linkItem.screenReaderLabel": "{recentlyAccessedItemLinklabel},类型:{pageType}",
"common.ui.savedObjectFinder.addNewItemButtonLabel": "添加新的 {item}",
"common.ui.savedObjectFinder.manageItemsButtonLabel": "管理 {items}",
@ -9923,4 +9921,4 @@
"xpack.watcher.watchActionsTitle": "满足后将执行 {watchActionsCount, plural, one{# 个操作} other {# 个操作}}",
"xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。"
}
}
}